Ctrl K
ComunidadGuías…

docker-compose.yml

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

Archivo original ↗Preguntar a KiBot
# MDMesh production stack. Configure via .env (generated by ./setup.sh), then:
#   Cloudflare Tunnel:  docker compose --profile cloudflare up -d
#     (by hand, also put COMPOSE_PROFILES=cloudflare in .env, as setup.sh does: --profile alone doesn't reach caddy,
#     which then won't believe cloudflared's CF-Connecting-IP; see docker/edge-peers.sh)
#   Own domain (HTTPS): docker compose -f docker-compose.yml -f docker-compose.domain.yml up -d
#
# Security defaults: Postgres and the server publish NO host ports; the edge (Caddy) is the only
# thing exposed, and only in the own-domain override. In tunnel mode nothing is published at all —
# cloudflared reaches Caddy over the internal network.

services:
  postgres:
    image: postgres:14
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${DB_NAME:-mdmesh}
      POSTGRES_USER: ${DB_USER:-mdmesh}
      POSTGRES_PASSWORD: ${DB_PASSWORD:?set DB_PASSWORD in .env}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-mdmesh} -d ${DB_NAME:-mdmesh}"]
      interval: 5s
      timeout: 5s
      retries: 20

  server:
    # Pull-based updates use the versioned GHCR image; `build:` is the source-deploy fallback (when an
    # image tag isn't published locally, compose builds it and tags it with this name). apply.sh bumps
    # SERVER_VERSION in .env then `docker compose pull server` to fetch the new signed image.
    image: ghcr.io/${IMAGE_OWNER:-local}/mdmesh-server:${SERVER_VERSION:-latest}
    build:
      context: .
      dockerfile: docker/server.Dockerfile
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      DB_HOST: postgres
      DB_PORT: "5432"
      DB_NAME: ${DB_NAME:-mdmesh}
      DB_USER: ${DB_USER:-mdmesh}
      DB_PASSWORD: ${DB_PASSWORD:?set DB_PASSWORD in .env}
      BASE_URL: ${BASE_URL:?set BASE_URL in .env}
      HASH_SECRET: ${HASH_SECRET:?set HASH_SECRET in .env}
      # Optional; unset = the key the server keeps in its volume. Prefixed .env key for the reason given at JAVA_OPTS.
      JWT_SECRET: ${SERVER_JWT_SECRET:-}
      SECURE_ENROLLMENT: ${SECURE_ENROLLMENT:-0}
      LEGACY_DEVICE_API: ${LEGACY_DEVICE_API:-0}
      SMTP_HOST: ${SMTP_HOST:-}
      SMTP_PORT: ${SMTP_PORT:-25}
      SMTP_FROM: ${SMTP_FROM:-mdm@localhost}
      SMTP_USERNAME: ${SMTP_USERNAME:-}
      SMTP_PASSWORD: ${SMTP_PASSWORD:-}
      SMTP_SSL: ${SMTP_SSL:-false}
      SMTP_STARTTLS: ${SMTP_STARTTLS:-false}
      # Optional JVM flags, e.g. a temporary DEBUG log config (docs/OPERATIONS.md "Server logs"). Read from the
      # prefixed .env key: compose lets the shell override .env, and a generic JAVA_OPTS exported on a Java host must
      # not reach the server.
      JAVA_OPTS: ${SERVER_JAVA_OPTS:-}
    volumes:
      - mdmeshdata:/opt/mdmesh

  caddy:
    # The web image (Caddy + baked SPA). Same pull/build duality as `server`; WEB_VERSION tracks the
    # release. apply.sh recreates `server` + `caddy` together so the SPA matches the API.
    image: ghcr.io/${IMAGE_OWNER:-local}/mdmesh-web:${WEB_VERSION:-latest}
    build:
      context: .
      dockerfile: docker/web.Dockerfile
      # Enrollment-QR parameters baked into the SPA (see docker/web.Dockerfile ARGs). setup.sh
      # verifies the latest release APK and persists these in .env; empty values fall back to the
      # SPA's debug defaults, matching what release CI bakes into published images.
      args:
        VITE_AGENT_PACKAGE: ${VITE_AGENT_PACKAGE:-}
        VITE_AGENT_CHECKSUM: ${VITE_AGENT_CHECKSUM:-}
        VITE_AGENT_APK_URL: ${VITE_AGENT_APK_URL:-}
    restart: unless-stopped
    depends_on:
      - server
      - supervisor
    environment:
      SITE_ADDRESS: ${SITE_ADDRESS:-:80}
      ACME_EMAIL: ${ACME_EMAIL:-}
      # The hosting mode: in tunnel mode (cloudflare) the edge believes cloudflared's CF-Connecting-IP
      # (docker/edge-peers.sh).
      COMPOSE_PROFILES: ${COMPOSE_PROFILES:-}
      # Proxies in front of the edge (a CDN, a load balancer) whose X-Forwarded-For it believes, as space-separated
      # CIDRs; set in .env. Unset or empty: none (docs/OPERATIONS.md, Sign-in limits).
      TRUSTED_PROXIES: ${TRUSTED_PROXIES:-}
    volumes:
      - caddy_data:/data
      - caddy_config:/config

  # Decoupled updater/recovery service. Polls GitHub for new releases, verifies the signed manifest,
  # exposes /update/status, serves /recovery, and (Phase 4) applies updates by driving compose against
  # the host daemon. The docker socket is host-root-equivalent — the supervisor is trusted and acts
  # only on VERIFIED manifests + AUTHORIZED callers (cookie-forward authz). It NEVER recreates itself
  # or postgres; apply.sh touches only `server` + `caddy`.
  supervisor:
    image: ghcr.io/${IMAGE_OWNER:-local}/mdmesh-supervisor:${SUPERVISOR_VERSION:-latest}
    build:
      context: .
      dockerfile: docker/supervisor.Dockerfile
    restart: unless-stopped
    environment:
      GITHUB_REPO: ${GITHUB_REPO:-}
      UPDATE_CHANNEL: ${UPDATE_CHANNEL:-stable}
      POLL_INTERVAL_HOURS: ${POLL_INTERVAL_HOURS:-6}
      GITHUB_TOKEN: ${GITHUB_TOKEN:-}
      CURRENT_VERSION: ${CURRENT_VERSION:-0.0.0}
      AUTO_UPDATE: ${AUTO_UPDATE:-0}
      # 0 on source installs (IMAGE_OWNER=local): there is no registry image to pull, so the console shows the manual
      # upgrade steps instead of a one-click apply that could only back up, fail and roll back. Native sets 0 too.
      # Defaults to 0 to match this file's IMAGE_OWNER default (local); setup.sh writes 1 for a registry owner.
      APPLY_SUPPORTED: ${APPLY_SUPPORTED:-0}
      # Pin project + compose-file set so the supervisor drives the SAME stack the host launched
      # (default project name = dir basename would fork a duplicate; COMPOSE_FILE carries the domain
      # overlay so recreating caddy keeps its published ports).
      COMPOSE_PROJECT_NAME: ${COMPOSE_PROJECT_NAME:-mdmesh}
      COMPOSE_FILE: ${COMPOSE_FILE:-docker-compose.yml}
      COMPOSE_PROFILES: ${COMPOSE_PROFILES:-}
      DB_USER: ${DB_USER:-mdmesh}
      DB_NAME: ${DB_NAME:-mdmesh}
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - .:/project
      - backups:/backups

  cloudflared:
    image: cloudflare/cloudflared:latest
    restart: unless-stopped
    profiles: ["cloudflare"]
    depends_on:
      - caddy
    command: tunnel --no-autoupdate run
    environment:
      TUNNEL_TOKEN: ${TUNNEL_TOKEN:-}

volumes:
  pgdata:
  mdmeshdata:
  caddy_data:
  caddy_config:
  backups:   # supervisor pre-update DB dumps + version snapshots + auto.json (survives recreate)