Ctrl K
ComunidadGuías…

docker-compose.yml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Shared build config: pass NEXT_PUBLIC_WS_URL as a build arg so Next inlines it into the client
# bundle at build time (F29). A runtime-only env_file does NOT reach the client bundle.
x-app-build: &app-build
  context: .
  args:
    NEXT_PUBLIC_WS_URL: ${NEXT_PUBLIC_WS_URL:-}
    # Optional version stamp for the update-check banner. Unset falls back to package.json, then "dev".
    APP_VERSION: ${APP_VERSION:-}

configs:
  garage_toml:
    content: |
      metadata_dir = "/var/lib/garage/meta"
      data_dir = "/var/lib/garage/data"
      db_engine = "sqlite"
      metadata_fsync = true
      data_fsync = true
      replication_factor = 1
      rpc_bind_addr = "[::]:3901"
      rpc_public_addr = "127.0.0.1:3901"
      [s3_api]
      s3_region = "${S3_REGION:-garage}"
      api_bind_addr = "[::]:3900"

services:
  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    environment:
      # Substituted into the Caddyfile as {$APP_DOMAIN} / {$ACME_EMAIL}. Compose auto-loads
      # these from the project .env for interpolation.
      APP_DOMAIN: ${APP_DOMAIN}
      ACME_EMAIL: ${ACME_EMAIL}
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddydata:/data
      - caddyconfig:/config
    depends_on:
      app:
        condition: service_healthy
      ws:
        condition: service_started

  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER:-warpdrive}
      # Overridable, with the historical value as the fallback so existing boxes keep booting.
      # Postgres only applies this at initdb, so setting it against an EXISTING pgdata volume
      # does NOT rotate the password; it just makes DATABASE_URL wrong. Choose it on a fresh
      # deploy, and keep it identical to the password inside DATABASE_URL.
      # Not reachable from outside the box today (this service publishes no host port and Caddy
      # is the only public listener), so the weak default is a defense-in-depth gap rather than a
      # live hole. It becomes a live hole the moment anyone adds a `ports:` mapping here.
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-warpdrive}
      POSTGRES_DB: ${POSTGRES_DB:-warpdrive}
    volumes: [pgdata:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-warpdrive}"]
      interval: 5s
      timeout: 5s
      retries: 10

  garage:
    image: dxflrs/garage:v2.5.0
    restart: unless-stopped
    command: ["/garage", "server", "--single-node", "--default-bucket"]
    configs:
      - source: garage_toml
        target: /etc/garage.toml
    environment:
      GARAGE_RPC_SECRET: ${GARAGE_RPC_SECRET:?GARAGE_RPC_SECRET is required, generate one with openssl rand -hex 32}
      GARAGE_DEFAULT_ACCESS_KEY: ${S3_ACCESS_KEY:-${MINIO_ACCESS_KEY:?S3_ACCESS_KEY is required}}
      GARAGE_DEFAULT_SECRET_KEY: ${S3_SECRET_KEY:-${MINIO_SECRET_KEY:?S3_SECRET_KEY is required}}
      GARAGE_DEFAULT_BUCKET: ${S3_BUCKET:-${MINIO_BUCKET:-warpdrive}}
    volumes:
      - garagemeta:/var/lib/garage/meta
      - garagedata:/var/lib/garage/data

  storagecors:
    image: amazon/aws-cli:2.39.3
    depends_on:
      garage:
        condition: service_started
    environment:
      AWS_ENDPOINT_URL: http://garage:3900
      AWS_ACCESS_KEY_ID: ${S3_ACCESS_KEY:-${MINIO_ACCESS_KEY:?S3_ACCESS_KEY is required}}
      AWS_SECRET_ACCESS_KEY: ${S3_SECRET_KEY:-${MINIO_SECRET_KEY:?S3_SECRET_KEY is required}}
      AWS_DEFAULT_REGION: ${S3_REGION:-garage}
      BUCKET: ${S3_BUCKET:-${MINIO_BUCKET:-warpdrive}}
      CORS_CONFIGURATION: '{"CORSRules":[{"AllowedOrigins":["${BASE_URL:?BASE_URL is required}"],"AllowedMethods":["GET","HEAD","POST"],"AllowedHeaders":["*"],"ExposeHeaders":["ETag"],"MaxAgeSeconds":3600}]}'
    entrypoint: ["/bin/sh", "-c"]
    command:
      - >-
        for attempt in $$(seq 1 30); do
        aws s3api put-bucket-cors --bucket "$$BUCKET" --cors-configuration "$$CORS_CONFIGURATION" && exit 0;
        echo "waiting for garage ($$attempt/30)"; sleep 2;
        done; exit 1

  # One-shot: apply forward-only Drizzle migrations, then exit. Gates the long-running tier.
  migrate:
    build: *app-build
    command: ["node", "dist/migrate.mjs"]
    env_file: .env
    depends_on:
      postgres:
        condition: service_healthy

  app:
    build: *app-build
    restart: unless-stopped
    command: ["node_modules/.bin/next", "start"]
    env_file: .env
    environment:
      S3_REGION: ${S3_REGION:-garage}
    healthcheck:
      test: ["CMD-SHELL", "wget -q -O /dev/null http://localhost:3000/api/health || exit 1"]
      interval: 15s
      timeout: 5s
      retries: 5
      start_period: 20s
    depends_on:
      migrate:
        condition: service_completed_successfully
      storagecors:
        condition: service_completed_successfully

  ws:
    build: *app-build
    restart: unless-stopped
    command: ["node", "dist/ws.mjs"]
    env_file: .env
    healthcheck:
      test:
        - "CMD"
        - "node"
        - "-e"
        - "require('node:net').connect(8080,'127.0.0.1').on('connect',()=>process.exit(0)).on('error',()=>process.exit(1))"
      interval: 15s
      timeout: 5s
      retries: 5
      start_period: 10s
    depends_on:
      migrate:
        condition: service_completed_successfully

  worker:
    build: *app-build
    restart: unless-stopped
    command: ["node", "dist/worker.mjs"]
    env_file: .env
    environment:
      S3_REGION: ${S3_REGION:-garage}
    depends_on:
      migrate:
        condition: service_completed_successfully

volumes:
  pgdata:
  garagemeta:
  garagedata:
  caddydata:
  caddyconfig: