Ctrl K
ComunidadGuías…

docker-compose.yml

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

Archivo original ↗Preguntar a KiBot
services:
  app:
    # A published image, pinned to an exact version by the installer, never a
    # moving tag. The default here is only what an unconfigured `docker
    # compose up` would get; every real install writes SUPAFFI_VERSION into
    # .env so an update is a deliberate move from one version to another.
    #
    # Building from source instead is still supported and is one line in .env:
    # COMPOSE_FILE=docker-compose.yml:docker-compose.build.yml
    image: ghcr.io/ugolbck/supaffi:${SUPAFFI_VERSION:-latest}
    restart: unless-stopped
    environment:
      DATABASE_URL: postgres://supaffi:${POSTGRES_PASSWORD}@db:5432/supaffi
      MASTER_ENCRYPTION_KEY: ${MASTER_ENCRYPTION_KEY} # decrypts Merchant Stripe keys at rest (ADR 0003), generated once at install, never stored in the DB
      AUTH_SECRET: ${AUTH_SECRET} # signs Owner session JWTs (Auth.js)
      AUTH_TRUST_HOST: "true" # Instance is served on many Merchant domains (ADR 0006), so there is no single AUTH_URL to pin; Caddy's on_demand_tls ask endpoint already gates which domains reach the app
      SUPAFFI_DOMAIN: ${SUPAFFI_DOMAIN} # the domain this Instance's own dashboard is served on; a Merchant may not claim it (src/lib/instance.ts)
      SUPAFFI_HOST_IP: ${SUPAFFI_HOST_IP:-} # the address the startup banner tells the operator to open, when there is no domain
      SUPAFFI_DASHBOARD_PORT: ${SUPAFFI_DASHBOARD_PORT:-3443} # where caddy-dashboard serves it
      SUPAFFI_UPDATE_CHECK: ${SUPAFFI_UPDATE_CHECK:-} # off to stop the dashboard reading GitHub's releases page
      # Where the onboarding check asks for a Merchant domain's certificate.
      # Fetching the domain's own public address from in here goes out to this
      # host's IP and back, and many hosts do not route that hairpin, so a
      # working site reads as unreachable. Set it when your proxy is not the
      # caddy service below; an instance behind someone else's proxy leaves it
      # unset and the check falls back to the round trip.
      SUPAFFI_PROXY_HOST: ${SUPAFFI_PROXY_HOST:-caddy}
      # SUPAFFI_VERSION is deliberately not passed in. It is baked into the
      # image at build time, so the version the dashboard reports is the
      # version that is actually running rather than whatever .env claims.
    ports:
      # Loopback by default, which is what proxy-less mode needs: the
      # operator's own reverse proxy forwards to 127.0.0.1:3000, and the SSH
      # tunnel in the README works with no extra steps. Binding all
      # interfaces would be worse than it looks, for the reason spelled out
      # on the db service below: Docker's iptables rules land ahead of ufw
      # and firewalld, so a published port is internet-reachable on a host
      # the operator believes is firewalled. Override deliberately, never by
      # accident.
      - "${SUPAFFI_APP_BIND:-127.0.0.1:3000}:3000"
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:18-alpine
    restart: unless-stopped
    # No published port. Docker writes its own iptables rules ahead of ufw and
    # firewalld, so a published 5432 is reachable from the internet even on a
    # host the operator believes is firewalled. The app reaches it as db:5432
    # over the compose network. For local psql access see
    # docker-compose.override.yml.example.
    environment:
      POSTGRES_USER: supaffi
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: supaffi
    healthcheck:
      # The app's entrypoint runs `prisma migrate deploy` before the server
      # starts. Waiting only for the container to exist (plain depends_on)
      # lets that run before Postgres accepts connections; it usually wins the
      # race, which is the worst kind of bug to ship to strangers' machines.
      test: ["CMD-SHELL", "pg_isready -U supaffi -d supaffi"]
      interval: 3s
      timeout: 3s
      retries: 20
    volumes:
      - supaffi_db:/var/lib/postgresql

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    # Opt-in, because this service takes ports 80 and 443 for itself and a
    # server that already runs a reverse proxy has neither to give. The
    # installer writes COMPOSE_PROFILES=bundled-proxy into .env for the
    # default path; leaving it unset drops this service entirely and the
    # operator's own proxy forwards to SUPAFFI_APP_BIND instead.
    #
    # A profile rather than a second compose file selected with -f: the
    # choice then lives in .env, so every command afterwards stays a plain
    # `docker compose up -d` / `docker compose logs app`, which is what the
    # README and the installer's token grep both rely on.
    profiles: ["bundled-proxy"]
    environment:
      # Compose always materializes this key inside the container, even when
      # SUPAFFI_DOMAIN is unset on the host, so Caddy would otherwise see an
      # empty string rather than a missing variable. The Caddyfile's
      # {$SUPAFFI_DOMAIN:localhost} default only fires on a genuinely absent
      # variable, so a present-but-empty value skips it and crashes Caddy on
      # startup. The default belongs here too, not just in the Caddyfile.
      SUPAFFI_DOMAIN: ${SUPAFFI_DOMAIN:-localhost} # substituted into the named site block in ./Caddyfile before parsing
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - supaffi_caddy_data:/data
    depends_on:
      - app

  caddy-dashboard:
    image: caddy:2-alpine
    restart: unless-stopped
    # Always runs, unlike the caddy service above. That one only exists when
    # Supaffi owns ports 80 and 443, and the dashboard has to be reachable on
    # a server where the operator's own nginx owns them. Compose also rejects
    # an empty port entry, so one service cannot publish 80/443 in one
    # situation and 3443 in another; two port layouts need two services.
    #
    # Still behind a profile so a single COMPOSE_PROFILES value in .env
    # describes the whole shape of the install. The installer always writes
    # dashboard-tls into it.
    profiles: ["dashboard-tls"]
    environment:
      # Defaults repeated here and not left to the Caddyfile alone: Compose
      # materializes every key it declares, so an unset host variable would
      # reach Caddy as an empty string, which is not the same as absent and
      # skips the Caddyfile's own default. That crash-looped the other Caddy
      # once already.
      SUPAFFI_HOST_IP: ${SUPAFFI_HOST_IP:-127.0.0.1}
      SUPAFFI_DASHBOARD_PORT: ${SUPAFFI_DASHBOARD_PORT:-3443}
    ports:
      # On all interfaces, deliberately, unlike the app's own binding. Being
      # reachable from the operator's laptop with no DNS and no proxy is the
      # entire point of this service, and the connection is encrypted.
      - "${SUPAFFI_DASHBOARD_PORT:-3443}:${SUPAFFI_DASHBOARD_PORT:-3443}"
    volumes:
      - ./Caddyfile.dashboard:/etc/caddy/Caddyfile
      - supaffi_caddy_dashboard_data:/data
    depends_on:
      - app

volumes:
  supaffi_db:
  supaffi_caddy_data:
  supaffi_caddy_dashboard_data: