Self-hosted deployment¶
antcrew-platform ships as a single Docker image. For teams of 1–15 running on one machine, no external services are required — it uses SQLite and an in-process task queue out of the box.
Single-container quickstart¶
docker run -d \
--name antcrew \
-p 8000:8000 \
-v ./data:/data \
-e ANTHROPIC_API_KEY=sk-ant-... \
ghcr.io/antcrew/platform:latest
On first start, the platform:
- Creates a SQLite database at
/data/platform.db - Generates a
PLATFORM_API_KEYand saves it to/data/.credentials - Logs the key prominently in the startup output
Retrieve it with:
docker logs antcrew 2>&1 | grep "PLATFORM_API_KEY="
# or read the file directly:
cat ./data/.credentials
Pass the key in the X-Api-Key header for all API calls.
What runs inside one container¶
| Component | Mode |
|---|---|
| API + dashboard | FastAPI / uvicorn, single worker |
| Database | SQLite at /data/platform.db |
| Pipeline execution | In-process ThreadPoolExecutor (4 workers by default) |
| Scheduler | asyncio background loop — eval and run schedules fire every 60 s |
| SSE fan-out | In-process; all clients served by the same worker |
| Rate limiting | Per-process in-memory sliding window |
Everything that would otherwise require Redis or Celery degrades gracefully to an in-process equivalent. No data is lost; the only trade-off is that a worker restart interrupts in-flight runs (they are marked interrupted on the next start).
Environment variables¶
Required:
| Variable | Description |
|---|---|
ANTHROPIC_API_KEY |
Default provider key used when no workspace BYOK key is set |
Optional but recommended for any persistent deployment:
| Variable | Default | Description |
|---|---|---|
DATA_DIR |
/data |
Directory for SQLite database and generated credentials |
DATABASE_URL |
sqlite+aiosqlite:////$DATA_DIR/platform.db |
Override to use PostgreSQL |
ANTCREW_ENCRYPTION_KEY |
(unset) | AES-GCM-256 key for encrypting run state at rest. Generate: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" |
BYOK_ENCRYPTION_KEY |
(unset) | Encrypts BYOK provider keys at rest. Same generation command. |
CORS_ORIGINS |
http://localhost:8000 |
Comma-separated list of allowed browser origins |
ANTCREW_WORKERS |
4 |
Thread-pool workers for in-process pipeline execution |
Persisting data¶
Mount /data as a Docker volume to survive container restarts:
# Named volume (Docker manages it)
docker run -v antcrew-data:/data ...
# Bind mount (you control the path)
docker run -v ./data:/data ...
The SQLite database and the auto-generated PLATFORM_API_KEY both live in this directory.
Upgrading¶
docker pull ghcr.io/antcrew/platform:latest
docker stop antcrew && docker rm antcrew
docker run -d --name antcrew -p 8000:8000 -v ./data:/data ... ghcr.io/antcrew/platform:latest
SQLite migrations run automatically on startup — no manual steps required.
Scaling up¶
When you need multi-replica deployments, add PostgreSQL, Redis, and Celery. The same image supports all modes — they activate via environment variables:
docker run \
--build-arg APP_ENV=prod \
-e DATABASE_URL=postgresql+asyncpg://user:pass@db/antcrew \
-e REDIS_URL=redis://redis:6379/0 \
-e REDIS_PUBSUB_URL=redis://redis:6379/0 \
-e CELERY_BROKER_URL=redis://redis:6379/1 \
-e ANTCREW_ENCRYPTION_KEY=... \
-e BYOK_ENCRYPTION_KEY=... \
...
See deployment.md for the full compose setup and Hetzner Terraform config.
Limitations of the single-container mode¶
- Single-writer: SQLite locks on writes. Under high concurrency (>10 simultaneous pipeline runs), you may see contention. Switch to PostgreSQL when that happens.
- No worker recovery: If the container restarts mid-run, the run is marked
interrupted. Celery workers retry on failure; the in-process executor does not. - Rate limiting is per-process: If you run multiple uvicorn workers (
--workers N), effective rate limit isRATE_LIMIT_RPM × N. SetREDIS_URLto share a single limiter. - SSE is in-process: Live dashboard works fine with one container. With multiple replicas behind a load balancer, a client on worker A may miss events from a run executing on worker B unless
REDIS_PUBSUB_URLis set.