Skip to Content

Production Guide: Deploy GlitchTip with Docker Compose + Caddy + PostgreSQL on Ubuntu

A production-ready blueprint for self-hosted error monitoring with TLS, persistence, backups, and operational validation.

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.com to 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 enable

If 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 Caddyfile

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

If 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: bridge

If 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 caddy

If 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 ps

If 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 caddy

If 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

Talk to us

If you want this deployed with enterprise hardening, managed backups, and ongoing support, our team can help.

Contact Us

Production Guide: Deploy Sentry with Docker Compose + Caddy + PostgreSQL on Ubuntu
A production-focused, operator-friendly Sentry deployment with TLS, persistence, backup discipline, and practical incident-response integration.