Self-Hosting
Docker Setup
Run HelloJohn with Docker and Docker Compose on any server or local machine.
Docker Setup
The easiest way to self-host HelloJohn is with Docker Compose. A single file starts HelloJohn, PostgreSQL, and Redis together.
Prerequisites
- Docker Engine 24+ and Docker Compose v2
- A domain pointing to your server (for production)
Minimal docker-compose.yml
version: "3.9"
services:
hellojohn:
image: ghcr.io/hellojohn/hellojohn:latest
restart: unless-stopped
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://hellojohn:secret@postgres:5432/hellojohn
JWT_SECRET: change-me-to-a-random-32-char-string
APP_URL: http://localhost:3000
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: hellojohn
POSTGRES_PASSWORD: secret
POSTGRES_DB: hellojohn
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U hellojohn"]
interval: 5s
timeout: 5s
retries: 5
volumes:
postgres_data:Start it:
docker compose up -d
docker compose logs -f hellojohnHelloJohn runs migrations automatically on first start.
Production docker-compose.yml
For a production deployment add Redis and proper secrets management:
version: "3.9"
services:
hellojohn:
image: ghcr.io/hellojohn/hellojohn:latest
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000" # bind to localhost only — proxy in front
env_file: .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
postgres:
image: postgres:16-alpine
restart: unless-stopped
env_file: .env.postgres
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --save 60 1 --loglevel warning
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
volumes:
postgres_data:
redis_data:.env file:
DATABASE_URL=postgres://hellojohn:strongpassword@postgres:5432/hellojohn
REDIS_URL=redis://redis:6379
JWT_SECRET=<generate with: openssl rand -hex 32>
APP_URL=https://auth.yourdomain.com
SMTP_HOST=smtp.yourdomain.com
SMTP_PORT=587
SMTP_USER=noreply@yourdomain.com
SMTP_PASS=<smtp-password>
SMTP_FROM=HelloJohn <noreply@yourdomain.com>.env.postgres:
POSTGRES_USER=hellojohn
POSTGRES_PASSWORD=strongpassword
POSTGRES_DB=hellojohnUsing a managed database
If you use a managed PostgreSQL service (Supabase, Neon, RDS, etc.) remove the postgres service from docker-compose.yml and set DATABASE_URL directly:
DATABASE_URL=postgres://user:pass@db.projectref.supabase.co:5432/postgres?sslmode=requirePinning a version
Use a specific version tag instead of latest in production:
image: ghcr.io/hellojohn/hellojohn:1.2.3Available tags are listed at github.com/hellojohn/hellojohn/releases.
Common Docker commands
# View logs
docker compose logs -f hellojohn
# Restart after config change
docker compose up -d --force-recreate hellojohn
# Run a database migration manually
docker compose exec hellojohn hellojohn migrate
# Open a Postgres shell
docker compose exec postgres psql -U hellojohn
# Pull the latest image
docker compose pull
docker compose up -dImage variants
| Tag | Description |
|---|---|
latest | Latest stable release |
1.2.3 | Specific version (recommended for prod) |
edge | Built from main branch — unstable |
All images are multi-arch (linux/amd64, linux/arm64) and published to GitHub Container Registry.