Edictum
Edictum Console

Self-Hosting Guide

Deploy Edictum Console on your own infrastructure. One Docker image, five minutes to production.

AI Assistance

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/setup

Build 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 .env

2. 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 -d

This starts:

ServiceImagePortHealth Check
postgrespostgres:165432 (internal)pg_isready every 10s
redisredis:7-alpine6379 (internal)redis-cli ping every 10s
serverBuilt from Dockerfile8000 (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-here

Both 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.com

This enables:

  • Secure cookies -- Secure flag 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.sql

Restore:

cat backup.sql | docker compose exec -T postgres psql -U postgres edictum

Monitoring

Poll the health endpoint:

curl -s https://edictum.example.com/api/v1/health | jq .status

Returns "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

VariableRequiredDefaultPurpose
POSTGRES_PASSWORDYes--Postgres container password
EDICTUM_SECRET_KEYYes--Session token signing
EDICTUM_SIGNING_KEY_SECRETRecommended--Ed25519 key encryption. Required for bundle signing and notification encryption
EDICTUM_ADMIN_EMAILFirst run--Bootstrap admin email
EDICTUM_ADMIN_PASSWORDFirst run--Bootstrap admin password (min 12 chars)
EDICTUM_BASE_URLNohttp://localhost:8000Public URL
EDICTUM_AUTH_PROVIDERNolocalAuth provider
EDICTUM_SESSION_TTL_HOURSNo24Session cookie lifetime
EDICTUM_ENV_NAMENodevelopmentproduction disables OpenAPI docs
EDICTUM_CORS_ORIGINSNohttp://localhost:8000,http://localhost:3000Comma-separated allowed origins
EDICTUM_RATE_LIMIT_MAX_ATTEMPTSNo10Login rate limit
EDICTUM_RATE_LIMIT_WINDOW_SECONDSNo300Rate 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 -d

The server applies any new database migrations during startup. No manual migration steps needed.

Troubleshooting

ProblemFix
Server won't startCheck docker compose logs server -- missing required env vars show clear error messages
Database connection refusedEnsure postgres is healthy: docker compose ps should show healthy
Session cookies not persistingSet EDICTUM_BASE_URL to your actual URL (HTTPS in production)
Interactive notification buttons don't workEDICTUM_BASE_URL must be a public HTTPS URL
EDICTUM_SECRET_KEY error on startupThis is required -- generate with python -c "import secrets; print(secrets.token_hex(32))"

Next Steps

Last updated on

On this page