Skip to main content
Platforms

AWS ECS

Run the Rivet control plane on AWS ECS with Fargate.

Requirements

  • An AWS account with ECS and Fargate available
  • An EFS file system, or an RDS PostgreSQL instance, for persistence
  • An Application Load Balancer in front of the service
  • AWS Secrets Manager, or SSM Parameter Store, for the admin token

A published Terraform module for the control plane does not exist yet. The AWS ECS template covers workers, not the control plane. The steps below are the generic container requirements mapped onto ECS.

Steps

Provision storage

Fargate task storage is ephemeral, so the data directory must live outside the task.

  • Single node: create an EFS file system and mount it into the task at /data.
  • Multi-node: create an RDS PostgreSQL instance instead and skip the volume. Use the direct endpoint, not RDS Proxy, which is not supported. See Storage.

Store the admin token

aws secretsmanager create-secret \
  --name rivet/admin-token \
  --secret-string "$(openssl rand -hex 32)"

Reference it from the task definition’s secrets block rather than putting it in environment.

Define the task

Use a pinned image tag, expose both ports, and mount the volume.

{
  "family": "rivet-engine",
  "requiresCompatibilities": ["FARGATE"],
  "networkMode": "awsvpc",
  "cpu": "1024",
  "memory": "2048",
  "containerDefinitions": [
    {
      "name": "rivet-engine",
      "image": "rivetdev/engine:2.3.3",
      "portMappings": [
        { "containerPort": 6420 },
        { "containerPort": 6421 }
      ],
      "environment": [
        { "name": "RIVET__FILE_SYSTEM__PATH", "value": "/data" }
      ],
      "secrets": [
        {
          "name": "RIVET__AUTH__ADMIN_TOKEN",
          "valueFrom": "arn:aws:secretsmanager:REGION:ACCOUNT:secret:rivet/admin-token"
        }
      ],
      "mountPoints": [
        { "sourceVolume": "rivet-data", "containerPath": "/data" }
      ]
    }
  ],
  "volumes": [
    {
      "name": "rivet-data",
      "efsVolumeConfiguration": { "fileSystemId": "fs-XXXXXXXX" }
    }
  ]
}

1 CPU and 2 GB of RAM is the recommended minimum per instance.

Raise the ALB idle timeout

The ALB default is 60 seconds, which severs long-lived WebSockets and causes reconnect storms.

aws elbv2 modify-load-balancer-attributes \
  --load-balancer-arn <arn> \
  --attributes Key=idle_timeout.timeout_seconds,Value=3600

Point the target group at container port 6420 and terminate TLS on the listener. See Ports.

Configure health checks

Set the target group health check path to /health on port 6421, with a five second timeout.

Verify

aws ecs describe-services --cluster <cluster> --services rivet-engine \
  --query 'services[0].deployments[0].rolloutState'

Expect COMPLETED. Then open /ui on the load balancer hostname and log in with the admin token.

Create the runner config

A runner config declares a pool that workers connect to. Without one the control plane refuses every worker, so create it before deploying any:

curl -X PUT "http://<load-balancer-host>/runner-configs/default?namespace=default" \
  -H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"datacenters":{"default":{"normal":{}}}}'

Use normal for long-lived workers. See Runner configs.

Scaling past one task

EFS-backed file system storage is single-node: RocksDB cannot be shared between tasks. Before raising the desired count above one, move to RDS PostgreSQL and add NATS for pub/sub. See Storage.

Once on RDS, ECS Service Auto Scaling can drive the desired count. Use target tracking on average CPU at 60%, matching the Kubernetes manifests, and keep a minimum of two tasks so the service survives losing one. Leave the desired count at one for as long as the task is on EFS.

Next steps