Amazon Elastic Container Service (ECS) on AWS Fargate runs RegScale as a managed container — without operating a Kubernetes cluster. An Application Load Balancer (ALB) handles ingress and TLS termination, RegScale connects to an external Amazon RDS for SQL Server database, and uploaded files are stored on an Amazon EFS file system mounted into the task.

📘

Before you begin

Complete the Prerequisites (database, networking) and review the Sizing Guide. After deployment, follow the Post-Deployment Steps.

Architecture

  • RegScale Core Platform — the regscale/regscale container image running as an ECS Fargate task, listening on HTTP port 80.
  • Ingress & TLS — an Application Load Balancer terminates TLS (using an AWS Certificate Manager certificate) and forwards traffic to the task.
  • SQL Server — an external Amazon RDS for SQL Server instance (SQL Server 2022+; see Prerequisites › Database Configuration). RegScale connects via a connection string.
  • File Storage — an Amazon EFS file system mounted into the task at /app/atlas-files for persistent, uploaded evidence.
  • Secrets — JWT, encryption, and database credentials are stored in AWS Secrets Manager and injected into the container as environment variables.

Prerequisites

  1. The AWS CLI configured with appropriate permissions.
  2. A VPC with private subnets for the task/EFS and subnets for the ALB, plus an ECS cluster (Fargate).
  3. Access to a container registry for the RegScale image — Docker Hub (regscale/regscale:x.x.x.x) or Iron Bank (registry1.dso.mil/ironbank/regscale/regscale:x.x.x.x).
  4. An external Amazon RDS for SQL Server instance, SQL Server 2022+ with Full-Text Search enabled.
  5. An ACM certificate for your domain (for HTTPS on the ALB).

📘

Replace x.x.x.x with the RegScale release you want to deploy. The latest release tags can be found in the RegScale Changelog.

Step 1 — Create the EFS File System

Create an EFS file system, mount targets in your private subnets, and an access point scoped to UID/GID 1001 (the RegScale container user):

# Create the file system (encrypted)
EFS_ID=$(aws efs create-file-system \
  --encrypted \
  --performance-mode generalPurpose \
  --tags Key=Name,Value=regscale-efs \
  --query 'FileSystemId' --output text)

# Create a security group allowing NFS (2049) from the task security group
#   (create/SG setup omitted — allow TCP 2049 from the ECS task SG)

# Create a mount target in each private subnet
aws efs create-mount-target --file-system-id $EFS_ID \
  --subnet-id <private-subnet-id> --security-groups <efs-sg-id>

# Create an access point owned by UID/GID 1001
EFS_AP_ID=$(aws efs create-access-point \
  --file-system-id $EFS_ID \
  --posix-user Uid=1001,Gid=1001 \
  --root-directory 'Path=/regscale,CreationInfo={OwnerUid=1001,OwnerGid=1001,Permissions=755}' \
  --query 'AccessPointId' --output text)

Step 2 — Store Secrets in AWS Secrets Manager

Generate strong, unique keys (32-char hex, 32 UTF-8 bytes each):

echo "JWTSecretKey=$(openssl rand -hex 16)"
echo "EncryptionKey=$(openssl rand -hex 16)"

⚠️

Do not use openssl rand -base64 32 for these keys — it produces 44 characters (not 32 bytes) and will fail the legacy AES key-length check.

aws secretsmanager create-secret --name regscale/jwt-secret-key --secret-string "<yourJWTSecretKey>"
aws secretsmanager create-secret --name regscale/encryption-key --secret-string "<yourEncryptionKey>"
aws secretsmanager create-secret --name regscale/sql-conn \
  --secret-string "Server=tcp:<rds-endpoint>,1433;Initial Catalog=REGSCALE;Persist Security Info=False;User ID=<db_admin>;Password=<db_password>;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"

Step 3 — Create IAM Roles

