# A Dockhand-friendly stack: every host-specific bit is pulled from an # environment variable, and the image is pulled from GHCR (published by # .github/workflows/docker-publish.yml) rather than built locally, since a # Dockhand node won't have this repo checked out. # # Every variable below is required (deploy fails with a clear message if # it's missing) except PLEX_TOKEN/JELLYFIN_API_KEY/EMBY_API_KEY, which stay # optional since most setups don't use all three servers. # # In Dockhand: paste this file into a new stack, then add/edit the # variables below (Environment tab, or an uploaded .env) -- secrets are # stored encrypted and injected at deploy time. For plain `docker compose`, # copy `.env.dockhand.example` next to this file as `.env` and fill it in. # # watchable itself is still configured from config.yaml -- this compose # file only supplies the environment variables that config.yaml's own # ${VAR} references resolve against at runtime, plus the handful of things # that vary per-deployment (image tag, paths, schedule). # # WATCHABLE_CONFIG_DIR is mounted as a *directory* (must contain a # config.yaml file) rather than bind-mounting config.yaml directly -- # Unraid's Docker UI doesn't handle single-file mappings well (it tends to # create the host path as a directory instead of bind-mounting the file). # # Volumes use the long form with an explicit `type: bind` rather than the # short "source:target" form -- Compose's short form decides bind-mount vs. # named-volume by whether `source` *looks like* a path (starts with /, ./, # ../, ~/) before env vars are substituted, and some UIs (Dockhand's compose # validator included) apply that check to the raw `${VAR:?...}` text itself, # misreading it as a reference to an undeclared named volume. `type: bind` # removes the ambiguity outright. services: watchable: image: ${WATCHABLE_IMAGE:?Set WATCHABLE_IMAGE, e.g. ghcr.io/poag/watchable:latest} container_name: ${WATCHABLE_CONTAINER_NAME:?Set WATCHABLE_CONTAINER_NAME, e.g. watchable} restart: ${WATCHABLE_RESTART_POLICY:?Set WATCHABLE_RESTART_POLICY, e.g. unless-stopped} command: ["${WATCHABLE_COMMAND:?Set WATCHABLE_COMMAND to run or sync}", "/config/config.yaml"] environment: TZ: ${TZ:?Set TZ, e.g. UTC} # The image starts as root and drops to the `watchable` user remapped # to these ids (default 1000:1000) -- set them to match whatever user # owns WATCHABLE_CONFIG_DIR/WATCHABLE_DATA_PATH on the host. PUID: ${PUID:?Set PUID, e.g. 1000} PGID: ${PGID:?Set PGID, e.g. 1000} # Referenced by config.yaml as ${PLEX_TOKEN} / ${JELLYFIN_API_KEY} / # ${EMBY_API_KEY} -- add one line per extra per-person token your # config.yaml references (e.g. ALEX_PLEX_TOKEN, SAM_PLEX_TOKEN), since # Plex scopes watch state to the token making the request. See # docs/CONFIGURATION.md and config.example.yaml. Left optional here # since most setups don't use all three servers. PLEX_TOKEN: ${PLEX_TOKEN:-} JELLYFIN_API_KEY: ${JELLYFIN_API_KEY:-} EMBY_API_KEY: ${EMBY_API_KEY:-} volumes: - type: bind source: ${WATCHABLE_CONFIG_DIR:?Set WATCHABLE_CONFIG_DIR to a directory containing config.yaml, e.g. ./config} target: /config read_only: true - type: bind source: ${WATCHABLE_DATA_PATH:?Set WATCHABLE_DATA_PATH, e.g. ./data} target: /data