Self-Hosting
Deploy Syntropic137 on your own infrastructure, from bare metal to Kubernetes
Deployment Architecture
Syntropic137 runs as a set of Docker services connected through a shared network:
Quick Start with Docker Compose
npx @syntropic137/setup initThe setup wizard handles everything: Docker validation, secret generation, API key configuration, image pulls, and starting the stack. See Getting Started for the full walkthrough.
The dashboard will be available at http://localhost:8137.
Services
| Service | Port | Description |
|---|---|---|
api | 8000 | FastAPI backend: REST API, SSE |
gateway | 80 | nginx reverse proxy + React dashboard frontend |
event-store | 50051 | gRPC event sourcing server |
event-collector | 8080 | High-throughput event ingestion |
timescaledb | 5432 | PostgreSQL + TimescaleDB for events and metrics |
redis | 6379 | Caching, pub/sub, projection store |
minio | 9000 | S3-compatible artifact storage |
Workspace Isolation
Each agent execution runs in an isolated Docker workspace with a two-phase security model:
Setup Phase: Secrets are available briefly to configure credential helpers. Raw tokens are cleared before the agent starts.
Agent Phase: A claude phase runs with its Anthropic credential, ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN, as its only runtime secret. A codex phase runs with an empty agent environment, its credential having been staged to ~/.codex/auth.json during provisioning. CODEX_AUTH_JSON is a deployment variable, not something the agent sees. Git operations use cached credentials from the setup phase.
AppArmor hosts (Codex workspaces)
On a Linux host whose Docker daemon enforces AppArmor (Ubuntu 24.04 and most Debian/Ubuntu servers; docker info lists name=apparmor under Security Options), Codex workspaces run under the AppArmor profile agentic-codex-sandbox: Docker's default profile with its blanket deny mount, replaced by only the mounts the Codex sandbox (bubblewrap) performs. The workspace refuses to start until that profile is loaded, and the execution fails with the provision reason apparmor_profile_not_loaded naming the fix.
Load it once on the Docker host. It is persisted under /etc/apparmor.d, so it loads again at every boot:
# From a repository checkout (also run automatically by `just selfhost-up`):
just apparmor-setup
# Without a checkout, copy the profile shipped in the running API image:
docker exec syn137-api python -c \
"from agentic_isolation import codex_sandbox_apparmor_profile_path as p; print(p().read_text(), end='')" \
| sudo tee /etc/apparmor.d/agentic-codex-sandbox >/dev/null
sudo apparmor_parser -r /etc/apparmor.d/agentic-codex-sandboxjust selfhost-update re-runs this step on every update, before restarting services. On an npx install, re-run the two commands above after each update.
Workspace image after an update
.env.example carries the default workspace image digest, and a SYN_WORKSPACE_DOCKER_IMAGE value in .env overrides the built-in default. just selfhost-update replaces a value that is a default shipped by an older release with the new default and prints the change; any other value is treated as your own override and left alone. The API also logs a warning at startup when the configured image is an older shipped default. On an npx install, remove the variable from .env (or set it to the new digest) after updating. Hosts without AppArmor (macOS, Docker Desktop) need nothing; just apparmor-setup skips them. The profile adds no capabilities, never uses apparmor=unconfined, and leaves host sysctls alone.
Environment Variables
| Variable | Default | Description |
|---|---|---|
APP_ENVIRONMENT | development | Environment mode (development, production) |
TIMESCALEDB_HOST | localhost | TimescaleDB hostname |
TIMESCALEDB_PORT | 5432 | TimescaleDB port |
REDIS_URL | redis://localhost:6379 | Redis connection URL |
MINIO_ENDPOINT | localhost:9000 | MinIO S3 endpoint |
MINIO_ACCESS_KEY | MinIO access key | |
MINIO_SECRET_KEY | MinIO secret key | |
GITHUB_APP_ID | GitHub App ID for webhook triggers | |
GITHUB_APP_PRIVATE_KEY | GitHub App private key (PEM) | |
ANTHROPIC_API_KEY | API key for claude phases. CLAUDE_CODE_OAUTH_TOKEN takes priority when both are set | |
CODEX_AUTH_JSON | Full contents of a Codex ~/.codex/auth.json. Required for codex phases, unused by claude phases |
Scaling Options
Single Server (Self-Host)
Recommended for 10–100 concurrent agents:
- 4+ CPU cores, 8GB+ RAM (16GB recommended)
- 500GB NVMe storage
- Docker with workspace pooling (10–50 containers)
Multi-Server
For 100–1,000 concurrent agents, run multiple Syntropic137 instances behind a load balancer:
Kubernetes
For 1,000+ concurrent agents with auto-scaling:
- Use Kata Containers runtime for workspace isolation
- HPA with 70% CPU target, 3–20 replicas
- PersistentVolume for artifact storage
Authentication
Gateway credentials
The nginx gateway uses HTTP Basic Auth to protect external access. During npx @syntropic137/setup init, a strong random password (~256 bits of entropy) is generated automatically and stored in ~/.syntropic137/.env.
The setup wizard never prints the password to the terminal. Retrieve it from ~/.syntropic137/.env when needed:
# View password
grep SYN_API_PASSWORD ~/.syntropic137/.env | cut -d= -f2
# Copy to clipboard (macOS)
grep SYN_API_PASSWORD ~/.syntropic137/.env | cut -d= -f2 | pbcopy
# Set env vars for the syn CLI
export SYN_API_USER=admin
export SYN_API_PASSWORD=$(grep SYN_API_PASSWORD ~/.syntropic137/.env | cut -d= -f2)To rotate credentials (generates a new password and restarts the stack):
npx @syntropic137/setup credentials rotatePort model
The gateway exposes two ports with different auth policies:
| Port | Auth | Used by |
|---|---|---|
| 80 | None | Docker health checks, internal service traffic, local dev (localhost:8137) |
| 8081 | Basic Auth | Cloudflare Tunnel, any external access |
If you use Cloudflare Tunnel, your tunnel config must route to http://gateway:8081 (not localhost:8137). The setup wizard enforces this: npx @syntropic137/setup tunnel will exit with an error if SYN_API_PASSWORD is not set.
See the Tunnels guide for full setup instructions.
Tunnels (Optional)
For secure external access and GitHub Actions webhook delivery, set up a tunnel. Syntropic137 includes built-in Cloudflare Tunnel support via a Docker Compose profile.
See the Tunnels guide for setup instructions.
Secrets
Infrastructure secrets (database, Redis, MinIO) are stored as chmod 600 files and mounted via Docker Compose's secrets mechanism, never baked into image layers. Application-level API keys (e.g. ANTHROPIC_API_KEY) are supplied via environment variables.
See Secrets Management for details on how secrets work and optional 1Password integration.
Syntropic137 Docs v0.33.1 · Last updated March 2026