Self-Hosting

Run EarlyPass on your own infrastructure. Your waitlist data stays on your servers.

Don't want to self-host? Use the hosted version at api.earlypass.app — free to get started, no setup required.

Docker Compose (recommended)

The fastest way to self-host EarlyPass. You need Docker and Docker Compose installed.

1. Create a docker-compose.yml

version: "3.9"

services:
  earlypass:
    image: ghcr.io/earlypass/earlypass:latest
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://earlypass:earlypass@postgres:5432/earlypass?sslmode=disable
      REDIS_URL: redis://redis:6379
      BASE_URL: https://earlypass.example.com
      DASHBOARD_JWT_SECRET: ${DASHBOARD_JWT_SECRET}
      EMAIL_FROM: [email protected]
      RESEND_API_KEY: ${RESEND_API_KEY:-}
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: earlypass
      POSTGRES_PASSWORD: earlypass
      POSTGRES_DB: earlypass
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U earlypass"]
      interval: 5s
      timeout: 3s
      retries: 5
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    volumes:
      - redisdata:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
    restart: unless-stopped

volumes:
  pgdata:
  redisdata:

2. Generate a JWT secret

export DASHBOARD_JWT_SECRET=$(openssl rand -hex 32)

Without this, dashboard sessions won't survive container restarts.

3. Start the stack

docker compose up -d

Database migrations run automatically on startup. After a few seconds, your instance is ready:

curl http://localhost:3000/healthz
# → {"status":"ok","components":{"database":"ok","redis":"ok"}}

4. Access the dashboard

Open http://localhost:3000/dashboard/login in your browser. Create an account with a magic link (requires email configuration — see below).

Updating

docker compose pull
docker compose up -d

Single container

If you already have PostgreSQL and Redis running, you can run EarlyPass as a single container:

docker run -d \
  -p 3000:3000 \
  -e DATABASE_URL="postgres://user:pass@host:5432/earlypass" \
  -e REDIS_URL="redis://host:6379" \
  -e DASHBOARD_JWT_SECRET="$(openssl rand -hex 32)" \
  -e BASE_URL="https://earlypass.example.com" \
  ghcr.io/earlypass/earlypass:latest

Kubernetes (Helm)

For teams already running Kubernetes, EarlyPass provides an OCI Helm chart.

Install

helm install earlypass \
  oci://ghcr.io/earlypass/charts/earlypass \
  --set secrets.databaseUrl="postgres://user:pass@host:5432/earlypass" \
  --set secrets.dashboardJwtSecret="$(openssl rand -hex 32)"

This assumes you bring your own PostgreSQL and Redis. The chart does not deploy databases.

Key values

ValueDescription
secrets.databaseUrlPostgreSQL connection string (required)
secrets.dashboardJwtSecretHMAC key for dashboard auth (required)
secrets.redisUrlRedis connection string
secrets.resendApiKeyResend API key for transactional email
config.baseUrlPublic URL of your instance
config.emailFromFrom address for emails
ingress.enabledEnable Ingress resource (default: false)
autoscaling.enabledEnable HPA (default: false, 2-10 replicas)

See the full chart source for all available values.

Configuration

All configuration is via environment variables. The same variables work for both Docker and Kubernetes deployments.

VariableRequiredDescription
DATABASE_URLyesPostgreSQL connection string
REDIS_URLyesRedis connection string
BASE_URLrecommendedPublic URL of your instance (e.g. https://earlypass.example.com). Used to build links in emails. No trailing slash.
DASHBOARD_JWT_SECRETrecommendedHMAC-SHA256 key for dashboard auth cookies. Generate with openssl rand -hex 32. If unset, a random key is generated per process — sessions won't survive restarts.
EMAIL_FROMnoFrom address for transactional emails
RESEND_API_KEYnoResend API key. Leave empty to disable email (magic links will be logged to stdout instead).
TRUSTED_PROXIESnoComma-separated CIDR ranges trusted to set X-Forwarded-For (e.g. 10.0.0.0/8)
PORTnoHTTP port (default: 3000)
OTEL_EXPORTER_OTLP_ENDPOINTnoOTLP collector endpoint for OpenTelemetry traces

Reverse proxy

In production, place EarlyPass behind a reverse proxy (Caddy, nginx, Traefik) that handles TLS. Point the proxy at localhost:3000 and set BASE_URL to your public domain. Set TRUSTED_PROXIES to your proxy's IP range so rate limiting uses the real client IP.

Health check

GET /healthz → {"status":"ok","components":{"database":"ok","redis":"ok"}}

Use this for load balancer health checks, Docker HEALTHCHECK, or Kubernetes liveness/readiness probes.

Data & backups

All persistent state is in PostgreSQL. Redis is used only for rate limiting and idempotency keys — it can be flushed without data loss. Back up your PostgreSQL database regularly:

docker compose exec postgres pg_dump -U earlypass earlypass > backup.sql

Next steps