Syftet med det här inlägget är egentligen ganska enkelt.

Under ungefär fem års tid har jag mer eller mindre djupdykt i Docker. Jag har testat olika sätt att strukturera containrar, byggt om min miljö flera gånger, brutit saker, löst problem och sakta men säkert hittat ett upplägg som faktiskt fungerar riktigt bra i längden.

Det här är alltså inte en konfiguration som uppstod över en kväll. Den är resultatet av många små förbättringar över tid.

Efter alla dessa iterationer känner jag nu att jag är på en nivå där jag åtminstone “typ” vet vad jag håller på med när det gäller Docker Compose. Inte att allt är perfekt – men tillräckligt stabilt, strukturerat och genomtänkt för att kunna delas med andra.

Förhoppningen är att upplägget kan fungera som inspiration eller utgångspunkt för andra som bygger sina egna self-hostade miljöer. Kanske kan någon plocka upp en idé, ett sätt att strukturera compose-filer, eller hur jag hanterar miljövariabler och labels.

För i slutändan är det ju precis så de flesta av oss lär oss inom homelab-världen.

Genom att titta på hur andra gör – och sedan bygga vidare därifrån.

Låt oss nu kika innuti min .env fil:

RESTART=unless-stopped
DATADIR=/mnt/user/media
APPSDIR=/mnt/user/docker
ENV=/mnt/user/docker/env
WEBHOOK="https://notifiarr.com/api/v1/notification/pullio/<REDACTED>"
NETWORK=docker
HOST=Unraid

Med ovanstående information i beaktning så kan vi ta och kika lite på min nuvarande compose:

### LABELS ###
x-PULLIO: &PULLIO
  org.hotio.pullio.notify: "true"
  org.hotio.pullio.update: "true"
  org.hotio.pullio.generic.webhook: "${WEBHOOK}"

### HARDWARE ###
x-HARDWARE: &HARDWARE
  devices:
    - "/dev/dri/renderD128:/dev/dri/renderD128"

x-QBITTORRENT: &QBITTORRENT
  devices:
    - "/dev/net/tun:/dev/net/tun"

### HEALTHCHECK ###
x-CONTAINERHEALTH: &CONTAINERHEALTH
  healthcheck:
    test: ["CMD-SHELL", "echo 'ok'"]
    interval: "30s"
    timeout: "5s"
    retries: "3"
    start_period: "5s"

x-DOZZLEHEALTH: &DOZZLEHEALTH
    healthcheck:
      test: ["CMD", "/dozzle", "healthcheck"]
      interval: 3s
      timeout: 30s
      retries: 5
      start_period: 30s

x-MARIADBHEALTH: &MARIADBHEALTH
  healthcheck:
    test: ["CMD-SHELL", "mysqladmin ping -u \"$$MYSQL_USER\" -p\"$$MYSQL_PASSWORD\" > /dev/null"]
    interval: "10s"
    timeout: "5s"
    retries: "5"
    start_period: "30s"

x-MYSQLDBHEALTH: &MYSQLDBHEALTH
  healthcheck:
    test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1"]
    interval: "10s"
    timeout: "5s"
    retries: "5"
    start_period: "30s"

x-NOTIFIARRHEALTH: &NOTIFIARRHEALTH
  healthcheck:
    test: ["CMD", "/notifiarr", "--curl", "http://127.0.0.1:5454/"]
    interval: "60s"
    timeout: "10s"
    retries: "3"
    start_period: "20s"

x-POSTGRESDBHEALTH: &POSTGRESDBHEALTH
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\""]
    interval: "10s"
    timeout: "5s"
    retries: "5"
    start_period: "5s"

x-REDISDBHEALTH: &REDISDBHEALTH
  healthcheck:
    test: ["CMD-SHELL", "redis-cli ping || exit 1"]
    interval: "10s"
    timeout: "5s"
    retries: "5"
    start_period: "5s"

### NETWORKS ###
x-HOST: &HOST
  hostname: "${HOST}"
  network_mode: "host"
  restart: "${RESTART}"

