Deploy on AWS ECS Fargate
This guide deploys AxonFlow Enterprise into your own AWS account as an ECS Fargate stack using CloudFormation. It is the right path when you run the platform inside your own VPC and region and own the deployment — as opposed to the AWS Marketplace procurement flow, which uses the same template.
Enterprise. The container images are the licensed Enterprise build and are pulled from a private registry using the credentials in your welcome bundle. For a Community trial on a single machine, see Deploy on a Cloud VM instead.
What it deploys
A single CloudFormation stack provisions:
- Agent — the policy-enforcement service (replica-scaled).
- Orchestrator — LLM routing and policy management (a required service).
- Customer Portal and Portal UI — the governance console (optional; set
their desired counts to
0to disable). - RDS PostgreSQL — the platform database.
- Application Load Balancer —
internalby default, orinternet-facing. - EFS volume — durable storage for the audit trail.
- Prometheus and Grafana — optional monitoring.
Architecture notes
- The agent is replica-interchangeable. Any agent task can serve any
request; scale
AgentDesiredCountup or down freely, and the load balancer spreads traffic across them. There is no agent-side sticky state. - The orchestrator is a required service. The agent depends on it for LLM
routing and policy management, so it must be running (
OrchestratorDesiredCount≥ 1) for the platform to serve traffic.
Prerequisites
| Requirement | Detail |
|---|---|
| VPC | An existing VPC in your target region. |
| Public subnets | 2, in 2 Availability Zones, each routing 0.0.0.0/0 to an Internet Gateway (for the load balancer). |
| Private subnets | 2, in the same 2 AZs (for the app services and RDS). |
| Tooling | AWS CLI v2 and Docker (with buildx), authenticated to your account. |
| Registry credentials | A username and read:packages token from your welcome bundle. |
| HTTPS (optional) | An ACM certificate ARN, or a domain name for the template to auto-issue one. Without it the load balancer serves HTTP only. |
Choose a registry option
The stack pulls images from whatever you set in ContainerImageRegistry. There
are two supported ways to make the images pullable by ECS.
Option 1 — Private registry direct (quickstart)
Fewest moving parts, ideal for an evaluation: ECS pulls the images straight from the private registry using a pull credential you store in AWS Secrets Manager.
Create the secret. Its value must be JSON of exactly this shape — a bare token string will not authenticate:
{"username":"<your-registry-username>","password":"<your-read:packages-token>"}
aws secretsmanager create-secret \
--name axonflow/registry-pull \
--secret-string '{"username":"YOUR_USER","password":"YOUR_TOKEN"}' \
--query ARN --output text
Then deploy with the private registry as ContainerImageRegistry and the
secret ARN as RegistryCredentialsSecretArn. The stack grants the task
execution role read access to that one secret so ECS can authenticate the pull.
A
read:packagestoken expires on its configured schedule. When it does, image pulls fail withCannotPullContainerErrorin the ECS service events; rotate the secret value to recover. For production, prefer Option 2.
Option 2 — Mirror to your own ECR (recommended for production)
Copy the images once into an ECR registry in your own account and point the stack there. This is the recommended production path because:
- Fast, same-region pulls — no cross-region or public-internet fetch on every task placement.
- Execution-role auth that never expires — ECS pulls from ECR using the task execution role, so there is no registry token to rotate and no token-expiry surprise mid-deploy.
- No internet egress required — with an ECR VPC endpoint, private subnets pull images with no route to the internet at all, which matters for regulated and data-sovereignty deployments.
- Fetch decoupled from rollout — mirroring happens ahead of time, so a slow pull never blocks a stack update.
The deployment package (in the axonflow-install bundle) includes
mirror-images-to-your-ecr.sh, which copies the six images from the private
registry into your ECR with the multi-architecture manifest preserved:
export AXONFLOW_GHCR_USER=your-registry-username
export AXONFLOW_GHCR_TOKEN=your-read-packages-token
./mirror-images-to-your-ecr.sh \
123456789012.dkr.ecr.us-east-1.amazonaws.com/axonflow 9.9.0
Then deploy with your ECR registry as ContainerImageRegistry and leave
RegistryCredentialsSecretArn empty — ECS pulls from ECR via the execution
role.
Key parameters
| Parameter | Notes |
|---|---|
OrganizationID | Your organization identifier (lowercase, hyphens). Required. |
VpcId, PublicSubnet1/2, PrivateSubnet1/2 | Your network (see prerequisites). Required. |
DBPassword | RDS master password. Required. |
ContainerImageRegistry | The private registry (Option 1) or your ECR registry (Option 2). |
RegistryCredentialsSecretArn | Secrets Manager ARN of {"username","password"} for the private-registry pull; empty for ECR. |
AgentImageTag … GrafanaImageTag | v-prefixed release image tag (e.g. v9.9.0); release images are published v-prefixed. |
AgentDesiredCount, OrchestratorDesiredCount | Replica counts. |
CustomerPortalDesiredCount, CustomerPortalUIDesiredCount | Set to 0 to disable. |
DeployPrometheus, DeployGrafana | Optional monitoring. |
LoadBalancerScheme | internal (default) or internet-facing. |
DomainName / CertificateArn | Optional HTTPS. |
Deploy
The template is larger than CloudFormation's inline body limit, so upload it to an S3 bucket in your account and deploy by URL:
REGION=us-east-1
BUCKET=my-cfn-templates # a bucket you own in $REGION
aws s3 cp cloudformation-ecs-fargate.yaml \
"s3://$BUCKET/axonflow/cloudformation-ecs-fargate.yaml"
aws cloudformation create-stack \
--region "$REGION" \
--stack-name axonflow-prod \
--template-url "https://$BUCKET.s3.amazonaws.com/axonflow/cloudformation-ecs-fargate.yaml" \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
--parameters \
ParameterKey=OrganizationID,ParameterValue=your-org \
ParameterKey=VpcId,ParameterValue=vpc-xxxxxxxx \
ParameterKey=PublicSubnet1,ParameterValue=subnet-aaaa \
ParameterKey=PublicSubnet2,ParameterValue=subnet-bbbb \
ParameterKey=PrivateSubnet1,ParameterValue=subnet-cccc \
ParameterKey=PrivateSubnet2,ParameterValue=subnet-dddd \
ParameterKey=DBPassword,ParameterValue='ChangeMe-Strong-Passw0rd!' \
ParameterKey=ContainerImageRegistry,ParameterValue=<registry> \
ParameterKey=RegistryCredentialsSecretArn,ParameterValue=<secret-arn-or-empty>
aws cloudformation wait stack-create-complete --region "$REGION" --stack-name axonflow-prod
Verify
# Stack outputs (endpoints)
aws cloudformation describe-stacks --region "$REGION" --stack-name axonflow-prod \
--query 'Stacks[0].Outputs' --output table
# ECS services should show runningCount == desiredCount
CLUSTER=$(aws cloudformation describe-stack-resources --region "$REGION" --stack-name axonflow-prod \
--query 'StackResources[?ResourceType==`AWS::ECS::Cluster`].PhysicalResourceId' --output text)
aws ecs list-services --region "$REGION" --cluster "$CLUSTER" --output table
The agent and orchestrator expose /health. With an internal load balancer
these endpoints are reachable only from inside the VPC (for example over an SSM
port-forward or a bastion); with LoadBalancerScheme=internet-facing they are
reachable from your workstation.
If images fail to pull, inspect the ECS service events — a bad or expired
pull credential shows there as CannotPullContainerError. It does not appear
in the CloudFormation stack events (CloudFormation only reports that the service
did not stabilize):
aws ecs describe-services --region "$REGION" --cluster "$CLUSTER" \
--services <service-arn> --query 'services[0].events[:10].message' --output table
Upgrade
Upgrade the whole stack to a single version so every service stays in lock-step; do not bump one image in isolation.
For the mirror-to-ECR path, the deployment package includes upgrade.sh, which
mirrors the new version into your ECR, flips only the six image-tag parameters
(keeping every other parameter unchanged), rolls the update, and re-checks that
the services reach a steady state:
cp upgrade.env.example upgrade.env # set STACK_NAME + ECR_REGISTRY
./upgrade.sh 9.9.0
For the private-registry-direct path, run update-stack with the new image tags
and the same RegistryCredentialsSecretArn.
Next steps
- Post-Deployment — what to validate once the stack is healthy.
- CloudFormation Deployment — a deeper look at the stack's networking and resources.
- Deployment Operations — running the platform day to day.
