Deploy to AWS EC2 (Minimal Effort)
Status: Reference Date: 2026-04-01
This is the simplest path to get Agience running on an EC2 VM with your own domain.
Recommended minimal architecture (single instance)
One EC2 instance runs:
- Caddy (TLS termination + reverse proxy)
- Frontend container
- Service stack (Origin, Mantle, Chorus, Facet) + support (ArangoDB, Postgres, MinIO)
- MinIO (S3-compatible content storage)
Domains (recommended):
app.yourdomain.com→ UIapi.yourdomain.com→ FastAPIcontent.yourdomain.com→ MinIO S3 endpoint (used by presigned URLs)
1) Create the EC2 instance
- Instance type:
t3.largeis a reasonable starting point (ArangoDB + Postgres + Mantle in-process MANTLE/SSE on one box needs RAM) - Disk: 60–100GB gp3 to start
- OS: Ubuntu 22.04 LTS or 24.04 LTS
- Attach an Elastic IP (optional but recommended)
Security Group
Inbound:
- 80/tcp (HTTP, used for ACME challenge → Caddy redirects to 443)
- 443/tcp (HTTPS)
Optional (lock down to your IP if you want admin access):
- 22/tcp (SSH)
Do not expose DB/search ports publicly.
2) DNS
Create A records pointing to the instance public IP:
app→ EC2 IPapi→ EC2 IPcontent→ EC2 IP
3) Install Docker on the instance
SSH into the instance and install Docker + Compose plugin.
(You can use your preferred method; the key requirement is docker compose works.)
Search infrastructure
After Step 2.6.9 (2026-05-09), search runs entirely in-process inside
Mantle on encrypted MANTLE+SSE blobs in MinIO/S3. There is no separate
search container, no JVM tuning, and no vm.max_map_count requirement.
4) Put the app on the server
Two minimal options:
Preferred path: deployment-only host repo
- clone the
agience-homestarter repo onto the instance - place a real
.envalongside it - run
docker compose pull && docker compose up -d
Alternative path: clone this repo on the server
This is still possible for experimentation, but it is no longer the preferred self-host story.
5) Create the .env on the instance
Start from .env.example and fill in the required values.
Minimum values for self-host (example):
-
Domains:
DOMAIN=app.yourdomain.comAPI_DOMAIN=api.yourdomain.com
-
OAuth:
GOOGLE_OAUTH_CLIENT_ID=...GOOGLE_OAUTH_CLIENT_SECRET=...GOOGLE_OAUTH_REDIRECT_URI=https://api.yourdomain.com/auth/callback
-
CORS / allowlists:
FRONTEND_URI=https://app.yourdomain.comBACKEND_URI=https://api.yourdomain.comALLOWED_EMAILS=you@example.com(orALLOWED_DOMAINS=example.com)
-
Frontend runtime config:
VITE_BACKEND_URI=https://api.yourdomain.comVITE_CLIENT_ID=agience-client
These values are read by the running frontend container at startup. They are not build args for the published frontend image.
-
Secrets:
ARANGO_ROOT_PASSWORD=...OPENSEARCH_INITIAL_ADMIN_PASSWORD=...OPENSEARCH_USERNAME=adminOPENSEARCH_PASSWORD=<same as OPENSEARCH_INITIAL_ADMIN_PASSWORD>
-
Content (MinIO):
MINIO_ROOT_USER=agienceMINIO_ROOT_PASSWORD=...CONTENT_BUCKET=agience-contentCONTENT_URI=https://content.yourdomain.comAWS_ACCESS_KEY_ID=<same as MINIO_ROOT_USER>AWS_SECRET_ACCESS_KEY=<same as MINIO_ROOT_PASSWORD>AWS_REGION=us-east-1AWS_ENDPOINT_URL_INTERNAL=http://content:9000AWS_ENDPOINT_URL_PUBLIC=https://content.yourdomain.com
6) Start the stack
On the instance, from the host repo:
docker compose pulldocker compose up -d
7) Verify
https://api.yourdomain.com/versionhttps://api.yourdomain.com/docshttps://app.yourdomain.com/(login should complete)- Upload a small file (validates MinIO + presigned URLs)
Migrating to AWS-native content (S3 + CloudFront)
The default EC2 path uses MinIO for fully self-contained object storage. If you later want to move to AWS-native S3 and CloudFront, the changes are confined to environment variables:
- Remove or disable the MinIO container
- Set
CONTENT_BUCKET,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_REGION - Set
CONTENT_URIto your CloudFront distribution domain - Remove
AWS_ENDPOINT_URL_INTERNALandAWS_ENDPOINT_URL_PUBLIC
CloudFront provides global edge caching and origin shielding. For most single-operator self-hosted installs MinIO is sufficient.