When engineering teams scale quickly, error visibility often fragments across logs, chat alerts, and issue trackers. This guide shows how to deploy GlitchTip in production with Docker Compose and Caddy so you get centralized exception monitoring, release tracking, and alerting without sending sensitive telemetry to third-party SaaS by default.
The target outcome is practical: secure HTTPS access, persistent Postgres/Redis-backed state, disciplined secrets handling, recovery-focused backups, and a clean upgrade workflow. Instead of a throwaway demo, this implementation is optimized for real operations where uptime and traceability matter.
GlitchTip gives Sentry-compatible ingestion and straightforward self-hosting economics. With the deployment pattern below, you can roll out observability for apps and APIs while preserving operational control. That matters for organizations with privacy requirements, regulated environments, or a simple preference for owning core telemetry infrastructure.
Beyond deployment, this guide emphasizes operational habits: staged rollout, baseline runbooks, and routine validation. The value of error monitoring comes from consistent response loops, not just collecting events. By pairing GlitchTip with practical process controls, teams can reduce MTTR and improve release confidence over time.
Architecture/flow overview
Caddy terminates TLS at the edge and proxies inbound traffic to GlitchTip over an internal Docker network. PostgreSQL stores events, users, and metadata while Redis handles queues/caching. None of the data stores are publicly exposed.
This design keeps external attack surface narrow and simplifies compliance discussions because telemetry remains under your infrastructure boundary. It also improves reliability through explicit restart policies and persistent volume mounts for stateful services.
Operational flow: app errors -> GlitchTip ingestion endpoint -> background processing -> searchable issue stream and notifications. Teams can correlate release versions with exception spikes and prioritize fixes faster.
From a platform perspective, separation of concerns helps day-2 operations: Caddy handles certificates and edge concerns; GlitchTip handles application-level processing; PostgreSQL and Redis carry data/state responsibilities. This separation makes troubleshooting more deterministic and limits blast radius during incidents.
Prerequisites
- Ubuntu 22.04+/Debian 12+ server (4 vCPU, 8 GB RAM, SSD)
- DNS record such as
errors.yourdomain.comto server IP - Docker Engine + Compose plugin installed
- Firewall allowing ports 80/443
- Non-root sudo user with SSH key authentication
For production, keep base OS patched and protect SSH with strict access controls. Pair provider snapshots with application-level backups. If your team has compliance requirements, define retention and access policies before onboarding all applications.
Also decide ownership early: who rotates secrets, who reviews alerts, and who approves upgrades. Clarifying these roles prevents operational drift once the platform becomes business-critical.
Step-by-step deployment
1) Prepare host
sudo apt update && sudo apt -y upgrade
sudo apt -y install ca-certificates curl gnupg ufw docker.io docker-compose-plugin
sudo systemctl enable --now docker
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enableIf the copy button does not work in your browser, manually copy the block.
2) Create working directories
mkdir -p ~/glitchtip/{caddy,postgres,redis,backups}
cd ~/glitchtip
touch .env docker-compose.yml CaddyfileIf the copy button does not work in your browser, manually copy the block.
3) Configure environment
cat > .env << 'EOF'
GLITCHTIP_DOMAIN=errors.yourdomain.com
SECRET_KEY=replace_with_long_random_value
POSTGRES_DB=glitchtip
POSTGRES_USER=glitchtip
POSTGRES_PASSWORD=replace_with_strong_password
TZ=America/Chicago
EOF
chmod 600 .envIf the copy button does not work in your browser, manually copy the block.
4) Write Docker Compose stack
version: "3.9"
services:
postgres:
image: postgres:15
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- ./postgres:/var/lib/postgresql/data
networks: [gt_net]
redis:
image: redis:7
restart: unless-stopped
command: ["redis-server", "--appendonly", "yes"]
volumes:
- ./redis:/data
networks: [gt_net]
glitchtip:
image: glitchtip/glitchtip:latest
restart: unless-stopped
depends_on: [postgres, redis]
environment:
DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
REDIS_URL: redis://redis:6379/0
SECRET_KEY: ${SECRET_KEY}
PORT: 8000
EMAIL_URL: smtp://user:[email protected]:587
GLITCHTIP_DOMAIN: ${GLITCHTIP_DOMAIN}
networks: [gt_net]
caddy:
image: caddy:2
restart: unless-stopped
ports: ["80:80","443:443"]
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy:/data
depends_on: [glitchtip]
networks: [gt_net]
networks:
gt_net:
driver: bridgeIf the copy button does not work in your browser, manually copy the block.
5) Configure Caddy reverse proxy
errors.yourdomain.com {
encode gzip
reverse_proxy glitchtip:8000
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "strict-origin-when-cross-origin"
}
}If the copy button does not work in your browser, manually copy the block.
6) Start and check services
cd ~/glitchtip
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=200 glitchtip caddyIf the copy button does not work in your browser, manually copy the block.
7) Create backup job
cat > ~/glitchtip/backups/backup.sh << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
cd ~/glitchtip
ts=$(date +%F-%H%M)
docker compose exec -T postgres pg_dump -U ${POSTGRES_USER} ${POSTGRES_DB} | gzip > backups/db-$ts.sql.gz
tar -czf backups/redis-$ts.tar.gz redis
EOF
chmod +x ~/glitchtip/backups/backup.sh
(crontab -l 2>/dev/null; echo "20 2 * * * ~/glitchtip/backups/backup.sh") | crontab -If the copy button does not work in your browser, manually copy the block.
Expect a short warm-up on first boot while migrations and app initialization complete. Do not proceed to application onboarding until health checks and login tests pass.
Configuration/secrets handling
Never commit .env to source control. Rotate DB credentials and GlitchTip secret key according to policy. Restrict shell and file access to operators with explicit ownership. If possible, generate runtime secrets from a central vault and track config changes through your change-management process.
For notification reliability, configure SMTP and test delivery before broad rollout. Alert channels are only useful if routing works during incidents. Keep sender domains aligned with your mail policy to reduce delivery failures and spam filtering.
As your platform matures, document a quarterly control review: credential rotation status, backup restore evidence, user-access review, and upgrade history. This gives leadership and security teams clear evidence that observability infrastructure is maintained intentionally rather than ad hoc.
docker compose exec postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} -c '\dt'
docker compose logs --tail=200 glitchtip
sudo journalctl -u docker --since "1 hour ago"If the copy button does not work in your browser, manually copy the block.
Verification
Validate both user workflow and operational durability before onboarding teams.
- HTTPS certificate valid and auto-renewing
- Admin account can log in and create a project
- Test exception appears in dashboard
- Email/notification channel is functional
- Backups are generated and restore-tested
- Service restarts recover cleanly
Verification should be repeatable. Save these checks as a runbook and execute them after every upgrade or major configuration change.
curl -I https://errors.yourdomain.com
docker compose exec postgres pg_isready -U ${POSTGRES_USER}
docker compose restart && sleep 10 && docker compose psIf the copy button does not work in your browser, manually copy the block.
Common issues/fixes
TLS certificate is not issued
Check DNS target and ensure ports 80/443 are reachable from the internet.
dig +short errors.yourdomain.com
sudo ufw status
docker compose logs --tail=120 caddyIf the copy button does not work in your browser, manually copy the block.
Events are not ingesting
Verify DSN configuration in your applications and confirm the GlitchTip container is healthy. Start by generating a known test exception to validate end-to-end flow.
Database connection errors
Re-check DATABASE_URL credentials and connectivity between containers.
Notification emails missing
Validate SMTP credentials, sender domain policy, and outbound connectivity.
Queue/backlog growth
Inspect Redis health and app worker throughput; increase resources if sustained load increases. Add alert thresholds so backlog growth is detected before it impacts triage responsiveness.
FAQ
Can GlitchTip replace hosted Sentry for many teams?
Yes, especially where data residency and cost control are priorities.
Is Docker Compose enough for production?
For small and mid-size environments, yes, with proper backup, monitoring, and upgrade discipline.
How do we onboard applications safely?
Start with low-risk services, validate event flow, then expand gradually.
How often should we test restore?
At least monthly in staging, with documented recovery steps.
Can we add SSO later?
Yes, but plan identity integration and role mapping up front.
What metrics indicate platform health?
Ingestion rate, queue latency, error backlog trend, and notification success rate.
Operational hardening checklist
Before marking the platform as fully production-ready, complete a hardening pass. Restrict inbound ports to minimum required, ensure only trusted operators have shell access, and confirm local firewall plus cloud security-group rules align. Disable unused host services and monitor for drift through periodic baseline checks.
Define an incident process for observability tooling itself. If GlitchTip fails, teams lose crucial signal during outages. Maintain simple break-glass procedures, including service restart commands, known-good compose revision references, and clear ownership for incident commander and communications.
Finally, implement lifecycle governance: planned maintenance windows, test-before-upgrade policy, and post-change verification evidence. These controls prevent avoidable regressions and keep confidence high as more teams rely on the system.
Related guides
- Production Guide: Deploy Sentry with Docker Compose + Caddy + PostgreSQL on Ubuntu
- Production Guide: Deploy Plane with Docker Compose + Nginx + PostgreSQL on Ubuntu
- Production Guide: Deploy MinIO with Docker Compose + Nginx on Ubuntu
Talk to us
If you want this deployed with enterprise hardening, managed backups, and ongoing support, our team can help.