Skip to main content
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:
  1. init-secrets: reads the share-store and identity passwords from Secrets Manager into a shared /secrets volume.
  2. init-config: downloads config.yaml, peers.json and the node’s identity files from S3.
  3. init-data: copies the identity files and peers.json onto EFS so they survive restarts.
  4. 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.