Skip to main content
Most users should use Sudoiq with an API key from Settings. You do not need Docker or self-hosting for that path. This page is for clients who must run the Agent Service API inside their own network (single-tenant VPC, air-gapped, or contractual data residency). You are responsible for infrastructure, security, and license compliance.

Reference compose file

Your Agent Service distribution ships a compose file for application containers only (often named docker-compose-no-storage.yml), colocated with startup/ and your team’s setup runbooks. That layout runs application containers only. It does not start Postgres or Redis; you must provide DB_HOST / DB_* and REDIS_URL (or equivalent) via .env.fastapi_main and run databases elsewhere (RDS, ElastiCache, self-managed, etc.), consistent with the comments at the top of the compose file.

Hardware

Plan for at least 4 GB RAM on the Docker host for a minimal dev-style stack that includes Traefik, two API replicas (api-blue / api-green), queue_runner, and webhooks. Production sizing depends on traffic, model latency, and worker concurrency—scale CPU/RAM accordingly. You must host Postgres and Redis yourself (the compose bundle does not start them). Postgres must be PostgreSQL 17–compatible (version and extensions aligned with the migrations and client libraries in your backend release). Redis must be reachable at whatever URL you set as REDIS_URL—typically the same class of network as the API (VPC, TLS to a managed cache, etc.). For both stores, prefer managed or otherwise operationally elastic services (scaling storage, instance size, replicas) so traffic spikes and growth do not leave you chasing capacity and throughput on a single undersized host.

Services in this stack

ServiceRole
traefikReverse proxy / router on host port 8001; connects to api-blue and api-green on the app network. Requires Traefik v3.6+ for modern Docker API compatibility (see file header comments).
api-blue / api-greenFastAPI app (uvicorn fastapi_main:app, port 8001 in-container). Blue/green traffic split via Traefik priorities. Mounts ./keys read-only.
queue_runnerBackground worker process (queue_runner.py) using the same image and env.
webhooksWebhook delivery worker (agent_event_webhooks.py).
psql-migrate (profile migrate)One-shot Postgres migration runner; not started by default.
Network: app (explicit name app). Project name api to avoid colliding with other stacks.

Configuration

  1. Generate env and keys (first-time or rotation). From the application repository root, run startup/generate_env.py so you get a keys/ directory and .env.fastapi_main with generated secrets and DB passwords (see the script’s docstring for OUTPUT_DIR / positional output path and --db-host overrides). Your internal README may wrap this step; run it before hand-editing compose-only hosts if you want a sane baseline.
  2. Place .env.fastapi_main and keys/ where your compose file expects them (usually the same working directory you start compose from).
  3. Set DB_*, REDIS_URL, JWT/signing variables, and any cloud credentials your backend expects—merging or overriding what generate_env produced if your databases live outside localhost.
  4. Start from that directory:
docker compose -f docker-compose-no-storage.yml up -d
  1. Point Python AsapiClient or the CLI config --host at your Traefik ingress (for example https://your-bastion-host:8001 or TLS-terminated URL).

Zero-downtime rotation (blue / green)

On a single host, docker-compose-no-storage.yml runs two API containers—api-blue and api-green—behind Traefik on port 8001. Each slot has a Traefik router priority; default traffic goes to whichever slot has the higher priority (compose defaults are roughly blue = 100, green = 1, so blue is live first). Use this pattern to rotate in a new build without blanking the only API instance:
  1. Build or pull the new image and point IMAGE_TAG (or your compose image override) at it—often while the current live slot keeps serving traffic.
  2. Recreate the standby slot with the new image (for example docker compose ... up -d api-green after updating the green service) so it passes its health check before you cut over.
  3. Smoke-test the standby from the host or bastion. The compose layout supports a pinned route via header X-Hit-Service: blue or X-Hit-Service: green so you can hit /health (or a narrow API check) against one slot only. Treat that header as diagnostic tooling, not a security boundary—it is trivially spoofable, so rely on it only from localhost or a trusted admin path.
  4. Swap priorities by setting API_BLUE_PRIORITY and API_GREEN_PRIORITY so the slot you just validated becomes the winner. These variables are read from a .env next to the compose file (Compose label interpolation), not from .env.fastapi_main—see the comments in your shipped compose file for the exact mechanism.
  5. Apply the change: docker compose -f docker-compose-no-storage.yml up -d api-blue api-green (and restart queue_runner / webhooks when they share the same image tag, so workers match the API version you intend to run).
Repeat the same steps on the next release, alternating which slot is “new” and which is “stable.” For database schema changes, run your migration step (for example the migrate profile in the compose file) before shifting default traffic to a code version that depends on the new schema.

Tenant bootstrap (create_tenant)

After the database exists and migrations have been applied, create an initial tenant and user with the repo script (run from the application project root with .env.fastapi_main loaded):
python -m startup.create_tenant --tenant-name "My Company" --username admin --email admin@example.com
Or tenant-only:
python -m startup.create_tenant --tenant-name "My Company" --no-user
See startup/create_tenant.py in the monorepo for API key issuance, service account registration, and other flags (--get-api-key, --register-service-account, etc.). Your internal DEVELOPER-README / runbooks describe the full first-time setup flow.

Licensing and resale

Resale / hosted service: Operating this software as a multi-tenant commercial substitute for Sudoiq—or reselling managed access without the right agreements—violates the suqoid agent-service open-source license. Do not assume this documentation grants redistribution rights. Enterprise deployments are typically single-customer dedicated environments.