This guide deploys citadel-core on ECS Fargate: one service per node, secrets from AWS Secrets Manager, configuration and identity files from S3, and the encrypted share store on EFS. Infrastructure is expected to be provisioned with Terraform; this page covers what the application needs from it.
How a task is put together
Each task runs four containers in sequence:
- init-secrets: reads the share-store and identity passwords from Secrets Manager into a shared
/secrets volume.
- init-config: downloads
config.yaml, peers.json and the node’s identity files from S3.
- init-data: copies the identity files and
peers.json onto EFS so they survive restarts.
- citadel-core: the node itself: distroless, non-root (UID 65532), read-only root filesystem, reading everything above from mounted volumes.
The main container has no shell, so every secret must already be a file by the time it starts.
Generate key material
Run these on a dedicated, ephemeral workstation (a temporary instance in a private subnet or an air-gapped machine) and wipe it after uploading the outputs. Never on a laptop or shared host.
Generate a share-store password per node and record it with each node’s identity password.
AWS resources
IAM
- Execution role:
AmazonECSTaskExecutionRolePolicy plus logs:CreateLogStream, logs:PutLogEvents and logs:CreateLogGroup on the log group.
- Task role:
secretsmanager:GetSecretValue on secret:citadel-core/*; s3:GetObject and s3:ListBucket on the bucket’s citadel-core/* prefix; elasticfilesystem:ClientMount, ClientWrite and ClientRootAccess on the file system.
Configuration for ECS
Leave the NATS and Consul addresses empty in config.yaml and inject them as the NATS_URL and CONSUL_ADDRESS environment variables on the citadel-core container; the same file then works in every environment.
Task definition
The main container’s command and mounts, with the init containers omitted for brevity:
The data volume is the node’s EFS access point with transit encryption and IAM authorisation enabled. secrets, config and identity are ephemeral task volumes written by the init containers. Passing --peers makes each node sync the registry into Consul at start-up, so no separate register-peers step is needed.
Verify
CloudWatch should show, in order: the version banner, Connected to badger kv store, Loaded peers from consul, [READY] Node is ready, Starting consumers. GET /health on port 8080 reports readiness; add an ECS healthCheck on it if you want the service to track container health (allow a startPeriod of about 60 seconds).
Troubleshooting
Next
Operate a cluster and the security checklist.