Docker Compose
Docker Compose gives you a reproducible, version-controlled deployment with persistent storage volumes — your analytics data stays in a named Docker volume on your server, including the shared hitkeep.db and any tenant-local databases created under tenants/*/hitkeep.db.
HitKeep images are published to two registries on every release:
| Registry | Image |
|---|---|
| Docker Hub | pascalebeier/hitkeep |
| GitHub Container Registry | ghcr.io/pascalebeier/hitkeep |
Both registries carry identical, multi-platform images (linux/amd64, linux/arm64) with signed provenance attestations. Use whichever registry suits your network or pull-rate requirements.
Quick Start
Section titled “Quick Start”Create a compose.yml:
services: hitkeep: # Also available as: ghcr.io/pascalebeier/hitkeep:latest image: pascalebeier/hitkeep:latest container_name: hitkeep restart: unless-stopped ports: - "8080:8080" volumes: - hitkeep_data:/var/lib/hitkeep/data - hitkeep_archive:/var/lib/hitkeep/archive environment: - HITKEEP_JWT_SECRET=${HITKEEP_JWT_SECRET} command: - "-public-url=http://localhost:8080" - "-archive-path=/var/lib/hitkeep/archive"
volumes: hitkeep_data: {} hitkeep_archive: {}Create a .env file alongside it (add to .gitignore):
echo "HITKEEP_JWT_SECRET=$(openssl rand -hex 32)" > .envStart it:
docker compose up -ddocker compose logs -f hitkeepThe database file lives inside the hitkeep_data volume. Back it up by copying the file out of the volume or using docker cp.
Reverse Proxy Configurations
Section titled “Reverse Proxy Configurations”Run HitKeep behind a reverse proxy for production HTTPS. Configure Trusted Proxies so real client IPs are used for analytics and rate limiting.
caddy-docker-proxy handles automatic HTTPS (Let’s Encrypt) and generates Caddy config directly from Docker labels.
Best practice is to use a dedicated ingress network and trust only that network CIDR in HitKeep.
services:caddy: image: lucaslorentz/caddy-docker-proxy:2.9-alpine restart: unless-stopped ports: - "80:80" - "443:443" - "443:443/udp" environment: - CADDY_INGRESS_NETWORKS=caddy volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - caddy_data:/data - caddy_config:/config networks: - caddy labels: caddy.email: mail@example.com
hitkeep: image: pascalebeier/hitkeep:latest restart: unless-stopped networks: - caddy volumes: - hitkeep_data:/var/lib/hitkeep/data - hitkeep_archive:/var/lib/hitkeep/archive environment: - HITKEEP_JWT_SECRET=${HITKEEP_JWT_SECRET:?set in .env} - HITKEEP_TRUSTED_PROXIES=${HITKEEP_TRUSTED_PROXIES:?set to your caddy network CIDR} command: - "-public-url=https://analytics.example.com" - "-archive-path=/var/lib/hitkeep/archive" labels: caddy: analytics.example.com caddy.reverse_proxy: "{{upstreams 8080}}" caddy.encode: "zstd gzip"
volumes:caddy_data: {}caddy_config: {}hitkeep_data: {}hitkeep_archive: {}
networks:caddy: external: trueCreate a .env file:
{echo "HITKEEP_JWT_SECRET=$(openssl rand -hex 32)"echo "HITKEEP_TRUSTED_PROXIES=$(docker network inspect caddy --format '{{(index .IPAM.Config 0).Subnet}}')"} > .envHITKEEP_TRUSTED_PROXIES must be the CIDR of your reverse-proxy network (not 0.0.0.0/0).
For existing Traefik stacks, expose HitKeep with labels:
services:hitkeep: image: pascalebeier/hitkeep:latest restart: unless-stopped volumes: - hitkeep_data:/var/lib/hitkeep/data - hitkeep_archive:/var/lib/hitkeep/archive environment: - HITKEEP_JWT_SECRET=${HITKEEP_JWT_SECRET:?set in .env} - HITKEEP_TRUSTED_PROXIES=${HITKEEP_TRUSTED_PROXIES:?set to your traefik network CIDR} command: - "-public-url=https://analytics.example.com" - "-archive-path=/var/lib/hitkeep/archive" labels: - "traefik.enable=true" - "traefik.http.routers.hitkeep.rule=Host(`analytics.example.com`)" - "traefik.http.routers.hitkeep.entrypoints=websecure" - "traefik.http.routers.hitkeep.tls.certresolver=myresolver" - "traefik.http.services.hitkeep.loadbalancer.server.port=8080"
volumes:hitkeep_data: {}hitkeep_archive: {}Related
Section titled “Related”Need managed hosting without giving up data sovereignty? HitKeep Cloud → runs the same Docker image in your chosen sovereign region — EU (Frankfurt, GDPR) or US (Virginia).