Create two roles, both trusting ecs-tasks.amazonaws.com:

  • Task execution role — attach AmazonECSTaskExecutionRolePolicy and grant secretsmanager:GetSecretValue on the three secrets above (so ECS can pull the image and inject secrets).

  • Task role — grant EFS client access:

    {
      "Version": "2012-10-17",
      "Statement": [{
        "Effect": "Allow",
        "Action": [
          "elasticfilesystem:ClientMount",
          "elasticfilesystem:ClientWrite",
          "elasticfilesystem:ClientRootAccess"
        ],
        "Resource": "arn:aws:elasticfilesystem:<region>:<account-id>:file-system/<EFS_ID>"
      }]
    }
    

Step 4 — Register the Task Definition

Create regscale-task.json (Fargate). The EFS volume is mounted at /app/atlas-files, matching StoredFilesPath=atlas-files:

{
  "family": "regscale",
  "requiresCompatibilities": ["FARGATE"],
  "networkMode": "awsvpc",
  "cpu": "2048",
  "memory": "4096",
  "executionRoleArn": "arn:aws:iam::<account-id>:role/regscale-execution-role",
  "taskRoleArn": "arn:aws:iam::<account-id>:role/regscale-task-role",
  "volumes": [
    {
      "name": "regscale-files",
      "efsVolumeConfiguration": {
        "fileSystemId": "<EFS_ID>",
        "transitEncryption": "ENABLED",
        "authorizationConfig": {
          "accessPointId": "<EFS_AP_ID>",
          "iam": "ENABLED"
        }
      }
    }
  ],
  "containerDefinitions": [
    {
      "name": "regscale",
      "image": "regscale/regscale:x.x.x.x",
      "essential": true,
      "portMappings": [
        { "containerPort": 80, "protocol": "tcp" }
      ],
      "environment": [
        { "name": "ASPNETCORE_HTTP_PORTS", "value": "80" },
        { "name": "ASPNETCORE_URLS", "value": "http://+:80" },
        { "name": "StoredFilesPath", "value": "atlas-files" },
        { "name": "FileSizeLimit", "value": "104857600" }
      ],
      "secrets": [
        { "name": "JWTSecretKey", "valueFrom": "arn:aws:secretsmanager:<region>:<account-id>:secret:regscale/jwt-secret-key" },
        { "name": "EncryptionKey", "valueFrom": "arn:aws:secretsmanager:<region>:<account-id>:secret:regscale/encryption-key" },
        { "name": "SQLConn", "valueFrom": "arn:aws:secretsmanager:<region>:<account-id>:secret:regscale/sql-conn" }
      ],
      "mountPoints": [
        { "sourceVolume": "regscale-files", "containerPath": "/app/atlas-files", "readOnly": false }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/regscale",
          "awslogs-region": "<region>",
          "awslogs-stream-prefix": "regscale"
        }
      }
    }
  ]
}

Register it:

aws ecs register-task-definition --cli-input-json file://regscale-task.json

Notes:

  • cpu/memory (2048 / 4096 = 2 vCPU / 4 GB) match the minimum web tier in the Sizing Guide; adjust for your tier.
  • The container listens on port 80; the ALB provides the public HTTPS endpoint.

Step 5 — Create the Service Behind an ALB

  1. Create an Application Load Balancer in your public subnets and a target group of type ip on port 80 with health-check path /health.
  2. Add an HTTPS (443) listener to the ALB using your ACM certificate, forwarding to the target group.
  3. Create the ECS service (Fargate) wired to the load balancer:
aws ecs create-service \
  --cluster regscale-cluster \
  --service-name regscale \
  --task-definition regscale \
  --desired-count 1 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[<private-subnet-ids>],securityGroups=[<task-sg-id>],assignPublicIp=DISABLED}" \
  --load-balancers "targetGroupArn=<target-group-arn>,containerName=regscale,containerPort=80"

📘

Security groups: allow the ALB SG → task SG on 80, the task SG → EFS SG on 2049, and the task SG → RDS SG on 1433.

Step 6 — Access RegScale

Point your DNS (e.g., Route 53) at the ALB, then browse to https://<your-domain> and complete the Post-Deployment Steps.

Scaling

  • Increase the service --desired-count, or configure ECS Service Auto Scaling. Because files are on a shared EFS file system, multiple tasks can run concurrently.
  • Update the image tag in the task definition, register a new revision, and update the service to upgrade — see the Upgrade Guide.