Deploy on AWS
One module call stands up one Vocion installation in your AWS account. You own the account, the data and the keys.
Get the module
The module is infra/terraform/modules/vocion-stack in vocion-core. Pin vocion-core as a submodule of your infrastructure repo so the infrastructure and the application move on one pin, and confirm the tag you pin contains the module (git -C vocion-core ls-tree <tag> infra/terraform/modules/vocion-stack). It needs OpenTofu 1.6+ and the AWS provider >= 5.70, < 6.0; region and credentials come from your provider block.
What it builds
| Default | |
|---|---|
| Network | VPC; public subnets for the load balancer and host; private DB subnets with no route out |
| Edge | ALB with an ACM certificate (DNS-validated in your zone), HTTPS only, WAF with AWS managed rules and a per-IP rate limit |
| Host | One EC2 (Amazon Linux 2023, encrypted, IMDSv2), r6i.large by default; reachable only from the ALB; access by SSM Session Manager, no SSH |
| Database | RDS PostgreSQL 16 with pgvector, TLS forced, encrypted with a customer-managed key, point-in-time recovery, deletion protection |
| Credential vault | A KMS key; the app runs VOCION_CREDENTIAL_VAULT=kms |
| Media | A private, versioned S3 bucket |
| Secrets | Two Secrets Manager entries, created empty — no secret value is ever in state |
| Backup | AWS Backup daily into a vault locked in governance mode |
| Logs and alarms | ALB, WAF and VPC flow logs (90 days); SNS alarms for the host, the site, 5xx and RDS |
| Models | The host's role may invoke Anthropic models on Bedrock (bedrock_enabled, default on) |
| Optional | Engineering runners on Fargate (runners_enabled), AgentCore IAM (agentcore_enabled), a monthly budget alarm |
It does not create your hosted zone, put secret values, self-host Langfuse, or deploy an AgentCore runtime.
The module call
module "vocion" {
source = "./vocion-core/infra/terraform/modules/vocion-stack"
name_prefix = "northwind-vocion"
azs = ["us-east-1a", "us-east-1b"]
hostname = "agents.northwind.example"
route53_zone_id = aws_route53_zone.agents.zone_id # null: add the validation records yourself
core_ref = "v5.0.0" # tag or 40-char commit; a branch is rejected
app_env = { # non-secret settings only (stored in SSM)
VOCION_SCHEDULE_OWNER = "1"
VOCION_ENFORCE_WORKSPACE_ACCESS = "1"
LANGFUSE_ENABLED = "false"
}
alarm_emails = ["ops@northwind.example"]
}
Set VOCION_SCHEDULE_OWNER = "1" yourself: the module doesn't, and without it no schedule, automation or workflow runs. Useful switches: instance_type, db_instance_class, db_multi_az, alias_hostnames (old hostnames 301 to the new one, /api/* served in place), waf_rate_limit, backup_retention_days (35), runners_enabled, budget_monthly_usd. The module README lists all inputs and outputs.
First deploy
tofu init && tofu apply
The host boots, finds empty secrets and waits.
1. App secrets. At least AUTH_SECRET and a separate VOCION_TOOL_SIGNING_SECRET (openssl rand -base64 32 each), plus your model keys if you don't use Bedrock:
aws secretsmanager put-secret-value \
--secret-id "$(tofu output -raw app_env_secret_name)" \
--secret-string file://app-env.json
2. The app's database login. Port-forward to RDS through the host and connect as the master user:
aws ssm start-session --target "$(tofu output -raw instance_id)" \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters "host=$(tofu output -raw db_address),portNumber=5432,localPortNumber=15432"
CREATE EXTENSION IF NOT EXISTS vector;
CREATE ROLE vocion_app LOGIN PASSWORD '<generated>';
ALTER DATABASE vocion OWNER TO vocion_app;
Store that login in rds_app_secret_name as {"username":"vocion_app","password":"<generated>"}.
3. Deploy. Run tofu output -raw deploy_command. The deploy checks out the pinned release, builds .env.production from the secret and app_env, builds the image on the host (or pulls one with VOCION_APP_IMAGE), applies migrations before swapping containers, starts the app behind Caddy, and fails loudly unless the new container reports the pinned commit.
4. First admin. The installation starts closed: no accounts, no users, and web sign-up only accepts invites. From a vocion-core checkout at the same release, with the port-forward from step 2 open, point DATABASE_URL at the vocion_app login and create the first admin:
DATABASE_URL='postgresql://vocion_app:<generated>@localhost:15432/vocion?sslmode=require' \
npm run user:create --workspace @vocion/core -- \
--email you@northwind.example --account "Northwind" --role admin
Everyone else is invited from Members.
Upgrade and roll back
- Upgrade: set
core_refto the new tag,tofu apply, then deploy (or deploy with--parameters ref=v5.y.zfirst). - Roll back: deploy the previous tag. Migrations only move forward, written expand-then-contract so the previous release runs on the newer schema.
- Change a secret:
put-secret-value, then deploy. Changeapp_env:tofu apply, then deploy.
See Release lines and upgrades.
Without the module
infra/aws in vocion-core is the older single-host path: one EC2 with Postgres, Langfuse and Caddy in Docker Compose (bootstrap.sh, update.sh <ref>). New installations should use the module.