Production Deployment¶
Step-by-step guide to deploying SerOps on your own infrastructure
using the deploy/quick-start/
distribution at the repository root.
The quick-start Compose stack is the canonical self-host path. It bundles everything SerOps itself needs (Postgres 16, Redis 7, backend, frontend, Caddy for TLS + ingress) and connects out to whatever observability tools you already run.
Prerequisites¶
| Requirement | Minimum | Recommended |
|---|---|---|
| Docker | 20.10+ | 24+ |
| Docker Compose | v2 (built-in) | v2 |
| CPU | 2 cores | 4+ cores |
| RAM | 4 GB | 8+ GB |
| Disk | 20 GB | 50+ GB |
| OS | Linux (any), macOS | Ubuntu 22.04+, RHEL 9+ |
| Public hostname | Optional (Caddy falls back to localhost) |
Recommended for HTTPS |
Not bundled — connect these separately
SerOps is the AI/analysis layer on top of the observability stack you already run. Elasticsearch / Loki / Datadog / Prometheus / CloudWatch / VictoriaMetrics / Mimir / OpenSearch are not shipped in this Compose file — you configure them from Integrations after the app is up.
Step 1: Clone the backend repo¶
Everything you need for a self-host deploy lives in this directory.
Step 2: Configure¶
Edit .env — at minimum:
# Security — REQUIRED
DJANGO_SECRET_KEY=<generate one: python3 -c 'import secrets; print(secrets.token_urlsafe(50))'>
# Database (Postgres bundled — set your own password)
POSTGRES_PASSWORD=<pick something long>
REDIS_PASSWORD=<pick something long>
# Hostname Caddy serves under — leave 'localhost' for local eval
SEROPS_DOMAIN=serops.yourdomain.com
# Add at least one AI provider configuration via the wizard after
# first login. AI keys are stored per-organisation via the UI, not
# via env vars (since the 2026-08-30 rework).
See the Configuration Reference for every supported variable.
Step 3: Bring the stack up¶
Compose starts these containers, in dependency order:
| Container | Role | Internal port |
|---|---|---|
serops-postgres |
Postgres 16 (app DB) | 5432 |
serops-redis |
Redis 7 (Celery broker + cache) | 6379 |
serops-backend |
Django API + Celery worker + Beat | 8000 |
serops-frontend |
React SPA (Nginx-served bundle) | 3000 |
serops-caddy |
HTTPS reverse proxy | 80 / 443 |
Only ports 80 and 443 are exposed to the host. Postgres and Redis are reachable only from inside the compose network.
Migrations run automatically on backend startup via start.sh — no
manual manage.py migrate needed.
Step 4: Create the first admin¶
Alternatively, open the web UI at your SEROPS_DOMAIN and use the
registration page — the first user through gets admin.
Step 5: Complete the onboarding wizard¶
Open your browser at your SEROPS_DOMAIN and log in. The onboarding
wizard walks through:
- Pick an AI provider (Gemini / Claude / OpenAI / DeepSeek / Ollama) and add your API key. Key is encrypted at rest with Fernet.
- Connect an observability source (log store).
- Create your first project + service.
- First AI analysis fires automatically once a service is linked.
The wizard uses computed state — no "Mark Complete" clicks needed; it detects each step as you finish it.
Step 6: Verify¶
curl https://serops.yourdomain.com/api/health/ # → 200 OK
docker compose ps # all healthy
docker compose logs -f backend # tail app logs
Post-deployment¶
TLS¶
Caddy handles HTTPS automatically via Let's Encrypt when
SEROPS_DOMAIN is a publicly-reachable hostname. No cert files to
manage.
If you're behind a corporate load balancer or want to terminate TLS elsewhere:
- Remove the
caddyservice fromdocker-compose.yml. - Point your ingress at
frontend:3000andbackend:8000(routing/api/*and/images/*to backend, everything else to frontend — seeconfig/caddy/Caddyfilefor the exact rules). - Set
SECURE_SSL_REDIRECT=Truein.env.
Database backups¶
The quick-start ships a rotation script — see the dedicated
Backup & Restore guide for the full procedure
(retention policy, off-site copies, encryption, disaster-recovery
drills, Kubernetes patterns, restoring the .env secrets alongside
the dump).
Quick reference:
# Back up (writes ./backups/serops_db_YYYYMMDD_HHMMSS.dump, keeps last 7)
./scripts/backup.sh
# Cron for nightly at 2am
0 2 * * * cd /path/to/quick-start && ./scripts/backup.sh
# Restore
docker exec -i serops-postgres pg_restore \
-U serops -d serops --clean --if-exists \
< backups/serops_db_YYYYMMDD_HHMMSS.dump
Use an external Postgres¶
If you already run Postgres and want SerOps to use it instead of the bundled container:
- Comment out the
postgresservice block indocker-compose.yml. - In
.env, setDB_HOSTto your Postgres hostname andDB_PORTif it's non-standard. docker compose up -d.
Monitoring the deployment¶
Docker's built-in healthchecks + docker compose ps covers most of
what you need day-to-day:
- Container status:
docker compose ps - Application logs:
docker compose logs -f backend - Celery worker output:
docker compose logs -f backend | grep -i celery - DB connection count:
docker exec serops-postgres psql -U serops -d serops -c 'SELECT count(*) FROM pg_stat_activity;'
For long-term observability, feed the SerOps container logs into the same log store you connected to SerOps itself — you get eating-your-own-dogfood for free.
Connect your observability + SCM providers¶
Once the app is up:
- Integrations → add your log provider (Elasticsearch, Loki, Datadog, CloudWatch, etc.).
- Add your SCM provider (GitHub, GitLab, Bitbucket, Azure DevOps) so AI analysis can reference recent commits.
- Add your CI/CD provider (Jenkins, GitHub Actions, GitLab CI, etc.) so AI can correlate incidents with deploys.
See the Integrations Overview for provider-specific setup.
Next¶
- Configuration Reference — every environment variable
- Upgrading — how to move between versions
- Team Management — invite the rest of your team