Ctrl K
ComunidadGuías…

docker-compose.yml

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

Archivo original ↗Preguntar a KiBot
services:
  jobd:
    # Pulled from GHCR, NOT built here. There used to be `build: .` + `image:
    # jobd:latest` with no registry behind that tag, which meant `docker compose pull`
    # was a silent no-op and a bare `docker compose up -d` after a `git pull` happily
    # reused the stale local image — you restarted the OLD code and nothing said so.
    # The workaround was to remember `--build` forever. A registry-backed, version-
    # pinned tag deletes that whole class of mistake instead of guarding against it.
    #
    # JOBD_TAG pins the EXACT version (e.g. 0.5.18). Production should never follow
    # `latest`: scripts/deploy-broker.sh resolves the newest release, writes JOBD_TAG
    # into .env, and can roll back to the previous pin if the new image fails its
    # health gate — none of which is possible against a moving tag. The `latest`
    # default here exists only so a fresh clone can start without ceremony.
    #
    # Building from source (a fork, or a local change):
    #   docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build
    image: ghcr.io/musharna/jobd:${JOBD_TAG:-latest}
    container_name: jobd
    restart: unless-stopped
    # PERMISSIONS: the image runs as a non-root user (uid 10001, see Dockerfile).
    # The bind-mounted ./data + ./logs below must be WRITABLE by that user, or the
    # broker crash-loops on "sqlite3.OperationalError: attempt to write a readonly
    # database". Two fixes — pick one:
    #   (a) chown the dirs to the image user:  chown -R 10001:10001 ./data ./logs
    #   (b) run the container as your host user so the files stay host-owned (nicer
    #       for backups/log-tailing): uncomment the line below, replacing 1000:1000
    #       with your own `id -u`:`id -g`.
    # user: "1000:1000"
    # SECURITY: host networking + JOBD_HOST binding is the access-control model.
    # Docker's default userland-proxy NATs all cross-host inbound source IPs to
    # the bridge gateway, which defeats the source-IP ACL. With network_mode:
    # host, uvicorn binds DIRECTLY to JOBD_HOST (a tailscale IP) on the host's
    # network namespace — only requests arriving on the tailscale interface
    # reach the broker, and source IPs are preserved for the ACL to inspect.
    # JOBD_HOST MUST be a loopback (127.0.0.1) or tailscale CGNAT (100.64.0.0/10)
    # address — never 0.0.0.0 or a public-facing interface. tests/test_deploy_lint.py
    # enforces this at CI.
    network_mode: host
    volumes:
      - ./config:/app/config:ro
      - ./data:/app/data
      - ./logs:/app/logs
    environment:
      JOBD_CONFIG_DIR: /app/config
      JOBD_DB_URL: sqlite:////app/data/jobd.db
      JOBD_LOGS_DIR: /app/logs
      # Set to your host's tailscale CGNAT IP (100.64.0.0/10) for cross-host
      # access, or leave at 127.0.0.1 for a single-host (loopback-only) broker.
      # MUST NOT be 0.0.0.0 or a public interface — see the SECURITY note above.
      JOBD_HOST: ${JOBD_HOST:-127.0.0.1}
      JOBD_PORT: 8765
      JOBD_API_TOKEN: ${JOBD_API_TOKEN:?JOBD_API_TOKEN must be set in .env (see docs/security.md)}