Skip to content

Docker Compose Deployment

git clone https://github.com/leaflockhq/leaflock.git
cd leaflock
cp .env.example .env
# Fill JWT_SECRET, SERVER_ENCRYPTION_KEY, POSTGRES_PASSWORD, REDIS_PASSWORD
docker compose up -d
docker compose logs -f backend

frontend

Caddy 2.8 serves frontend/dist, performs API proxying, and exposes health at /health.

backend

Go Fiber API with health endpoints at /api/v1/health/* and optional Prometheus metrics when ENABLE_METRICS=true.

postgres

PostgreSQL 15 with pg_isready health checks; data stored in the postgres_data volume.

redis

Redis 7 configured with password auth and persistence inside the redis_data volume.

services:
backend:
build: ./backend
environment:
DATABASE_URL: postgres://postgres:{POSTGRES_PASSWORD}@postgres:5432/notes?sslmode=disable
REDIS_URL: redis:6379
REDIS_PASSWORD: {REDIS_PASSWORD}
JWT_SECRET: {JWT_SECRET}
SERVER_ENCRYPTION_KEY: {SERVER_ENCRYPTION_KEY}
CORS_ORIGINS: {CORS_ORIGINS}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
frontend:
build: ./frontend
environment:
BACKEND_INTERNAL_URL: http://backend:8080

Why Caddy?

  • DNS lookups run on every request, preventing IPv6 caching issues (important on Railway and Fly.io).
  • Enforces security headers and compresses static assets out of the box.
  • /usr/local/bin/docker-entrypoint.sh (shipped with the repo) rewrites upstream addresses based on runtime environment variables.
curl http://localhost:3000/health # frontend container
curl http://localhost:8080/api/v1/health/live # backend liveness
curl http://localhost:8080/api/v1/health/ready # backend readiness (checks DB + Redis)
docker compose exec postgres pg_isready -U postgres # database health
docker compose exec redis redis-cli --auth "$REDIS_PASSWORD" ping

Ready checks map directly to Kubernetes and cloud load balancer probes—keep them healthy before putting LeafLock behind a reverse proxy.

  • Logs: docker compose logs -f backend frontend
  • Redeploy after config change: docker compose up -d --build
  • Exec shell: docker compose exec backend sh
  • Shutdown: docker compose down (append -v to drop volumes)

LeafLock bundles docker-compose.prod.yml for hardened deployments. Merge it with the base file:

docker compose \
-f docker-compose.yml \
-f docker-compose.prod.yml \
up -d

Key changes in the override:

  • Removes host port exposure for PostgreSQL and Redis (internal network only).
  • Forces APP_ENV=production and enables metrics on the backend.
  • Binds frontend to ports 80/443 and attaches an external network for your ingress layer.
  • Persists data under /var/lib/leaflock/* with root-owned directories.
  • Rotate JWT_SECRET, SERVER_ENCRYPTION_KEY, POSTGRES_PASSWORD, and REDIS_PASSWORD before first deploy.
  • Set ENABLE_DEFAULT_ADMIN=false or change the default admin password post-boot.
  • Configure trusted CORS origins (CORS_ORIGINS=https://app.leaflock.example).
  • Enforce TLS termination in front of the frontend container.
  • Schedule database and volume backups (see /operations/backups once created).
  • 502 Bad Gateway from frontend: ensure BACKEND_INTERNAL_URL=http://backend:8080 and the backend container is healthy.
  • Backend exits on startup: weak secrets cause config.LoadConfig() to log.Fatalf; check container logs.
  • Redis AUTH failures: confirm the password in .env matches the Compose command arguments.
  • Slow cold starts: the backend waits for database migrations and template seeding; monitor docker compose logs backend until readiness flips to ready.