Deploy ShellForge

Two deployment options — Docker for simplicity, Kubernetes for scale. Pick the one that fits your infra.

Images
  • ghcr.io/llinguini/shellforge-api:latest — API (port 8080)
  • ghcr.io/llinguini/shellforge-web:latest — Web dashboard (port 3000)
docker-compose.yml
services:
  postgres:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_USER: shellforge
      POSTGRES_PASSWORD: change-me-use-a-long-random-string
      POSTGRES_DB: shellforge
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U shellforge"]
      interval: 5s
      timeout: 5s
      retries: 10

  api:
    image: ghcr.io/llinguini/shellforge-api:latest
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    ports:
      - "8080:8080"
    environment:
      APP_ENV: production
      PORT: 8080
      DB_HOST: postgres
      DB_PORT: 5432
      DB_USER: shellforge
      DB_PASSWORD: change-me-use-a-long-random-string
      DB_NAME: shellforge
      DB_AUTO_MIGRATE: "true"
      DB_CONNECT_MAX_WAIT: 30s
      JWT_SECRET: replace-me-with-a-long-random-secret-min-32-chars
      JWT_ACCESS_TTL: 15m
      JWT_REFRESH_TTL: 168h
      CORS_ALLOWED_ORIGINS: "http://your-app-domain.com"
      LOG_LEVEL: info

  web:
    image: ghcr.io/llinguini/shellforge-web:latest
    restart: unless-stopped
    depends_on:
      - api
    ports:
      - "3000:3000"
    environment:
      NEXT_PUBLIC_API_URL: "http://your-api-domain.com"

volumes:
  postgres_data:
.env reference
# API environment variables — never commit real secrets

APP_ENV=production
PORT=8080

# PostgreSQL
DB_HOST=postgres
DB_PORT=5432
DB_USER=shellforge
DB_PASSWORD=change-me-use-a-long-random-string
DB_NAME=shellforge
DB_AUTO_MIGRATE=true
DB_CONNECT_MAX_WAIT=30s

# JWT — use a long random string in production
JWT_SECRET=replace-me-with-a-long-random-secret-min-32-chars
JWT_ACCESS_TTL=15m
JWT_REFRESH_TTL=168h

# CORS — set to your web app domain
CORS_ALLOWED_ORIGINS=http://your-app-domain.com

LOG_LEVEL=info

# Web app
NEXT_PUBLIC_API_URL=http://your-api-domain.com

Steps

  1. Copy docker-compose.yml and replace all placeholder values (change-me-*, replace-me-*, domain URLs).
  2. Run docker compose up -d.
  3. The API runs on port 8080, the web dashboard on port 3000.
  4. Point your reverse proxy (nginx, Caddy, Traefik…) to those ports and add TLS.