Self-Hosting Guide
Deploy Edictum Console on your own infrastructure. One Docker image, five minutes to production.
Right page if: you are deploying Edictum Console on your own infrastructure with Docker Compose and need the production checklist (HTTPS, backups, monitoring, key rotation). Wrong page if: you want a first-time quickstart (see https://docs.edictum.ai/docs/console/setup) or Railway deployment (see https://docs.edictum.ai/docs/console/deploy-railway). Gotcha: `EDICTUM_BASE_URL` must be set to your public HTTPS URL -- without it, secure cookies, CORS, and interactive notification buttons all break.
Deploy Edictum Console on your own infrastructure. One Docker image, five minutes to production.
Quick Start (Published Image)
No need to clone or build -- pull the published image and run:
# 1. Download the compose file
curl -fsSL https://raw.githubusercontent.com/edictum-ai/edictum-console/master/deploy/docker-compose.yml -o docker-compose.yml
# 2. Create .env with your secrets
cat <<EOF > .env
POSTGRES_PASSWORD=$(python3 -c "import secrets; print(secrets.token_hex(16))")
EDICTUM_SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
EDICTUM_SIGNING_KEY_SECRET=$(python3 -c "import secrets; print(secrets.token_hex(32))")
EOF
# 3. Start everything
docker compose up -d
# 4. Open http://localhost:8000/dashboard/setupBuild from Source
If you prefer to build the image yourself from source.
Docker Compose
Three services: Postgres 16, Redis 7, and the Edictum server.
1. Clone and Configure
git clone https://github.com/edictum-ai/edictum-console.git
cd edictum-console
cp .env.example .env2. Generate Secrets
Every deployment needs three secrets. Generate each one:
python -c "import secrets; print(secrets.token_hex(32))"Add them to your .env:
# Session token signing (required)
EDICTUM_SECRET_KEY=<paste-hex-here>
# Ed25519 key encryption for bundle signing (required for deploys)
EDICTUM_SIGNING_KEY_SECRET=<paste-hex-here>
# Postgres password (required)
POSTGRES_PASSWORD=<paste-hex-here>3. Start Everything
docker compose up -dThis starts:
| Service | Image | Port | Health Check |
|---|---|---|---|
postgres | postgres:16 | 5432 (internal) | pg_isready every 10s |
redis | redis:7-alpine | 6379 (internal) | redis-cli ping every 10s |
server | Built from Dockerfile | 8000 (exposed) | Waits for postgres + redis |
The server waits for both dependencies to be healthy before starting. Postgres data persists in a pgdata volume.
4. Bootstrap Admin
Option A: Setup Wizard (recommended)
Open http://localhost:8000/dashboard/setup and create your admin account. Password must be at least 12 characters.
Option B: Environment Variables
Add to .env before first start:
EDICTUM_ADMIN_EMAIL=admin@example.com
EDICTUM_ADMIN_PASSWORD=your-secure-password-hereBoth options are protected by bootstrap lock -- admin creation only works when zero users exist.
5. Verify
curl http://localhost:8000/api/v1/health{
"status": "ok",
"bootstrap_complete": true
}Railway
Railway deploys directly from your GitHub repo using the included railway.toml and Dockerfile. Add Postgres and Redis plugins, set your secrets, and Railway handles the rest -- the docker-entrypoint.sh automatically maps Railway's auto-injected DATABASE_URL and REDIS_URL to the format the server expects.
See the full walkthrough: Deploy to Railway
Production Checklist
HTTPS
Set EDICTUM_BASE_URL to your public HTTPS domain:
EDICTUM_BASE_URL=https://edictum.example.comThis enables:
- Secure cookies --
Secureflag auto-set on session cookies - Interactive notifications -- Telegram, Slack, and Discord button callbacks require HTTPS
- CORS -- origin matching uses this URL
Reverse Proxy Configuration
The login endpoint requires a detectable client IP to enforce rate limiting. If you run the console behind a reverse proxy (nginx, Caddy, Traefik, etc.), you must forward the client IP and enable Uvicorn's proxy headers support.
Forward the client IP from your proxy:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;Enable ProxyHeadersMiddleware via the environment:
FORWARDED_ALLOW_IPS=*Without this, the login endpoint returns 400 Cannot determine client address and all login attempts fail.
Set FORWARDED_ALLOW_IPS carefully. Using * trusts all upstream X-Forwarded-For headers. In production, restrict this to your proxy's IP address to prevent IP spoofing in rate-limit bypass attempts.
Set EDICTUM_TRUSTED_PROXIES when EDICTUM_BASE_URL is HTTPS. If you run the console behind a reverse proxy (nginx, Caddy, Traefik, or a cloud load balancer) and omit this variable, the server logs a startup warning. Without it, trailing-slash redirects may downgrade from HTTPS to HTTP and rate limiting will key on the proxy IP instead of the real client IP. Set it to the proxy CIDR range (e.g., 10.0.0.0/8) or * if the proxy IP is dynamic. See Environment Variables for details.
Backups
Back up Postgres regularly:
docker compose exec postgres pg_dump -U postgres edictum > backup.sqlRestore:
cat backup.sql | docker compose exec -T postgres psql -U postgres edictumMonitoring
Poll the health endpoint:
curl -s https://edictum.example.com/api/v1/health | jq .statusReturns "ok" when Postgres and Redis are reachable, "degraded" otherwise. Monitor this with your existing alerting (Uptime Kuma, Datadog, etc.).
Signing Key Rotation
Rotate the Ed25519 signing key periodically from Dashboard > Settings > Danger Zone > Rotate Signing Key. This generates a new keypair and re-signs all currently deployed bundles. Connected agents receive the updated bundles automatically via SSE.
Environment Variables Reference
| Variable | Required | Default | Purpose |
|---|---|---|---|
POSTGRES_PASSWORD | Yes | -- | Postgres container password |
EDICTUM_SECRET_KEY | Yes | -- | Session token signing |
EDICTUM_SIGNING_KEY_SECRET | Recommended | -- | Ed25519 key encryption. Required for bundle signing and notification encryption |
EDICTUM_ADMIN_EMAIL | First run | -- | Bootstrap admin email |
EDICTUM_ADMIN_PASSWORD | First run | -- | Bootstrap admin password (min 12 chars) |
EDICTUM_BASE_URL | No | http://localhost:8000 | Public URL |
EDICTUM_AUTH_PROVIDER | No | local | Auth provider |
EDICTUM_SESSION_TTL_HOURS | No | 24 | Session cookie lifetime |
EDICTUM_ENV_NAME | No | development | production disables OpenAPI docs |
EDICTUM_CORS_ORIGINS | No | http://localhost:8000,http://localhost:3000 | Comma-separated allowed origins |
EDICTUM_RATE_LIMIT_MAX_ATTEMPTS | No | 10 | Login rate limit |
EDICTUM_RATE_LIMIT_WINDOW_SECONDS | No | 300 | Rate limit window |
Upgrading
Alembic migrations run automatically on startup (the Dockerfile CMD runs alembic upgrade head before starting the server). To upgrade:
git pull
docker compose build
docker compose up -dThe server applies any new database migrations during startup. No manual migration steps needed.
Troubleshooting
| Problem | Fix |
|---|---|
| Server won't start | Check docker compose logs server -- missing required env vars show clear error messages |
| Database connection refused | Ensure postgres is healthy: docker compose ps should show healthy |
| Session cookies not persisting | Set EDICTUM_BASE_URL to your actual URL (HTTPS in production) |
| Interactive notification buttons don't work | EDICTUM_BASE_URL must be a public HTTPS URL |
EDICTUM_SECRET_KEY error on startup | This is required -- generate with python -c "import secrets; print(secrets.token_hex(32))" |
Next Steps
- Connecting Agents -- install the SDK and connect your first agent
- Notification Channels -- set up Telegram, Slack, Discord, or email alerts
Last updated on
AI Contract Assistant
A streaming chat assistant that helps you write edictum contracts. Knows the full contract schema and answers questions about selectors, operators, and effects.
Production Checklist
Pre-flight verification for deploying Edictum Console to production. Covers secrets, HTTPS, database, monitoring, and security hardening.