Appearance
AWS — CloudFormation
This stack provisions a complete production Agentcy environment in a single AWS account using AWS-native services. It's the fastest path to a managed AWS deployment without operating Kubernetes.
What Gets Provisioned
| Component | Service |
|---|---|
| Networking | VPC, 2 public + 2 private subnets, NAT Gateway, Route 53 hosted zone (optional) |
| Compute | ECS Fargate (agentcy-api, agentcy-frontend) |
| Load balancer | Application Load Balancer with ACM certificate |
| Postgres | RDS Postgres 16 (db.t4g.medium) with the vector extension enabled |
| Redis | ElastiCache Redis 7 (cache.t4g.micro, single node) |
| Context Engine | Basic provider — Neo4j AuraDB (external; you create separately and pass the URI). Advanced provider — kyma sidecar on ECS Fargate writing to an S3 bucket the stack provisions; toggle via the ContextEngine parameter. |
| Secrets | AWS Secrets Manager for DB and JWT secrets |
| Logs | CloudWatch Logs |
Why AuraDB on Basic?
Running Neo4j on EC2 or ECS works but requires you to manage backups, upgrades, and HA. AuraDB handles that for you and has an AWS-region peer for low latency. If you must keep the Basic graph inside AWS, see the Self-managed Neo4j section below.
Picking Advanced (kyma) instead
Set ContextEngine=advanced on the stack to provision a kyma sidecar (Fargate task) plus an S3 bucket for columnar extents. The CFN template wires CONTEXT_ENGINE=advanced, KYMA_BASE_URL, KYMA_TOKEN automatically. Reads happen via Arrow Flight gRPC — your agents get KQL / SQL / Cypher against the same engine. Reference docs at getkyma.dev.
Prerequisites
- AWS CLI v2 configured with an account that has admin access (narrower scope possible — see IAM section)
- A registered domain in Route 53 (or external DNS) and an ACM certificate in the deploy region
- A Neo4j AuraDB instance — note the URI, username, and password
- An LLM API key (Anthropic or OpenAI) stored in Secrets Manager
Get the Template
The CloudFormation template ships in the release tarball at infra/aws/cloudformation/agentcy.yaml. The same template is also published to a public S3 bucket (URL provided to self-host customers).
Pre-create Secrets
Create these secrets first — the stack references them:
bash
aws secretsmanager create-secret \
--name agentcy/llm-api-key \
--secret-string "sk-ant-..."
aws secretsmanager create-secret \
--name agentcy/neo4j \
--secret-string '{
"uri":"neo4j+s://xxxxx.databases.neo4j.io",
"username":"neo4j",
"password":"replace-me"
}'
aws secretsmanager create-secret \
--name agentcy/jwt \
--secret-string "$(openssl rand -hex 32)"Deploy
bash
aws cloudformation deploy \
--template-file agentcy.yaml \
--stack-name agentcy-prod \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
DomainName=agentcy.example.com \
AcmCertificateArn=arn:aws:acm:us-east-1:123456789012:certificate/xxxxx \
DbInstanceClass=db.t4g.medium \
ApiTaskCount=2 \
FrontendTaskCount=2 \
LlmProvider=anthropic \
AuthProvider=local \
BackendImage=ghcr.io/agentcy/backend:latest \
FrontendImage=ghcr.io/agentcy/frontend:latestWatch the stack:
bash
aws cloudformation describe-stack-events \
--stack-name agentcy-prod \
--query 'StackEvents[].[Timestamp,ResourceStatus,ResourceType,LogicalResourceId]' \
--output tableProvisioning takes 12–20 minutes — most of it is RDS and ElastiCache.
Outputs
Once the stack is CREATE_COMPLETE:
bash
aws cloudformation describe-stacks \
--stack-name agentcy-prod \
--query 'Stacks[0].Outputs'You'll get the ALB DNS name and the application URL. Point your domain at the ALB via a Route 53 alias (the template can do this for you if you pass HostedZoneId).
Parameters
| Parameter | Default | Description |
|---|---|---|
DomainName | — | Public hostname for the app |
AcmCertificateArn | — | ACM certificate covering DomainName |
HostedZoneId | (empty) | If set, creates a Route 53 alias |
VpcCidr | 10.42.0.0/16 | VPC CIDR |
DbInstanceClass | db.t4g.medium | RDS instance class |
DbAllocatedStorage | 50 | Initial DB storage (GB) |
DbMaxAllocatedStorage | 200 | Storage autoscaling cap |
DbMultiAz | false | Set to true for production HA |
RedisNodeType | cache.t4g.micro | ElastiCache node size |
ApiTaskCount | 2 | API replicas |
FrontendTaskCount | 2 | Frontend replicas |
BackendImage | ghcr.io/agentcy/backend:latest | Backend image |
FrontendImage | ghcr.io/agentcy/frontend:latest | Frontend image |
LlmProvider | anthropic | anthropic or openai |
AuthProvider | local | local or oidc |
ContextEngine | basic | basic (Neo4j-compatible Bolt) or advanced (kyma + S3) |
KymaImage | ghcr.io/agentcylabs/kyma:latest | Only used when ContextEngine=advanced |
Self-managed Neo4j (Basic provider, on AWS)
If you must keep the Basic-provider graph inside AWS:
- Use the companion stack
infra/aws/cloudformation/neo4j-ec2.yaml. It runs Neo4j 5 Community on a single EC2 instance with EBS-backed/data, daily snapshots via Data Lifecycle Manager, and a Security Group that only the ECS task security group can reach. - Pass the private DNS name as
Neo4jUri=bolt://neo4j.internal:7687when deploying the main stack.
For HA, use Neo4j Enterprise with a causal cluster — the template doesn't ship this configuration, so contact agentcylabs.com for an enterprise reference architecture.
Self-hosting kyma (Advanced provider, on AWS)
When ContextEngine=advanced:
- The CFN template provisions an S3 bucket (
agentcy-kyma-<account>-<region>) and an ECS Fargate service running the kyma container (KymaImageparameter, defaultghcr.io/agentcylabs/kyma:latest). - kyma reads/writes Arrow extents on S3 via the
object_storecrate (zero-egress paths within the same region) and persists its catalog in the same RDS Postgres instance under a separate database (kyma_catalog). - The API task gets
CONTEXT_ENGINE=advanced,KYMA_BASE_URL=http://kyma.<vpc>:8080,KYMA_TOKEN(Secrets Manager),KYMA_DATABASE=kyma. - OTLP gRPC port
4317is exposed via a Network Load Balancer if theEnableOtlpparameter is set, so emitters across your stack can ship logs/traces directly into kyma.
For multi-region or federation, see the kyma docs at getkyma.dev — the same single-binary model scales out without a rewrite, so the CFN template is forward-compatible.
IAM Footprint
The stack creates two IAM roles:
| Role | Permissions |
|---|---|
agentcy-task-execution-role | Pull images from ECR/GHCR, write CloudWatch Logs, read three Secrets Manager secrets |
agentcy-task-role | App-level — read-only access to other Secrets Manager paths under agentcy/connectors/* |
If your org requires Permission Boundaries, add --parameter-overrides PermissionsBoundary=arn:aws:iam::123:policy/MyBoundary to the deploy command.
Cost Estimate
| Item | Monthly (us-east-1, on-demand) |
|---|---|
| ECS Fargate · 4 tasks @ 0.5 vCPU / 1 GB | ~$60 |
| ALB | ~$20 |
RDS db.t4g.medium (single-AZ) | ~$60 |
ElastiCache cache.t4g.micro | ~$12 |
| NAT Gateway | ~$32 |
| CloudWatch Logs | ~$5 |
| AuraDB Free tier | $0 |
| Total | ~$190/mo |
Multi-AZ RDS roughly doubles the DB line item. Switch to AuraDB Pro and add ~$65/mo when you outgrow the free tier.
Updating
To roll a new image, update the BackendImage or FrontendImage parameter and re-run cloudformation deploy. ECS does a rolling update (50% min healthy, 200% max) — no downtime.
To change anything else (instance size, replica count, etc.) just modify the parameter override and redeploy.
Tearing Down
bash
aws cloudformation delete-stack --stack-name agentcy-prodData
RDS and ElastiCache are deleted with the stack — take a snapshot first if the data matters. The Secrets Manager secrets you created out-of-band stay behind.
Next Steps
- Architecture & Tech Stack — what each AWS service replaces and why
- AWS ECS — deeper dive on the ECS task definitions
- Audit Log