Deploy on AWS (ECS Express Mode)

How to run a Sentinel gateway in your AWS account on ECS Express Mode, register its URL, and avoid the health-check loop

To deploy SUPERWISE® Sentinel on AWS, run the Sentinel gateway container as an ECS Express Mode service with a /healthz health check, its three identity values in Secrets Manager, and its public URL registered as SENTINEL_EXTERNAL_HOST. Express Mode provisions the load balancer, HTTPS certificate, target groups, security groups, and auto scaling for you, and pulls the image directly from the SUPERWISE registry, so no image mirroring is needed.

🧭

ECS Express Mode is one example, not the only option. The gateway is a standard container, so on AWS it runs just as well on EKS (with the Helm chart), plain ECS, App Runner, or EC2. This page walks through ECS Express Mode end to end; pick the service that fits your platform. See Deploy a gateway for the full list.

Before you start

  • Complete Before you deploy: create the Sentinel, and have SENTINEL_ID, SW_CLIENT_ID, and SW_CLIENT_SECRET in your shell.
  • AWS CLI 2.36 or later, signed in, with deploy access to the account.
  • The commands below also use TEAM (a name for this gateway's resources) and ACCOUNT_ID (your AWS account ID).

1. Create the cluster, roles, and secrets

aws ecs create-cluster --cluster-name "sentinel-${TEAM}"

# Execution role: pulls the image and reads the secrets
aws iam create-role --role-name "sentinel-execution-role-${TEAM}" \
  --assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow",
    "Principal":{"Service":"ecs-tasks.amazonaws.com"},"Action":"sts:AssumeRole"}]}'
aws iam attach-role-policy --role-name "sentinel-execution-role-${TEAM}" \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

# Infrastructure role: provisions the load balancer and networking
aws iam create-role --role-name "sentinel-infra-role-${TEAM}" \
  --assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow",
    "Principal":{"Service":"ecs.amazonaws.com"},"Action":"sts:AssumeRole"}]}'
aws iam attach-role-policy --role-name "sentinel-infra-role-${TEAM}" \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSInfrastructureRoleforExpressGatewayServices

# Per gateway: one secret per team
aws secretsmanager create-secret --name "sentinel/${TEAM}/SENTINEL_ID" \
  --secret-string "$SENTINEL_ID" --query ARN --output text

# Per organization: create once, reuse for every gateway
for n in SW_CLIENT_ID SW_CLIENT_SECRET; do
  aws secretsmanager describe-secret --secret-id "sentinel/$n" >/dev/null 2>&1 \
    || aws secretsmanager create-secret --name "sentinel/$n" --secret-string "${!n}"
  aws secretsmanager describe-secret --secret-id "sentinel/$n" --query ARN --output text
done

sleep 60   # let the roles propagate before first use

Grant the execution role secretsmanager:GetSecretValue on those three secret ARNs.

⚠️

Use the full secret ARNs, including the random suffix Secrets Manager appends, in the IAM policy's Resource field. The short form without the suffix resolves in the container definition but silently fails to match in an IAM policy.

2. Create the service and capture its URL

GATEWAY_URL=$(aws ecs create-express-gateway-service \
  --service-name "sentinel-${TEAM}" \
  --cluster "sentinel-${TEAM}" \
  --execution-role-arn "arn:aws:iam::${ACCOUNT_ID}:role/sentinel-execution-role-${TEAM}" \
  --infrastructure-role-arn "arn:aws:iam::${ACCOUNT_ID}:role/sentinel-infra-role-${TEAM}" \
  --health-check-path "/healthz" \
  --scaling-target '{"minTaskCount":2,"maxTaskCount":10}' \
  --primary-container '{
    "image": "us-central1-docker.pkg.dev/admina33d6818/sentinel-public/sentinel:latest",
    "containerPort": 8000,
    "secrets": [
      {"name":"SENTINEL_ID","valueFrom":"<SENTINEL_ID secret ARN>"},
      {"name":"SW_CLIENT_ID","valueFrom":"<SW_CLIENT_ID secret ARN>"},
      {"name":"SW_CLIENT_SECRET","valueFrom":"<SW_CLIENT_SECRET secret ARN>"}
    ]
  }' \
  --query "service.activeConfigurations[0].ingressPaths[0].endpoint" --output text)

echo "$GATEWAY_URL"   # https://se-<id>.ecs.<region>.on.aws
⚠️

Always pass --health-check-path "/healthz". The AWS CLI help says the default is /ping, but the target group is created with /, which the gateway answers with a 404. Tasks then start, heartbeat, and get replaced in a loop.

From the SUPERWISE side this looks like success: the Sentinel shows Active the whole time, because each task does heartbeat before the load balancer replaces it. If traffic fails against a Sentinel that shows Active, check the ECS service events.

The URL is a generated identifier, not your service name, and routing is by host header only. The load balancer's own DNS name returns a 404 from the balancer itself, not from the gateway. Use the assigned hostname, which carries a valid AWS-managed certificate and needs no ACM or DNS work.

If $GATEWAY_URL comes back empty, the load balancer is still provisioning. Wait a minute, then read the URL from the service's detail page in the ECS console.

3. Register the URL

Set SENTINEL_EXTERNAL_HOST to the gateway's URL, so the Connect dialog in the SUPERWISE app points at an address your applications can reach:

aws ecs update-express-gateway-service \
  --service-arn "<service ARN>" \
  --primary-container '{ ...same block, plus: ...
    "environment": [{"name":"SENTINEL_EXTERNAL_HOST","value":"'"$GATEWAY_URL"'"}]
  }'

Updates run blue/green, so two tasks and two target groups during a rollout are expected.

4. Raise the load balancer idle timeout

Raise the provisioned load balancer's idle timeout from its 60-second default, or long streaming responses are cut short. See Raise the request timeout.

5. Verify the gateway

The Sentinel turns Active in the SUPERWISE app within about a minute. First check that the container is serving. /healthz answers 200 OK:

curl -i "$GATEWAY_URL"/healthz
HTTP/1.1 200 OK
...
{"status":"ok"}

/healthz shows only that the gateway is up. Then confirm a guardrail fires with POST /test, as described in Verify the gateway. If traffic fails while the Sentinel shows Active, check the ECS service events for the health-check loop described above.

Update or remove it

To run a newer image or change a variable, run aws ecs update-express-gateway-service with the full container block, as in step 3. Updates run blue/green.

To remove the gateway, delete the service. The Sentinel's configuration stays in the SUPERWISE app.

aws ecs delete-express-gateway-service --service-arn "<service ARN>"

Common errors

Tasks start and get replaced in a loop while the Sentinel shows Active

The target group's health check is on /, which the gateway answers with a 404. Recreate or update the service with --health-check-path "/healthz". See step 2.

The load balancer's DNS name returns 404

Routing is by host header only, so the balancer answers its own DNS name with a 404 before the request reaches the gateway. Use the assigned https://se-<id>.ecs.<region>.on.aws hostname.

The task cannot read its secrets

The IAM policy names a secret ARN without the random suffix Secrets Manager appends. The short form resolves in the container definition but does not match in an IAM policy. Use the full ARN. See step 1.

Long responses are cut short

The load balancer's idle timeout is still at its 60-second default. See step 4.

Get Sentinel: Create a free SUPERWISE account.

See also


Did this page help you?