services: traefik: image: ghcr.io/traefik/traefik:3.3 container_name: traefik restart: always command: - --ping=true - --providers.docker=true - --providers.docker.exposedbydefault=false - --entrypoints.web.address=:80 - --entrypoints.web-secure.address=:443 - --experimental.plugins.rewrite-body.modulename=github.com/packruler/rewrite-body - --experimental.plugins.rewrite-body.version=v1.2.0 - --experimental.plugins.rewriteHeaders.modulename=github.com/XciD/traefik-plugin-rewrite-headers - --experimental.plugins.rewriteHeaders.version=v0.0.3 ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro extra_hosts: - host.docker.internal:172.17.0.1 healthcheck: test: ["CMD", "traefik", "healthcheck", "--ping"] interval: 30s retries: 10 sonarr: image: lscr.io/linuxserver/sonarr container_name: sonarr environment: - PUID=${USER_ID} - PGID=${GROUP_ID} - TZ=${TIMEZONE} volumes: - ${CONFIG_ROOT:-.}/sonarr:/config:Z - ${DATA_ROOT}:/data:Z restart: always healthcheck: test: ["CMD", "curl", "--fail", "http://127.0.0.1:8989/sonarr/ping"] interval: 30s retries: 10 labels: - traefik.enable=true - traefik.http.routers.sonarr.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/sonarr`)) - traefik.http.routers.sonarr.entrypoints=web - traefik.http.services.sonarr.loadbalancer.server.port=8989 - homepage.group=Media - homepage.name=Sonarr - homepage.icon=sonarr.png - homepage.href=/sonarr - homepage.description=Series management - homepage.weight=0 - homepage.widget.type=sonarr - homepage.widget.url=http://sonarr:8989/sonarr - homepage.widget.key=${SONARR_API_KEY} radarr: image: lscr.io/linuxserver/radarr container_name: radarr environment: - PUID=${USER_ID} - PGID=${GROUP_ID} - TZ=${TIMEZONE} volumes: - ${CONFIG_ROOT:-.}/radarr:/config:Z - ${DATA_ROOT}:/data:Z restart: always healthcheck: test: ["CMD", "curl", "--fail", "http://127.0.0.1:7878/radarr/ping"] interval: 30s retries: 10 labels: - traefik.enable=true - traefik.http.routers.radarr.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/radarr`)) - traefik.http.routers.radarr.entrypoints=web - traefik.http.services.radarr.loadbalancer.server.port=7878 - homepage.group=Media - homepage.name=Radarr - homepage.icon=radarr.png - homepage.href=/radarr - homepage.description=Movies management - homepage.weight=1 - homepage.widget.type=radarr - homepage.widget.url=http://radarr:7878/radarr - homepage.widget.key=${RADARR_API_KEY} lidarr: image: lscr.io/linuxserver/lidarr container_name: lidarr environment: - PUID=${USER_ID} - PGID=${GROUP_ID} - TZ=${TIMEZONE} volumes: - ${CONFIG_ROOT:-.}/lidarr:/config:Z - ${DATA_ROOT}:/data:Z restart: always healthcheck: test: ["CMD", "curl", "--fail", "http://127.0.0.1:8686/lidarr/ping"] interval: 30s retries: 10 labels: - traefik.enable=true - traefik.http.routers.lidarr.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/lidarr`)) - traefik.http.routers.lidarr.entrypoints=web - traefik.http.services.lidarr.loadbalancer.server.port=8686 - homepage.group=Media - homepage.name=Lidarr - homepage.icon=lidarr.png - homepage.href=/lidarr - homepage.description=Music management - homepage.weight=2 - homepage.widget.type=lidarr - homepage.widget.url=http://lidarr:8686/lidarr - homepage.widget.key=${LIDARR_API_KEY} profiles: - lidarr bazarr: image: lscr.io/linuxserver/bazarr container_name: bazarr environment: - PUID=${USER_ID} - PGID=${GROUP_ID} - TZ=${TIMEZONE} volumes: - ${CONFIG_ROOT:-.}/bazarr/config:/config:Z - ${DATA_ROOT}:/data:Z restart: always healthcheck: test: ["CMD", "curl", "--fail", "http://127.0.0.1:6767/bazarr/ping"] interval: 5s retries: 10 labels: - traefik.enable=true - traefik.http.routers.bazarr.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/bazarr`)) - traefik.http.routers.bazarr.entrypoints=web - traefik.http.services.bazarr.loadbalancer.server.port=6767 - homepage.group=Download - homepage.name=Bazarr - homepage.icon=bazarr.png - homepage.href=/bazarr - homepage.description=Subtitles management - homepage.weight=4 - homepage.widget.type=bazarr - homepage.widget.url=http://bazarr:6767/bazarr - homepage.widget.key=${BAZARR_API_KEY} jellyseerr: image: ghcr.io/fallenbagel/jellyseerr:latest container_name: jellyseerr environment: - LOG_LEVEL=debug - TZ=${TIMEZONE} volumes: - ${CONFIG_ROOT:-.}/jellyseerr:/app/config:Z restart: always healthcheck: test: [ "CMD", "wget", "http://127.0.0.1:5055/api/v1/status", "-qO", "/dev/null", ] interval: 30s retries: 10 labels: - traefik.enable=true - traefik.http.routers.jellyseerr.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/jellyseerr`)) - traefik.http.routers.jellyseerr.entrypoints=web - traefik.http.services.jellyseerr.loadbalancer.server.port=5055 - traefik.http.routers.jellyseerr.middlewares=jellyseerr-stripprefix,jellyseerr-rewrite,jellyseerr-rewriteHeaders - traefik.http.middlewares.jellyseerr-stripprefix.stripPrefix.prefixes=/jellyseerr - traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[0].header=location - traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[0].regex=^/(.+)$ - traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[0].replacement=/jellyseerr/$1 - traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[1].header=location - traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[1].regex=^/$ - traefik.http.middlewares.jellyseerr-rewriteHeaders.plugin.rewriteHeaders.rewrites[1].replacement=/jellyseerr - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.monitoring.types[0]=text/html - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.monitoring.types[1]=application/javascript - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.monitoring.types[2]=*/* - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.monitoring.types[3]=application/json - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[1].regex=/_next - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[1].replacement=/jellyseerr/_next - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[2].regex=/_next/data/ - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[2].replacement=/jellyseerr/_next/data/ - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[3].regex=/api/v1 - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[3].replacement=/jellyseerr/api/v1 - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[4].regex=/login/plex/loading - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[4].replacement=/jellyseerr/login/plex/loading - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[5].regex=/images/ - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[5].replacement=/jellyseerr/images/ - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[6].regex=/favicon - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[6].replacement=/jellyseerr/favicon - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[7].regex=/logo_ - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[7].replacement=/jellyseerr/logo_ - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[8].regex=/site.webmanifest - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[8].replacement=/jellyseerr/site.webmanifest - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[9].regex=/sw.js - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[9].replacement=/jellyseerr/sw.js - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[10].regex=/offline.html - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[10].replacement=/jellyseerr/offline.html - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[11].regex=src="/os_logo_square.png" - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[11].replacement=src="/jellyseerr/os_logo_square.png" - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[12].regex=href([=:])"/([/a-zA-Z?=]*)" - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[12].replacement=href$1"/jellyseerr/$2" - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[13].regex=linkUrl:"/([/a-zA-Z?=]*)" - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[13].replacement=linkUrl:"/jellyseerr/$1" - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[14].regex="/([a-z]+)/".concat - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[14].replacement="/jellyseerr/$1/".concat - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[15].regex=url:"/([/a-zA-Z?=]*)" - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[15].replacement=url:"/jellyseerr/$1" - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[16].regex=/imageproxy/ - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[16].replacement=/jellyseerr/imageproxy/ - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[17].regex=/avatarproxy/ - traefik.http.middlewares.jellyseerr-rewrite.plugin.rewrite-body.rewrites[17].replacement=/jellyseerr/avatarproxy/ - homepage.group=Media - homepage.name=JellySeerr - homepage.icon=jellyseerr.png - homepage.href=/jellyseerr - homepage.description=Content Recommendations and Request Management - homepage.weight=3 - homepage.widget.type=jellyseerr - homepage.widget.url=http://jellyseerr:5055 - homepage.widget.key=${JELLYSEERR_API_KEY} prowlarr: image: lscr.io/linuxserver/prowlarr:latest container_name: prowlarr environment: - PUID=${USER_ID} - PGID=${GROUP_ID} - TZ=${TIMEZONE} volumes: - ${CONFIG_ROOT:-.}/prowlarr:/config:Z restart: always healthcheck: test: ["CMD", "curl", "--fail", "http://127.0.0.1:9696/prowlarr/ping"] interval: 30s retries: 10 labels: - traefik.enable=true - traefik.http.routers.prowlarr.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/prowlarr`)) - traefik.http.routers.prowlarr.entrypoints=web - traefik.http.services.prowlarr.loadbalancer.server.port=9696 - homepage.group=Download - homepage.name=Prowlarr - homepage.icon=prowlarr.png - homepage.href=/prowlarr - homepage.description=Indexers management - homepage.weight=1 - homepage.widget.type=prowlarr - homepage.widget.url=http://prowlarr:9696/prowlarr - homepage.widget.key=${PROWLARR_API_KEY} flaresolverr: image: 21hsmw/flaresolverr:nodriver container_name: flaresolverr restart: always environment: - LOG_LEVEL=${LOG_LEVEL:-info} - LOG_HTML=${LOG_HTML:-false} - CAPTCHA_SOLVER=${CAPTCHA_SOLVER:-none} - TZ=${TIMEZONE} labels: - traefik.enable=true - traefik.http.routers.flaresolverr.rule=PathPrefix(`/flaresolverr`) - traefik.http.routers.flaresolverr.entrypoints=web - traefik.http.services.flaresolverr.loadbalancer.server.port=8191 profiles: - flaresolverr qbittorrent: image: lscr.io/linuxserver/qbittorrent:libtorrentv1 container_name: qbittorrent environment: - PUID=${USER_ID} - PGID=${GROUP_ID} - TZ=${TIMEZONE} - WEBUI_PORT=8080 - DOCKER_MODS=ghcr.io/gabe565/linuxserver-mod-vuetorrent volumes: - ${CONFIG_ROOT:-.}/qbittorrent:/config:Z - ${DOWNLOAD_ROOT}:/data/torrents:Z restart: always healthcheck: # Container may fail if the PIA's token expired, so mark as unhealthy when there is no internet connection # see: https://github.com/qdm12/gluetun/issues/641#issuecomment-933856220 test: ["CMD", "curl", "--fail", "http://127.0.0.1:8080", "https://google.com"] interval: 30s retries: 10 labels: - traefik.enable=true - traefik.http.routers.qbittorrent.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/qbittorrent`)) - traefik.http.routers.qbittorrent.entrypoints=web - traefik.http.services.qbittorrent.loadbalancer.server.port=8080 - traefik.http.routers.qbittorrent.middlewares=qbittorrent-strip-slash,qbittorrent-stripprefix # https://github.com/qbittorrent/qBittorrent/issues/5693#issuecomment-552146296 - traefik.http.middlewares.qbittorrent-stripprefix.stripPrefix.prefixes=/qbittorrent # https://community.traefik.io/t/middleware-to-add-the-if-needed/1895/19 - traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.regex=(^.*\/qbittorrent$$) - traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.replacement=$$1/ - traefik.http.middlewares.qbittorrent-strip-slash.redirectregex.permanent=false #- com.centurylinklabs.watchtower.depends-on=/vpn - homepage.group=Download - homepage.name=qBittorrent - homepage.icon=qbittorrent.png - homepage.href=/qbittorrent - homepage.description=Bittorrent client - homepage.weight=2 - homepage.widget.type=qbittorrent - homepage.widget.url=http://qbittorrent:8080 - homepage.widget.username=${QBITTORRENT_USERNAME} - homepage.widget.password=${QBITTORRENT_PASSWORD} unpackerr: image: ghcr.io/unpackerr/unpackerr:latest container_name: unpackerr volumes: - ${DOWNLOAD_ROOT}:/data/torrents:Z restart: always user: ${USER_ID}:${GROUP_ID} environment: - TZ=${TIMEZONE} - UN_SONARR_0_URL=http://sonarr:8989/sonarr - UN_SONARR_0_API_KEY=${SONARR_API_KEY} - UN_RADARR_0_URL=http://radarr:7878/radarr - UN_RADARR_0_API_KEY=${RADARR_API_KEY} security_opt: - no-new-privileges:true sabnzbd: image: lscr.io/linuxserver/sabnzbd:latest container_name: sabnzbd environment: - PUID=${USER_ID} - PGID=${GROUP_ID} - TZ=${TIMEZONE} volumes: - ${CONFIG_ROOT:-.}/sabnzbd:/config:Z - ${DATA_ROOT}:/data:Z restart: always labels: - traefik.enable=true - traefik.http.routers.sabnzbd.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/sabnzbd`) || PathPrefix(`/sabnzbd`)) - traefik.http.routers.sabnzbd.entrypoints=web - traefik.http.services.sabnzbd.loadbalancer.server.port=8080 - homepage.group=Download - homepage.name=Sabnzbd - homepage.icon=sabnzbd.png - homepage.href=/sabnzbd - homepage.description=Usenet - homepage.weight=3 - homepage.widget.type=sabnzbd - homepage.widget.url=http://sabnzbd:8080/sabnzbd - homepage.widget.key=${SABNZBD_API_KEY} profiles: - sabnzbd jellyfin: image: lscr.io/linuxserver/jellyfin:latest container_name: jellyfin environment: - PUID=${USER_ID} - PGID=${GROUP_ID} - TZ=${TIMEZONE} - JELLYFIN_PublishedServerUrl=${HOSTNAME}/jellyfin volumes: - ${CONFIG_ROOT:-.}/jellyfin:/config:Z - ${DATA_ROOT}:/data:Z ports: - "7359:7359/udp" - "1900:1900/udp" restart: always healthcheck: test: ["CMD", "curl", "--fail", "http://127.0.0.1:8096/jellyfin/health"] interval: 30s retries: 10 labels: - traefik.enable=true - traefik.http.routers.jellyfin.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/jellyfin`)) - traefik.http.routers.jellyfin.entrypoints=web - traefik.http.services.jellyfin.loadbalancer.server.port=8096 - homepage.group=Media - homepage.name=Jellyfin - homepage.icon=jellyfin.png - homepage.href=/jellyfin - homepage.description=Media server - homepage.weight=4 - homepage.widget.type=jellyfin - homepage.widget.url=http://jellyfin:8096/jellyfin - homepage.widget.key=${JELLYFIN_API_KEY} calibre-web: image: lscr.io/linuxserver/calibre-web:latest container_name: calibre-web environment: - PUID=${USER_ID} - PGID=${GROUP_ID} - TZ=${TIMEZONE} - DOCKER_MODS=linuxserver/mods:universal-calibre - OAUTHLIB_RELAX_TOKEN_SCOPE=1 volumes: - ${CONFIG_ROOT:-.}/calibre-web:/config:Z - ${DATA_ROOT}/books:/books:Z restart: unless-stopped labels: - traefik.enable=true - traefik.http.middlewares.calibre-headers.headers.customRequestHeaders.X-Scheme=https - traefik.http.middlewares.calibre-headers.headers.customRequestHeaders.X-Script-Name=/calibre - traefik.http.middlewares.calibre-stripprefixregex.stripPrefixRegex.regex=/calibre - traefik.http.routers.calibre.middlewares=calibre-headers,calibre-stripprefixregex - traefik.http.routers.calibre.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/calibre`)) - traefik.http.routers.calibre.entrypoints=web - traefik.http.services.calibre.loadbalancer.server.port=8083 - homepage.group=Media - homepage.name=Calibre-Web - homepage.icon=calibre-web.png - homepage.href=/calibre - homepage.description=Books management - homepage.weight=5 - homepage.widget.type=calibreweb - homepage.widget.url=http://calibre-web:8083 - homepage.widget.username=${CALIBRE_USERNAME} - homepage.widget.password=${CALIBRE_PASSWORD} profiles: - calibre-web decluttarr: image: ghcr.io/manimatter/decluttarr:latest container_name: decluttarr restart: always environment: - PUID=${USER_ID} - PGID=${GROUP_ID} - TZ=${TIMEZONE} - RADARR_URL=http://radarr:7878/radarr - RADARR_KEY=${RADARR_API_KEY} - SONARR_URL=http://sonarr:8989/sonarr - SONARR_KEY=${SONARR_API_KEY} - LIDARR_URL=http://lidarr:8686/lidarr - LIDARR_KEY=${LIDARR_API_KEY} - QBITTORRENT_URL=http://qbittorrent:8080 - QBITTORRENT_USERNAME=${QBITTORRENT_USERNAME} - QBITTORRENT_PASSWORD=${QBITTORRENT_PASSWORD} - LOG_LEVEL=${DECLUTTARR_LOG_LEVEL:-INFO} - TEST_RUN=${DECLUTTARR_TEST_RUN:-False} - REMOVE_TIMER=${DECLUTTARR_REMOVE_TIMER:-10} - REMOVE_FAILED=${DECLUTTARR_REMOVE_FAILED:-True} - REMOVE_FAILED_IMPORTS=${DECLUTTARR_REMOVE_FAILED_IMPORTS:-True} - REMOVE_METADATA_MISSING=${DECLUTTARR_REMOVE_METADATA_MISSING:-True} - REMOVE_MISSING_FILES=${DECLUTTARR_REMOVE_MISSING_FILES:-True} - REMOVE_ORPHANS=${DECLUTTARR_REMOVE_ORPHANS:-True} - REMOVE_SLOW=${DECLUTTARR_REMOVE_SLOW:-True} - REMOVE_STALLED=${DECLUTTARR_REMOVE_STALLED:-True} - REMOVE_UNMONITORED=${DECLUTTARR_REMOVE_UNMONITORED:-True} - RUN_PERIODIC_RESCANS=${DECLUTTARR_RUN_PERIODIC_RESCANS:-} - PERMITTED_ATTEMPTS=${DECLUTTARR_PERMITTED_ATTEMPTS:-3} - NO_STALLED_REMOVAL_QBIT_TAG=${DECLUTTARR_REMOVAL_QBIT_TAG:-"stalled"} - MIN_DOWNLOAD_SPEED=${DECLUTTARR_MIN_DOWNLOAD_SPEED:-100} - FAILED_IMPORT_MESSAGE_PATTERNS=${DECLUTTARR_FAILED_IMPORT_MESSAGE_PATTERNS:-} - IGNORED_DOWNLOAD_CLIENTS=${DECLUTTARR_IGNORED_DOWNLOAD_CLIENTS:-} profiles: - decluttarr homepage: image: ghcr.io/gethomepage/homepage:latest container_name: homepage environment: - HOMEPAGE_VAR_TITLE=${HOMEPAGE_VAR_TITLE} - HOMEPAGE_VAR_SEARCH_PROVIDER=${HOMEPAGE_VAR_SEARCH_PROVIDER} - HOMEPAGE_VAR_HEADER_STYLE=${HOMEPAGE_VAR_HEADER_STYLE} - HOMEPAGE_VAR_WEATHER_CITY=${HOMEPAGE_VAR_WEATHER_CITY} - HOMEPAGE_VAR_WEATHER_LAT=${HOMEPAGE_VAR_WEATHER_LAT} - HOMEPAGE_VAR_WEATHER_LONG=${HOMEPAGE_VAR_WEATHER_LONG} - HOMEPAGE_VAR_WEATHER_TIME=${TIMEZONE} - HOMEPAGE_VAR_WEATHER_UNIT=${HOMEPAGE_VAR_WEATHER_UNIT} - HOMEPAGE_ALLOWED_HOSTS=${HOSTNAME} volumes: - ${CONFIG_ROOT:-.}/homepage:/app/config:Z - /var/run/docker.sock:/var/run/docker.sock:ro - ${DATA_ROOT}:/data:Z restart: always command: [sh, -c, "cp -n /app/config/tpl/*.yaml /app/config && node server.js"] labels: - traefik.enable=true - traefik.http.routers.homepage.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/`)) - traefik.http.routers.homepage.entrypoints=web - traefik.http.services.homepage.loadbalancer.server.port=3000 watchtower: image: ghcr.io/containrrr/watchtower:latest container_name: watchtower restart: always environment: - WATCHTOWER_CLEANUP=true volumes: - /var/run/docker.sock:/var/run/docker.sock autoheal: image: willfarrell/autoheal:latest container_name: autoheal restart: always environment: - AUTOHEAL_CONTAINER_LABEL=all volumes: - /var/run/docker.sock:/var/run/docker.sock tailscale: image: tailscale/tailscale:latest container_name: tailscale hostname: ${TAILSCALE_HOSTNAME:-tailscale-nas} # Hostname for Tailscale access environment: TS_AUTHKEY: ${TAILSCALE_AUTHKEY} # Needs to be set in .env TS_EXTRA_ARGS: "--advertise-tags=${TAILSCALE_TAGS:-tag:nas}" # Keep tags if desired TS_STATE_DIR: "/var/lib/tailscale" TS_USERSPACE: "false" # Switch to enable Funnel (public access) or Serve (Tailnet only) ENABLE_FUNNEL_HTTPS: ${ENABLE_FUNNEL_HTTPS:-false} volumes: - ${CONFIG_ROOT:-.}/tailscale/state:/var/lib/tailscale:Z # Persist state - /var/run/docker.sock:/var/run/docker.sock # Optional, keep if needed devices: - /dev/net/tun:/dev/net/tun cap_add: - NET_ADMIN - NET_RAW restart: always command: - /bin/sh - -c - | set -e echo "Starting containerboot (tailscaled)..." /usr/local/bin/containerboot & echo "Waiting for tailscaled to achieve running state..." retries=60 count=0 until tailscale status --json | grep -q '"BackendState": "Running"'; do count=$$(($$count+1)) if [ $$count -gt $$retries ]; then echo "Error: tailscaled did not reach running state after $$retries seconds." exit 1 fi echo -n "." sleep 1 done echo " Tailscaled is running." # --- Start Tailscale Funnel/Serve --- # Check the ENABLE_FUNNEL_HTTPS variable if [ "${ENABLE_FUNNEL_HTTPS}" = "true" ]; then echo "ENABLE_FUNNEL_HTTPS is true. Setting up Funnel -> http://traefik:80..." tailscale funnel --bg http://traefik:80 echo "Tailscale Funnel configured." else echo "ENABLE_FUNNEL_HTTPS is false. Setting up Serve -> http://traefik:80..." tailscale serve --bg http://traefik:80 echo "Tailscale Serve configured." fi # --- End Tailscale Funnel/Serve --- echo "Tailscale forwarding configured. Container will remain running." wait # Wait indefinitely for background processes networks: default: name: docker-compose-nas