🐳 stack-auth

The user infrastructure platform. You choose the frontend, backend, and database. Hexclave handles everything else.

Docker Ready 23 Services 6,843 stars
1. Pull Image docker pull ankane/pghero
2. 1-Click Launch Command docker run -d --name stack-auth ankane/pghero:latest
3. Compose Deployment docker compose up -d
docker/dependencies/docker.compose.yaml
Download .yml
services:

  # ================= PostgreSQL =================

  db:
    build: ../dev-postgres-with-extensions
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: PASSWORD-PLACEHOLDER--uqfEC1hmmv
      # Note: A readonly user is also created with password PASSWORD-PLACEHOLDER--readonlyuqfEC1hmmv
      # for read replica emulation. See the Dockerfile for details.
      POSTGRES_DB: stackframe
      POSTGRES_DELAY_MS: ${POSTGRES_DELAY_MS:-0}
      POSTGRES_INITDB_ARGS: --nosync
    # Increase max_connections for E2E tests that create many databases
    command: postgres -c max_connections=500
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}28:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
    cap_add:
      - NET_ADMIN  # required for the fake latency during dev

  # ================= PostgreSQL Replica (with replication lag) =================

  db-replica:
    build: ../dev-postgres-replica
    environment:
      PGDATA: /var/lib/postgresql/data
      PRIMARY_HOST: db
      PRIMARY_PORT: 5432
      REPLICATOR_USER: replicator
      REPLICATOR_PASSWORD: PASSWORD-PLACEHOLDER--replicatorpass
      RECOVERY_MIN_APPLY_DELAY: ${STACK_DATABASE_REPLICA_LAG_MS:-15}ms
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}34:5432"
    volumes:
      - postgres-replica-data:/var/lib/postgresql/data
    depends_on:
      - db

  # ================= PgHero =================

  pghero:
    image: ankane/pghero:latest
    environment:
      DATABASE_URL: postgres://postgres:PASSWORD-PLACEHOLDER--uqfEC1hmmv@db:5432/stackframe
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}16:8080"

  # ================= WAL Info =================

  wal-info:
    build: ./wal-info
    environment:
      PRIMARY_HOST: db
      PRIMARY_PORT: 5432
      REPLICA_HOST: db-replica
      REPLICA_PORT: 5432
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: PASSWORD-PLACEHOLDER--uqfEC1hmmv
      POSTGRES_DB: stackframe
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}38:8080"
    depends_on:
      - db
      - db-replica

  # ================= PgHero (Replica) =================

  pghero-replica:
    image: ankane/pghero:latest
    environment:
      DATABASE_URL: postgres://postgres:PASSWORD-PLACEHOLDER--uqfEC1hmmv@db-replica:5432/stackframe
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}35:8080"
    depends_on:
      - db-replica

  # ================= PgAdmin =================

  pgadmin:
    image: dpage/pgadmin4
    environment:
      PGADMIN_DEFAULT_EMAIL: [email protected]
      PGADMIN_DEFAULT_PASSWORD: PASSWORD-PLACEHOLDER--vu9p2iy3f  # note: this password is not used for authentication due to PGADMIN_CONFIG_SERVER_MODE=False
      PGADMIN_CONFIG_SERVER_MODE: "False"
      PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"
    configs:
      - source: pgadmin_servers
        target: /pgadmin4/servers.json
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}17:80"

  # ================= Supabase Studio =================

  supabase-studio:
    image: supabase/studio:20241202-71e5240
    restart: unless-stopped
    healthcheck:
      test:
        [
          "CMD",
          "node",
          "-e",
          "fetch('http://studio:3000/api/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})"
        ]
      timeout: 10s
      interval: 5s
      retries: 3
    environment:
      STUDIO_PG_META_URL: http://supabase-meta:8080
      POSTGRES_PASSWORD: PASSWORD-PLACEHOLDER--uqfEC1hmmv

      OPENAI_API_KEY: ${OPENAI_API_KEY:-}

      NEXT_PUBLIC_ENABLE_LOGS: true
      NEXT_ANALYTICS_BACKEND_PROVIDER: postgres
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}18:3000"
  
  supabase-meta:
    image: supabase/postgres-meta:v0.84.2
    restart: unless-stopped
    environment:
      PG_META_PORT: 8080
      PG_META_DB_HOST: db
      PG_META_DB_PORT: 5432
      PG_META_DB_NAME: stackframe
      PG_META_DB_USER: postgres
      PG_META_DB_PASSWORD: PASSWORD-PLACEHOLDER--uqfEC1hmmv


  # ================= Inbucket =================

  inbucket:
    image: inbucket/inbucket:3.1.0
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}29:2500"
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}05:9000"
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}30:1100"
    volumes:
      - inbucket-data:/data

  # ================= OpenTelemetry & Jaeger =================

  jaeger:
    image: jaegertracing/all-in-one:latest
    environment:
      - COLLECTOR_OTLP_ENABLED=true
      - MEMORY_MAX_TRACES=50000
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}07:16686"  # Jaeger UI
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}31:4318"    # OTLP Endpoint
    mem_limit: 1g
    restart: always

  # ================= svix =================

  svix-db:
    image: "docker.io/postgres:16.1"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: PASSWORD-PLACEHOLDER--KsoIMcchtp
      POSTGRES_DB: svix
    volumes:
      - svix-postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -p 5432 -d svix"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    
  svix-redis:
    image: docker.io/redis:7-alpine
    command: --save 60 500 --appendonly yes --appendfsync everysec --requirepass PASSWORD-PLACEHOLDER--oVn8GSD6b9
    volumes:
      - svix-redis-data:/data
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s

  svix-server:
    image: svix/svix-server
    environment:
      WAIT_FOR: 'true'
      SVIX_REDIS_DSN: redis://:PASSWORD-PLACEHOLDER--oVn8GSD6b9@svix-redis:6379
      SVIX_DB_DSN: postgres://postgres:PASSWORD-PLACEHOLDER--KsoIMcchtp@svix-db:5432/svix
      SVIX_CACHE_TYPE: memory
      SVIX_JWT_SECRET: secret
      SVIX_LOG_LEVEL: trace
      SVIX_QUEUE_TYPE: redis
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}13:8071"
    depends_on:
      svix-redis:
        condition: service_healthy
      svix-db:
        condition: service_healthy

  # ================= Adobe S3 Mock =================
  
  s3mock:
    image: adobe/s3mock:4.12.2
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}21:9090"
    environment:
      - initialBuckets=stack-storage,stack-storage-private,marshal
      - root=s3mockroot
      - debug=false
    volumes:
      - s3mock-data:/tmp/s3mock
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:9090/"]
      interval: 30s
      timeout: 10s
      retries: 3


  # ================= LocalStack =================
  localstack:
    image: localstack/localstack:4.7
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}24:4566"            # LocalStack Gateway
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}50-${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}99:4510-4559"  # external services port range
    environment:
      # LocalStack configuration: https://docs.localstack.cloud/references/configuration/
      - DEBUG=${DEBUG:-0}
    volumes:
      - localstack-data:/var/lib/localstack
      - "/var/run/docker.sock:/var/run/docker.sock"

  # ================= Freestyle mock =================

  freestyle-mock:
    build:
      context: ./freestyle-mock
      dockerfile: Dockerfile
    image: freestyle-mock
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}22:8080"       # POST http://localhost:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}19/execute/v1/script
    extra_hosts:
      - "host.docker.internal:host-gateway"  # noop on Docker Desktop/Orbstack, enables host.docker.internal on Linux
    environment:
      HOST_ON_HOST: host.docker.internal
      HEXCLAVE_FREESTYLE_MOCK_MAX_IN_FLIGHT: "4"
      HEXCLAVE_FREESTYLE_MOCK_BRIDGE_ENABLED: "true"
      HEXCLAVE_FREESTYLE_MOCK_BRIDGE_ORIGINS: "http://host.docker.internal:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}02"
    read_only: true
    tmpfs:
      - /tmp:size=256m,mode=1777
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    mem_limit: 1g
    cpus: 4
    pids_limit: 512
    volumes:
      - freestyle-module-cache:/app/module-cache

  # ================= Vercel mock =================

  vercel-mock:
    build:
      context: ./vercel-mock
      dockerfile: Dockerfile
    image: vercel-mock
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}26:8080"       # mock of the Vercel REST API for the Deployments app

  # ================= Fly mock =================

  fly-mock:
    build:
      context: ./fly-mock
      dockerfile: Dockerfile
    image: fly-mock
    restart: unless-stopped                                    # a guarded handler shouldn't crash, but recover the container if it ever does
    ports:
      # Bound to LOOPBACK, unlike the other mocks: this one's /__mock introspection returns
      # each machine's resolved environment, which in a deployments test run holds decrypted
      # project secrets. Only Marshal (on the host) needs to reach it, so there is nothing to
      # gain from publishing it to every interface.
      - "127.0.0.1:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}48:8080"  # mock of the Fly.io Machines/GraphQL/logs APIs for Marshal

  # ================= Stripe =================
  
  stripe-mock:
    image: stripe/stripe-mock:v0.195.0
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}23:12111"
    environment:
      - STRIPE_API_KEY=sk_test_1234567890

  # ================= QStash =================

  qstash:
    image: bgodil/qstash:latest
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}25:8080"
    command: qstash dev
    extra_hosts:
      - "host.docker.internal:host-gateway"  # noop on Docker Desktop/Orbstack, enables host.docker.internal on Linux
    environment:
      HOST_ON_HOST: host.docker.internal

  # ================= ClickHouse =================

  clickhouse:
    image: clickhouse/clickhouse-server:25.10
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}36:8123"  # HTTP interface
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}37:9000"  # Native interface
    environment:
      CLICKHOUSE_DB: analytics
      CLICKHOUSE_USER: stackframe
      CLICKHOUSE_PASSWORD: PASSWORD-PLACEHOLDER--9gKyMxJeMx
      CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1
    volumes:
      - clickhouse-data:/var/lib/clickhouse
    ulimits:
      nofile:
        soft: 262144
        hard: 262144

  # ================= SpacetimeDB =================

  spacetimedb:
    image: clockworklabs/spacetime:v2.1.0
    pull_policy: always
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}39:3000"
    command: start

  # ================= Drizzle Gateway =================

  drizzle-gateway:
    image: ghcr.io/drizzle-team/gateway:latest
    restart: always
    ports:
      - "${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}33:1133"
    environment:
      PORT: 1133
      STORE_PATH: /app
    volumes:
      - drizzle-gateway:/app

# ================= volumes =================

volumes:
  postgres-data:
  postgres-replica-data:
  inbucket-data:
  svix-redis-data:
  svix-postgres-data:
  s3mock-data:
  freestyle-module-cache:
  localstack-data:
  clickhouse-data:
  drizzle-gateway:

# ================= configs =================

configs:
  pgadmin_servers:
    content: |
      {
        "Servers": {
          "1": {
            "Name": "Local Postgres DB",
            "Group": "Servers",
            "Host": "db",
            "Port": 5432,
            "Username": "postgres",
            "PasswordExecCommand": "echo 'PASSWORD-PLACEHOLDER--uqfEC1hmmv'",
            "MaintenanceDB": "stackframe"
          }
        }
      }