x-NETWORK: &NETWORK
  hostname: "${HOST}"
  networks:
    - "${NETWORK}"
  restart: "${RESTART}"

### CONTAINERS ###
name: Unraid
services:
  adminer:
    image: adminer
    container_name: adminer
    env_file: ${ENV}/adminer.env
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  authelia:
    image: authelia/authelia
    container_name: authelia
    env_file: ${ENV}/authelia.env
    depends_on:
      redis:
        condition: "service_healthy"
    volumes:
      - "${APPSDIR}/authelia:/config:rw"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  bazarr:
    image: ghcr.io/hotio/bazarr:latest
    container_name: bazarr
    env_file: ${ENV}/bazarr.env
    volumes:
      - "${APPSDIR}/bazarr:/config:rw"
      - "${DATADIR}:/data:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  binternet:
    image: ghcr.io/ahwxorg/binternet:latest
    container_name: binternet
    env_file: ${ENV}/binternet.env
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  bytestash:
    image: ghcr.io/jordan-dalby/bytestash:latest
    container_name: bytestash
    env_file: ${ENV}/bytestash.env
    volumes:
      - "${APPSDIR}/bytestash:/data/snippets:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  changedetection:
    image: lscr.io/linuxserver/changedetection.io
    container_name: changedetection
    env_file: ${ENV}/changedetection.env
    volumes:
      - "${APPSDIR}/changedetection:/config:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  cloudflareddns:
    image: ghcr.io/hotio/cloudflareddns:latest
    container_name: cloudflareddns
    env_file: ${ENV}/cloudflareddns.env
    volumes:
      - "${APPSDIR}/cloudflareddns:/config:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  code_server:
    image: lscr.io/linuxserver/code-server
    container_name: code_server
    env_file: ${ENV}/code_server.env
    volumes:
      - "${APPSDIR}/code_server:/config:rw"
      - "${APPSDIR}:/docker:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  dozzle:
    image: amir20/dozzle
    container_name: dozzle
    env_file: ${ENV}/dozzle.env
    volumes:
      - "${APPSDIR}/dozzle:/data:rw"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    <<: [*NETWORK, *DOZZLEHEALTH]
    labels:
      <<: *PULLIO

  factorio:
    image: factoriotools/factorio:latest
    container_name: factorio
    ports:
      - "27015:27015"
      - "34197:34197"
    env_file: ${ENV}/factorio.env
    volumes:
      - "${APPSDIR}/factorio:/factorio:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  foundry:
    image: felddy/foundryvtt:13
    container_name: foundry
    env_file: ${ENV}/foundry.env
    volumes:
      - "${APPSDIR}/foundry:/data:rw"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  filebrowser:
    image: ghcr.io/binhex/arch-filebrowser
    container_name: filebrowser
    env_file: ${ENV}/filebrowser.env
    volumes:
      - "${APPSDIR}:/media:rw"
      - "${APPSDIR}/filebrowser:/config:rw"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  freshrss:
    image: lscr.io/linuxserver/freshrss
    container_name: freshrss
    env_file: ${ENV}/freshrss.env
    depends_on:
      freshrss_db:
        condition: "service_healthy"
    volumes:
      - "${APPSDIR}/freshrss:/config:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  freshrss_db:
    image: postgres:16
    container_name: freshrss_db
    env_file: ${ENV}/freshrss_db.env
    volumes:
      - "${APPSDIR}/freshrss_db:/var/lib/postgresql/data:rw"
    <<: [*NETWORK, *POSTGRESDBHEALTH]
    labels:
      <<: *PULLIO

  ghost:
    image: ghost:latest
    container_name: ghost
    env_file: ${ENV}/ghost.env
    depends_on:
      ghost_db:
        condition: "service_healthy"
    volumes:
      - "${APPSDIR}/ghost/content:/var/lib/ghost/content:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  ghost_db:
    image: mysql:8
    container_name: ghost_db
    env_file: ${ENV}/ghost_db.env
    volumes:
      - "${APPSDIR}/ghost_db:/var/lib/mysql:rw"
    <<: [*NETWORK, *MYSQLDBHEALTH]
    labels:
      <<: *PULLIO

  hishtory:
    image: lscr.io/linuxserver/hishtory-server
    container_name: hishtory
    env_file: ${ENV}/hishtory.env
    depends_on:
      hishtory_db:
        condition: "service_healthy"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  hishtory_db:
    image: postgres:16
    container_name: hishtory_db
    env_file: ${ENV}/hishtory_db.env
    volumes:
      - "${APPSDIR}/hishtory_db:/var/lib/postgresql/data:rw"
    <<: [*NETWORK, *POSTGRESDBHEALTH]
    labels:
      <<: *PULLIO

  homebox:
    image: ghcr.io/sysadminsmedia/homebox:latest
    container_name: homebox
    env_file: ${ENV}/homebox.env
    volumes:
      - "${APPSDIR}/homebox:/data:rw"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  homebox_companion:
    image: ghcr.io/duelion/homebox-companion:latest
    container_name: homebox_companion
    env_file: ${ENV}/homebox_companion.env
    depends_on:
      homebox:
        condition: "service_healthy"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  it_tools:
    image: sharevb/it-tools:latest
    container_name: it_tools
    env_file: ${ENV}/it_tools.env
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  immich:
    image: ghcr.io/imagegenius/immich
    container_name: immich
    env_file: ${ENV}/immich.env
    depends_on:
      immich_db:
        condition: "service_healthy"
      immich_machinelearning:
        condition: "service_healthy"
      redis:
        condition: "service_healthy"
    volumes:
      - "${DATADIR}/photos:/photos:rw"
      - "${APPSDIR}/immich:/config:rw"
    <<: [*NETWORK, *CONTAINERHEALTH, *HARDWARE]
    labels:
      <<: *PULLIO

  immich_db:
    image: ghcr.io/immich-app/postgres:16-vectorchord0.3.0-pgvectors0.3.0
    container_name: immich_db
    env_file: ${ENV}/immich_db.env
    volumes:
      - "${APPSDIR}/immich_db:/var/lib/postgresql/data:rw"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  immich_machinelearning:
    image: ghcr.io/immich-app/immich-machine-learning:release
    container_name: immich_machinelearning
    env_file: ${ENV}/immich_machinelearning.env
    volumes:
      - "${APPSDIR}/machinelearning:/cache:rw"
    <<: [*NETWORK, *HARDWARE]
    labels:
      <<: *PULLIO

  immich_power_tools:
    image: ghcr.io/varun-raj/immich-power-tools
    container_name: immich_power_tools
    env_file: ${ENV}/immich_power_tools.env
    depends_on:
      immich:
        condition: "service_healthy"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  joplin:
    image: joplin/server:latest
    container_name: joplin
    env_file: ${ENV}/joplin.env
    depends_on:
      joplin_db:
        condition: "service_healthy"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  joplin_db:
    image: postgres:16
    container_name: joplin_db
    env_file: ${ENV}/joplin_db.env
    volumes:
      - "${APPSDIR}/joplin_db:/var/lib/postgresql/data:rw"
    <<: [*NETWORK, *POSTGRESDBHEALTH]
    labels:
      <<: *PULLIO

  jotty:
    image: ghcr.io/fccview/jotty:latest
    container_name: jotty
    env_file: ${ENV}/jotty.env
    volumes:
      - "${APPSDIR}/jotty/cache:/app/.next/cache:rw"
      - "${APPSDIR}/jotty/config:/app/config:rw"
      - "${APPSDIR}/jotty/data:/app/data:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  kometa:
    image: lscr.io/linuxserver/kometa
    container_name: kometa
    env_file: ${ENV}/kometa.env
    depends_on:
      plex:
        condition: "service_healthy"
    volumes:
      - "${DATADIR}:/data:rw"
      - "${APPSDIR}/kometa:/config:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  libremdb:
    image: quay.io/pussthecatorg/libremdb:latest
    container_name: libremdb
    env_file: ${ENV}/libremdb.env
    depends_on:
      redis:
        condition: "service_healthy"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  maintainerr:
    image: jorenn92/maintainerr:latest
    container_name: maintainerr
    env_file: ${ENV}/maintainerr.env
    volumes:
      - "${APPSDIR}/maintainerr:/opt/data:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  n8n:
    image: n8nio/n8n
    container_name: n8n
    env_file: ${ENV}/n8n.env
    depends_on:
      n8n_db:
        condition: "service_healthy"
      redis:
        condition: "service_healthy"
    volumes:
      - "${APPSDIR}/n8n:/home/node/.n8n:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  n8n_db:
    image: postgres:16
    container_name: n8n_db
    env_file: ${ENV}/n8n_db.env
    volumes:
      - "${APPSDIR}/n8n_db:/var/lib/postgresql/data:rw"
    <<: [*NETWORK, *POSTGRESDBHEALTH]
    labels:
      <<: *PULLIO

  nextcloud:
    image: lscr.io/linuxserver/nextcloud
    container_name: nextcloud
    env_file: ${ENV}/nextcloud.env
    depends_on:
      nextcloud_db:
        condition: "service_healthy"
    volumes:
      - "${APPSDIR}/nextcloud/config:/config:rw"
      - "${APPSDIR}/nextcloud/data:/data:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  nextcloud_db:
    image: postgres:16
    container_name: nextcloud_db
    env_file: ${ENV}/nextcloud_db.env
    volumes:
      - "${APPSDIR}/nextcloud_db:/var/lib/postgresql/data:rw"
    <<: [*NETWORK, *POSTGRESDBHEALTH]
    labels:
      <<: *PULLIO

  nginx_proxy_manager:
    image: jc21/nginx-proxy-manager
    container_name: nginx_proxy_manager
    ports:
      - "3000:3000"
      - "443:443"
      - "80:80"
      - "81:81"
    env_file: ${ENV}/nginx_proxy_manager.env
    volumes:
      - "${APPSDIR}/nginx_proxy_manager/letsencrypt:/etc/letsencrypt:rw"
      - "/tmp/nginx_proxy_manager/var/log:/var/log:rw"
      - "${APPSDIR}/nginx_proxy_manager/data:/data:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  notifiarr:
    image: golift/notifiarr
    container_name: notifiarr
    env_file: ${ENV}/notifiarr.env
    volumes:
      - "${APPSDIR}/notifiarr:/config:rw"
      - "/var/run/utmp:/var/run/utmp:ro"
      - "/etc/machine-id:/etc/machine-id:ro"
      - "/mnt/disk1:/storage/1:ro"
    <<: [*NETWORK, *NOTIFIARRHEALTH]
    labels:
      <<: *PULLIO

  open_webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open_webui
    env_file: ${ENV}/open_webui.env
    volumes:
      - "${APPSDIR}/open_webui:/app/backend/data:rw"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  paperless:
    image: ghcr.io/paperless-ngx/paperless-ngx
    container_name: paperless
    env_file: ${ENV}/paperless.env
    depends_on:
      paperless_db:
        condition: "service_healthy"
      redis:
        condition: "service_healthy"
    volumes:
      - "${APPSDIR}/paperless/media:/usr/src/paperless/media:rw"
      - "${APPSDIR}/paperless/consume:/usr/src/paperless/consume:rw"
      - "${APPSDIR}/paperless/export:/usr/src/paperless/export:rw"
      - "${APPSDIR}/paperless/data:/usr/src/paperless/data:rw"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  paperless_db:
    image: postgres:16
    container_name: paperless_db
    env_file: ${ENV}/paperless_db.env
    volumes:
      - "${APPSDIR}/paperless_db:/var/lib/postgresql/data:rw"
    <<: [*NETWORK, *POSTGRESDBHEALTH]
    labels:
      <<: *PULLIO

  plex:
    image: plexinc/pms-docker
    container_name: plex
    env_file: ${ENV}/plex.env
    volumes:
      - "/tmp:/transcode:rw"
      - "${DATADIR}:/data:rw"
      - "${APPSDIR}/plex:/config:rw"
    <<: [*HOST, *HARDWARE]
    labels:
      <<: *PULLIO

  privatebin:
    image: privatebin/nginx-fpm-alpine
    container_name: privatebin
    env_file: ${ENV}/privatebin.env
    volumes:
      - "${APPSDIR}/privatebin/run:/run:rw"
      - "${APPSDIR}/privatebin/data:/srv/data:rw"
      - "${APPSDIR}/privatebin/tmp:/tmp:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  protonmail_bridge:
    image: shenxn/protonmail-bridge
    container_name: protonmail_bridge
    env_file: ${ENV}/protonmail_bridge.env
    volumes:
      - "${APPSDIR}/protonmail_bridge:/root:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  prowlarr:
    image: ghcr.io/hotio/prowlarr:latest
    container_name: prowlarr
    env_file: ${ENV}/prowlarr.env
    volumes:
      - "${APPSDIR}/prowlarr:/config:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  qbittorrent:
    image: ghcr.io/hotio/qbittorrent:latest
    container_name: qbittorrent
    env_file: ${ENV}/qbittorrent.env
    dns:
      - 1.1.1.1
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
      - net.ipv6.conf.all.disable_ipv6=1
    cap_add:
      - NET_ADMIN
    volumes:
      - "${APPSDIR}/qbittorrent:/config:rw"
      - "${DATADIR}:/data:rw"
    <<: [*NETWORK, *CONTAINERHEALTH, *QBITTORRENT]
    labels:
      <<: *PULLIO

  qui:
    image: ghcr.io/autobrr/qui:latest
    container_name: qui
    env_file: ${ENV}/qui.env
    depends_on:
      qbittorrent:
        condition: "service_healthy"
    volumes:
      - "${APPSDIR}/qui:/config:rw"
      - "${DATADIR}:/data:rw"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  radarr:
    image: ghcr.io/hotio/radarr:latest
    container_name: radarr
    env_file: ${ENV}/radarr.env
    volumes:
      - "${APPSDIR}/radarr:/config:rw"
      - "${DATADIR}:/data:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  reactive:
    image: amruthpillai/reactive-resume:latest
    container_name: reactive
    env_file: ${ENV}/reactive.env
    depends_on:
      reactive_db:
        condition: "service_healthy"
      reactive_printer:
        condition: "service_healthy"
    volumes:
      - "${APPSDIR}/reactive:/app/data:rw"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  reactive_db:
    image: postgres:16
    container_name: reactive_db
    env_file: ${ENV}/reactive_db.env
    volumes:
      - "${APPSDIR}/reactive_db:/var/lib/postgresql/data:rw"
    <<: [*NETWORK, *POSTGRESDBHEALTH]
    labels:
      <<: *PULLIO

  reactive_printer:
    image: ghcr.io/browserless/chromium:latest
    container_name: reactive_printer
    env_file: ${ENV}/reactive_printer.env
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  redis:
    image: redis
    container_name: redis
    env_file: ${ENV}/redis.env
    <<: [*NETWORK, *REDISDBHEALTH]
    labels:
      <<: *PULLIO

  redlib:
    image: quay.io/redlib/redlib:latest
    container_name: redlib
    env_file: ${ENV}/redlib.env
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  searxng:
    image: searxng/searxng
    container_name: searxng
    env_file: ${ENV}/searxng.env
    volumes:
      - "${APPSDIR}/searxng:/etc/searxng"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  seerr:
    image: ghcr.io/hotio/seerr:latest
    container_name: seerr
    env_file: ${ENV}/seerr.env
    depends_on:
      seerr_db:
        condition: "service_healthy"
    volumes:
      - "${APPSDIR}/seerr:/config:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  seerr_db:
    image: postgres:16
    container_name: seerr_db
    env_file: ${ENV}/seerr_db.env
    volumes:
      - "${APPSDIR}/seerr_db:/var/lib/postgresql/data:rw"
    <<: [*NETWORK, *POSTGRESDBHEALTH]
    labels:
      <<: *PULLIO

  sonarr:
    image: ghcr.io/hotio/sonarr:latest
    container_name: sonarr
    env_file: ${ENV}/sonarr.env
    volumes:
      - "${APPSDIR}/sonarr:/config:rw"
      - "${DATADIR}:/data:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  sons_of_the_forest:
    image: jammsen/sons-of-the-forest-dedicated-server:latest
    container_name: sons_of_the_forest
    ports:
      - "27016:27016"
      - "8766:8766"
      - "9700:9700"
    env_file: ${ENV}/sons_of_the_forest.env
    volumes:
      - "${APPSDIR}/sons_of_the_forest:/sonsoftheforest:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  stirlingpdf:
    image: stirlingtools/stirling-pdf
    container_name: stirlingpdf
    env_file: ${ENV}/stirlingpdf.env
    volumes:
      - "${APPSDIR}/stirlingpdf/configs:/configs:rw"
      - "${APPSDIR}/stirlingpdf/customFiles:/customFiles:rw"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  tandoor:
    image: vabene1111/recipes
    container_name: tandoor
    env_file: ${ENV}/tandoor.env
    depends_on:
      tandoor_db:
        condition: "service_healthy"
    volumes:
      - "${APPSDIR}/tandoor/mediafiles:/opt/recipes/mediafiles:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  tandoor_db:
    image: postgres:16
    container_name: tandoor_db
    env_file: ${ENV}/tandoor_db.env
    volumes:
      - "${APPSDIR}/tandoor_db:/var/lib/postgresql/data:rw"
    <<: [*NETWORK, *POSTGRESDBHEALTH]
    labels:
      <<: *PULLIO

  tautulli:
    image: ghcr.io/hotio/tautulli
    container_name: tautulli
    env_file: ${ENV}/tautulli.env
    volumes:
      - "${APPSDIR}/tautulli:/config"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  unpackerr:
    image: ghcr.io/hotio/unpackerr:latest
    container_name: unpackerr
    env_file: ${ENV}/unpackerr.env
    volumes:
      - "${APPSDIR}/unpackerr:/config:rw"
      - "${DATADIR}:/data:rw"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  uptime_kuma:
    image: louislam/uptime-kuma
    container_name: uptime_kuma
    env_file: ${ENV}/uptime_kuma.env
    volumes:
      - "${APPSDIR}/uptime_kuma:/app/data:rw"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  wallos:
    image: bellamy/wallos:latest
    container_name: wallos
    env_file: ${ENV}/wallos.env
    volumes:
      - "${APPSDIR}/wallos/db:/var/www/html/db:rw"
      - "${APPSDIR}/wallos/logos:/var/www/html/images/uploads/logos:rw"
    <<: [*NETWORK]
    labels:
      <<: *PULLIO

  webcheck:
    image: lissy93/web-check
    container_name: webcheck
    env_file: ${ENV}/webcheck.env
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

  wizarr:
    image: ghcr.io/wizarrrr/wizarr
    container_name: wizarr
    env_file: ${ENV}/wizarr.env
    volumes:
      - "${APPSDIR}/wizarr:/data"
    <<: [*NETWORK, *CONTAINERHEALTH]
    labels:
      <<: *PULLIO

networks:
  docker:
    external: true
    name: ${NETWORK}

Jag delar min nuvarande Docker Compose-konfiguration eftersom den är byggd på ett sätt som gör den säker att publicera. All känslig information, som API-nycklar, lösenord och andra hemligheter, ligger inte direkt i compose-filen utan är istället placerade i separata .env-filer.

Det gör att själva konfigurationen kan delas öppet utan att någon känslig information exponeras. Samtidigt blir det också mycket enklare för andra att använda samma upplägg, eftersom de bara behöver skapa sina egna .env-filer med sina egna värden.

Om du tittar igenom composen rekommenderar jag att du tar en extra titt på labels-sektionen högst upp. Där har jag lagt till saker som exempelvis hardware-labels, healthchecks och andra små detaljer som gör det enklare att strukturera och övervaka containrarna.

Det är inga avancerade saker i sig, men tillsammans gör de att miljön blir mer organiserad, mer robust och enklare att felsöka.

På så sätt blir konfigurationen både säker, portabel och enkel att dela med andra.