Deploy with Docker Compose
Use Compose when the whole platform fits on one host and you do not need horizontal scaling or rolling updates. It is the fastest path to a running system and the one the repository exercises most. Use Kubernetes instead when you need more than one replica of anything, or when you want MCP server instances and agent sandboxes confined by a RuntimeClass — the Compose stack drives MCP containers through the host Docker socket, which offers no kernel isolation. Two Compose files sit at the repository root:
This guide covers
docker-compose.yaml.
Prerequisites
- Docker Engine 20.10 or later with Compose v2 (
docker compose, notdocker-compose) - A clone of the repository — the Compose file bind-mounts
./config/auth/kratosand./agentarea-platform/temporal-config, so it does not run standalone - Ports 3000, 4433, 7999, 8000 free on the host, plus 5432, 6379, and 9000 if you publish them
- See requirements for the full list
Steps
1. Create the environment file
.env.example ships working development values. Three of them must be replaced
before this is a deployment rather than a demo.
2. Generate the secrets that must not stay at their defaults
SECRET_MANAGER_ENCRYPTION_KEY is the Fernet key that encrypts every stored
credential — LLM provider keys, MCP server secrets — in the agentarea
database. It must be a valid Fernet key, and it must not change after data
exists, or the ciphertext already written becomes unreadable.
SANDBOX_ACTIVATION_AUTH_SECRET and SANDBOX_CLEANUP_AUTH_SECRET are HMAC
shared secrets between the control plane and the sandbox runner. Both must be at
least 32 bytes. The Compose file declares them with :?, so docker compose
refuses to start the stack if either is empty rather than falling back to a
default.
.env:
.env.example also ships a KRATOS_JWKS_B64 value with the private key
included. It is a published test key. Anyone can mint tokens your API will
accept. Replace it before exposing the API to a network you do not control.
3. Start the stack
make up — which runs the same file in the foreground.
Compose brings services up in dependency order:
dbbecomes healthy (pg_isready), andrustfsbecomes healthy.postgres_initcreates theagentarea,temporal, andkratosdatabases. It is idempotent.app_migrationsrunsagentarea-api migratewith working directory/app/apps/api, andkratos-migrateruns the Kratos schema migration.rclone-initcreates the two object-storage buckets.app,frontend,agentarea-worker,agentarea-events,agentarea-mcp-manager,sandbox-executor,temporal, andkratosstart.
postgres_init, app_migrations, kratos-migrate,
rclone-init) exit 0 and stay in Exited state. That is the expected result,
not a failure.
temporal has a 120-second start_period on its health check, and
agentarea-worker waits for it. First start therefore takes a couple of minutes
before tasks can run.
4. Reach the platform
The Compose file publishes no ports for PostgreSQL, Valkey, RustFS, Temporal, or
the event service. They are reachable only on the Compose networks.
Verify
Check that the long-running services are up and the one-shot jobs completed:Exited (0). Any other exit code means
that step failed and the services depending on it did not start.
Check the two HTTP health endpoints:
Migrations completed successfully. Stamped database to head revision is also a success — it means the schema already existed and Alembic
recorded the current head without replaying migrations.
Troubleshooting
docker compose exits immediately with SANDBOX_ACTIVATION_AUTH_SECRET must be set. The Compose file uses ${VAR:?message} for both sandbox secrets, so
an empty value aborts the run rather than starting an unauthenticated sandbox
path. Set both in .env.
The API container restarts in a loop with
SECRET_MANAGER_ENCRYPTION_KEY environment variable must be set. The default
secret backend is database, which requires a Fernet key. SecretManagerFactory
validates this at construction time and raises, so the process exits at startup
instead of failing later on the first secret read. Generate a key as in step 2.
app never starts and app_migrations shows Exited (1). Read
docker compose -f docker-compose.yaml logs app_migrations. The migration
command prints Migration failed: followed by the cause. A dirty database — one
with tables from another product but no provider_specs table — makes Alembic
apply migrations onto a schema it does not own. See
database and migrations.
The db container refuses to start after an upgrade from an older checkout.
postgres:18 stores data under /var/lib/postgresql/<version>/docker, so the
Compose file mounts ./data/postgres at /var/lib/postgresql, not at the legacy
/var/lib/postgresql/data. A data directory laid out for the old mount point
will not start under this file.
Tasks stay queued and never execute. agentarea-worker waits for
temporal to pass its health check, which has a 120-second start period. Check
docker compose -f docker-compose.yaml logs temporal and confirm the worker is
running.
Stopping the stack loses data. docker compose down -v removes volumes. The
platform’s data lives in bind mounts under ./data/ (postgres, rustfs,
valkey), so down -v does not remove it — but rm -rf ./data does. See
backup and recovery.