Fix critical discrepancies found during validation audit

- Fix cert resolver name (cloudflare -> letsencrypt)
- Fix volume paths (/mnt/nas -> /data)
- Add missing custom port mappings
- Add privileged/capability flags
- Add 8 new roles: portainer, n8n, nodered, influxdb, guacamole, watchtower, rtlamr2mqtt, telegraf
- Fix Traefik template (ports, ACME path, wildcard cert)
- Add extra volume mounts for media services
This commit is contained in:
2026-02-01 23:54:39 -06:00
parent b9be45dc71
commit 50829af421
38 changed files with 368 additions and 36 deletions
@@ -3,6 +3,8 @@ services:
image: {{ adguard_image }}
container_name: adguard
restart: unless-stopped
cap_add:
- NET_ADMIN
ports:
- "53:53/tcp"
- "53:53/udp"
@@ -14,7 +16,7 @@ services:
traefik.enable: "true"
traefik.http.routers.adguard.rule: "Host(`{{ adguard_host }}`)"
traefik.http.routers.adguard.entrypoints: websecure
traefik.http.routers.adguard.tls.certresolver: cloudflare
traefik.http.routers.adguard.tls.certresolver: letsencrypt
traefik.http.services.adguard.loadbalancer.server.port: "3000"
networks:
- {{ docker_network_name }}
+1 -1
View File
@@ -4,6 +4,6 @@ backup_schedule: "0 2 * * *"
backup_retention_days: 30
backup_sources:
- /data/docker-compose
backup_destination: /mnt/nas/backups/{{ inventory_hostname }}
backup_destination: /data/backups/{{ inventory_hostname }}
backup_pre_commands: []
backup_post_commands: []
@@ -20,7 +20,7 @@ services:
traefik.enable: "true"
traefik.http.routers.frigate.rule: "Host(`{{ frigate_host }}`)"
traefik.http.routers.frigate.entrypoints: websecure
traefik.http.routers.frigate.tls.certresolver: cloudflare
traefik.http.routers.frigate.tls.certresolver: letsencrypt
traefik.http.services.frigate.loadbalancer.server.port: "5000"
networks:
- {{ docker_network_name }}
+7
View File
@@ -0,0 +1,7 @@
---
guacamole_compose_dir: "{{ docker_compose_base }}/guacamole"
guacamole_image: "guacamole/guacamole"
guacd_image: "guacamole/guacd"
guacamole_db_image: "mysql:8.0"
guacamole_db_password: "{{ vault_guacamole_db_password | default('changeme') }}"
guacamole_host: "solo.{{ domain }}"
+14
View File
@@ -0,0 +1,14 @@
---
- name: Create guacamole compose directory
file:
path: "{{ guacamole_compose_dir }}"
state: directory
- name: Deploy guacamole compose file
template:
src: docker-compose.yml.j2
dest: "{{ guacamole_compose_dir }}/docker-compose.yml"
notify: restart guacamole
- name: Start guacamole
community.docker.docker_compose_v2:
project_src: "{{ guacamole_compose_dir }}"
state: present
@@ -0,0 +1,54 @@
services:
guacd:
image: {{ guacd_image }}
container_name: guacd
restart: unless-stopped
volumes:
- /data/guacamole/record:/record
- /data/guacamole:/drive
networks:
- guacamole_internal
- {{ docker_network_name }}
guacamole-db:
image: {{ guacamole_db_image }}
container_name: guacamole-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: {{ guacamole_db_password }}
MYSQL_DATABASE: guacamole_db
MYSQL_USER: guacamole
MYSQL_PASSWORD: {{ guacamole_db_password }}
volumes:
- /data/guacamole/mysql:/var/lib/mysql
networks:
- guacamole_internal
guacamole:
image: {{ guacamole_image }}
container_name: guacamole
restart: unless-stopped
environment:
GUACD_HOSTNAME: guacd
MYSQL_HOSTNAME: guacamole-db
MYSQL_DATABASE: guacamole_db
MYSQL_USER: guacamole
MYSQL_PASSWORD: {{ guacamole_db_password }}
depends_on:
- guacd
- guacamole-db
labels:
traefik.enable: "true"
traefik.http.routers.guacamole.rule: "Host(`{{ guacamole_host }}`)"
traefik.http.routers.guacamole.entrypoints: websecure
traefik.http.routers.guacamole.tls.certresolver: letsencrypt
traefik.http.services.guacamole.loadbalancer.server.port: "8080"
networks:
- guacamole_internal
- {{ docker_network_name }}
networks:
guacamole_internal:
driver: bridge
{{ docker_network_name }}:
external: true
@@ -12,7 +12,7 @@ services:
traefik.enable: "true"
traefik.http.routers.ha.rule: "Host(`{{ ha_host }}`)"
traefik.http.routers.ha.entrypoints: websecure
traefik.http.routers.ha.tls.certresolver: cloudflare
traefik.http.routers.ha.tls.certresolver: letsencrypt
traefik.http.services.ha.loadbalancer.server.port: "8123"
networks:
- {{ docker_network_name }}
@@ -21,6 +21,7 @@ services:
image: {{ esphome_image }}
container_name: esphome
restart: unless-stopped
privileged: true
environment:
TZ: {{ timezone }}
volumes:
@@ -29,7 +30,7 @@ services:
traefik.enable: "true"
traefik.http.routers.esphome.rule: "Host(`{{ esphome_host }}`)"
traefik.http.routers.esphome.entrypoints: websecure
traefik.http.routers.esphome.tls.certresolver: cloudflare
traefik.http.routers.esphome.tls.certresolver: letsencrypt
traefik.http.services.esphome.loadbalancer.server.port: "6052"
networks:
- {{ docker_network_name }}
+3
View File
@@ -0,0 +1,3 @@
---
influxdb_compose_dir: "{{ docker_compose_base }}/influxdb"
influxdb_image: "influxdb:1.8-alpine"
+14
View File
@@ -0,0 +1,14 @@
---
- name: Create influxdb compose directory
file:
path: "{{ influxdb_compose_dir }}"
state: directory
- name: Deploy influxdb compose file
template:
src: docker-compose.yml.j2
dest: "{{ influxdb_compose_dir }}/docker-compose.yml"
notify: restart influxdb
- name: Start influxdb
community.docker.docker_compose_v2:
project_src: "{{ influxdb_compose_dir }}"
state: present
@@ -0,0 +1,13 @@
services:
influxdb:
image: {{ influxdb_image }}
container_name: influxdb
restart: unless-stopped
volumes:
- /data/influxdb:/var/lib/influxdb
networks:
- {{ docker_network_name }}
networks:
{{ docker_network_name }}:
external: true
+1 -1
View File
@@ -12,7 +12,7 @@ services:
traefik.enable: "true"
traefik.http.routers.synapse.rule: "Host(`{{ matrix_host }}`)"
traefik.http.routers.synapse.entrypoints: websecure
traefik.http.routers.synapse.tls.certresolver: cloudflare
traefik.http.routers.synapse.tls.certresolver: letsencrypt
traefik.http.services.synapse.loadbalancer.server.port: "8008"
networks:
- {{ docker_network_name }}
+3 -3
View File
@@ -4,7 +4,7 @@ jellyfin_data_dir: /data/jellyfin
sonarr_data_dir: /data/sonarr
radarr_data_dir: /data/radarr
sabnzbd_data_dir: /data/sabnzbd
nzbhydra_data_dir: /data/nzbhydra2
nzbhydra_data_dir: /data/hydra
jellyseerr_data_dir: /data/jellyseerr
jellyfin_image: "jellyfin/jellyfin:latest"
@@ -14,8 +14,8 @@ sabnzbd_image: "lscr.io/linuxserver/sabnzbd:latest"
nzbhydra_image: "lscr.io/linuxserver/nzbhydra2:latest"
jellyseerr_image: "fallenbagel/jellyseerr:latest"
media_path: /mnt/nas/media
downloads_path: /mnt/nas/downloads
media_path: /data/media
downloads_path: /data/media/downloads
jellyfin_host: "jellyfin.{{ domain }}"
sonarr_host: "sonarr.{{ domain }}"
+16 -8
View File
@@ -13,7 +13,7 @@ services:
traefik.enable: "true"
traefik.http.routers.jellyfin.rule: "Host(`{{ jellyfin_host }}`)"
traefik.http.routers.jellyfin.entrypoints: websecure
traefik.http.routers.jellyfin.tls.certresolver: cloudflare
traefik.http.routers.jellyfin.tls.certresolver: letsencrypt
traefik.http.services.jellyfin.loadbalancer.server.port: "8096"
networks:
- {{ docker_network_name }}
@@ -27,14 +27,15 @@ services:
PGID: "{{ pgid }}"
TZ: {{ timezone }}
volumes:
- {{ sonarr_data_dir }}:/config
- /data/sonarr/config:/config
- {{ media_path }}/tv:/tv
- {{ downloads_path }}:/downloads
- /data/media/ssd/tv_down:/tv_down
labels:
traefik.enable: "true"
traefik.http.routers.sonarr.rule: "Host(`{{ sonarr_host }}`)"
traefik.http.routers.sonarr.entrypoints: websecure
traefik.http.routers.sonarr.tls.certresolver: cloudflare
traefik.http.routers.sonarr.tls.certresolver: letsencrypt
traefik.http.services.sonarr.loadbalancer.server.port: "8989"
networks:
- {{ docker_network_name }}
@@ -48,14 +49,16 @@ services:
PGID: "{{ pgid }}"
TZ: {{ timezone }}
volumes:
- {{ radarr_data_dir }}:/config
- /data/radarr/config:/config
- {{ media_path }}/movies:/movies
- {{ downloads_path }}:/downloads
- /data/media:/media
- /data/media/ssd/movies_down:/movies_down
- /data:/data
labels:
traefik.enable: "true"
traefik.http.routers.radarr.rule: "Host(`{{ radarr_host }}`)"
traefik.http.routers.radarr.entrypoints: websecure
traefik.http.routers.radarr.tls.certresolver: cloudflare
traefik.http.routers.radarr.tls.certresolver: letsencrypt
traefik.http.services.radarr.loadbalancer.server.port: "7878"
networks:
- {{ docker_network_name }}
@@ -68,14 +71,19 @@ services:
PUID: "{{ puid }}"
PGID: "{{ pgid }}"
TZ: {{ timezone }}
ports:
- "8082:8080"
- "9092:9090"
volumes:
- {{ sabnzbd_data_dir }}:/config
- {{ downloads_path }}:/downloads
- /data/media/ssd/movies_down:/movies_down
- /data/media/ssd/tv_down:/tv_down
labels:
traefik.enable: "true"
traefik.http.routers.sabnzbd.rule: "Host(`{{ sabnzbd_host }}`)"
traefik.http.routers.sabnzbd.entrypoints: websecure
traefik.http.routers.sabnzbd.tls.certresolver: cloudflare
traefik.http.routers.sabnzbd.tls.certresolver: letsencrypt
traefik.http.services.sabnzbd.loadbalancer.server.port: "8080"
networks:
- {{ docker_network_name }}
@@ -106,7 +114,7 @@ services:
traefik.enable: "true"
traefik.http.routers.jellyseerr.rule: "Host(`{{ jellyseerr_host }}`)"
traefik.http.routers.jellyseerr.entrypoints: websecure
traefik.http.routers.jellyseerr.tls.certresolver: cloudflare
traefik.http.routers.jellyseerr.tls.certresolver: letsencrypt
traefik.http.services.jellyseerr.loadbalancer.server.port: "5055"
networks:
- {{ docker_network_name }}
@@ -3,6 +3,8 @@ services:
image: {{ prometheus_image }}
container_name: prometheus
restart: unless-stopped
ports:
- "9095:9090"
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
@@ -15,7 +17,7 @@ services:
traefik.enable: "true"
traefik.http.routers.prometheus.rule: "Host(`{{ prometheus_host }}`)"
traefik.http.routers.prometheus.entrypoints: websecure
traefik.http.routers.prometheus.tls.certresolver: cloudflare
traefik.http.routers.prometheus.tls.certresolver: letsencrypt
traefik.http.services.prometheus.loadbalancer.server.port: "9090"
networks:
- {{ docker_network_name }}
@@ -24,6 +26,8 @@ services:
image: {{ grafana_image }}
container_name: grafana
restart: unless-stopped
ports:
- "3001:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: {{ grafana_admin_password }}
GF_SERVER_ROOT_URL: "https://{{ grafana_host }}"
@@ -34,7 +38,7 @@ services:
traefik.enable: "true"
traefik.http.routers.grafana.rule: "Host(`{{ grafana_host }}`)"
traefik.http.routers.grafana.entrypoints: websecure
traefik.http.routers.grafana.tls.certresolver: cloudflare
traefik.http.routers.grafana.tls.certresolver: letsencrypt
traefik.http.services.grafana.loadbalancer.server.port: "3000"
networks:
- {{ docker_network_name }}
@@ -43,6 +47,8 @@ services:
image: {{ alertmanager_image }}
container_name: alertmanager
restart: unless-stopped
ports:
- "9093:9093"
command:
- --config.file=/etc/alertmanager/alertmanager.yml
volumes:
@@ -51,7 +57,7 @@ services:
traefik.enable: "true"
traefik.http.routers.alertmanager.rule: "Host(`{{ alertmanager_host }}`)"
traefik.http.routers.alertmanager.entrypoints: websecure
traefik.http.routers.alertmanager.tls.certresolver: cloudflare
traefik.http.routers.alertmanager.tls.certresolver: letsencrypt
traefik.http.services.alertmanager.loadbalancer.server.port: "9093"
networks:
- {{ docker_network_name }}
@@ -60,6 +66,8 @@ services:
image: {{ node_exporter_image }}
container_name: node-exporter
restart: unless-stopped
ports:
- "9100:9100"
command:
- --path.rootfs=/host
volumes:
+3
View File
@@ -0,0 +1,3 @@
---
n8n_compose_dir: "{{ docker_compose_base }}/n8n"
n8n_image: "docker.n8n.io/n8nio/n8n"
+14
View File
@@ -0,0 +1,14 @@
---
- name: Create n8n compose directory
file:
path: "{{ n8n_compose_dir }}"
state: directory
- name: Deploy n8n compose file
template:
src: docker-compose.yml.j2
dest: "{{ n8n_compose_dir }}/docker-compose.yml"
notify: restart n8n
- name: Start n8n
community.docker.docker_compose_v2:
project_src: "{{ n8n_compose_dir }}"
state: present
+15
View File
@@ -0,0 +1,15 @@
services:
n8n:
image: {{ n8n_image }}
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
volumes:
- /data/n8n:/home/node/.n8n
networks:
- {{ docker_network_name }}
networks:
{{ docker_network_name }}:
external: true
+3
View File
@@ -0,0 +1,3 @@
---
nodered_compose_dir: "{{ docker_compose_base }}/nodered"
nodered_image: "nodered/node-red:latest"
+14
View File
@@ -0,0 +1,14 @@
---
- name: Create nodered compose directory
file:
path: "{{ nodered_compose_dir }}"
state: directory
- name: Deploy nodered compose file
template:
src: docker-compose.yml.j2
dest: "{{ nodered_compose_dir }}/docker-compose.yml"
notify: restart nodered
- name: Start nodered
community.docker.docker_compose_v2:
project_src: "{{ nodered_compose_dir }}"
state: present
@@ -0,0 +1,15 @@
services:
nodered:
image: {{ nodered_image }}
container_name: nodered
restart: unless-stopped
ports:
- "1880:1880"
volumes:
- /data/nodered:/data
networks:
- {{ docker_network_name }}
networks:
{{ docker_network_name }}:
external: true
@@ -12,7 +12,7 @@ services:
traefik.enable: "true"
traefik.http.routers.openclaw.rule: "Host(`{{ openclaw_host }}`)"
traefik.http.routers.openclaw.entrypoints: websecure
traefik.http.routers.openclaw.tls.certresolver: cloudflare
traefik.http.routers.openclaw.tls.certresolver: letsencrypt
traefik.http.services.openclaw.loadbalancer.server.port: "3000"
networks:
- {{ docker_network_name }}
+3
View File
@@ -0,0 +1,3 @@
---
portainer_compose_dir: "{{ docker_compose_base }}/portainer"
portainer_image: "portainer/portainer-ce:latest"
+14
View File
@@ -0,0 +1,14 @@
---
- name: Create portainer compose directory
file:
path: "{{ portainer_compose_dir }}"
state: directory
- name: Deploy portainer compose file
template:
src: docker-compose.yml.j2
dest: "{{ portainer_compose_dir }}/docker-compose.yml"
notify: restart portainer
- name: Start portainer
community.docker.docker_compose_v2:
project_src: "{{ portainer_compose_dir }}"
state: present
@@ -0,0 +1,17 @@
services:
portainer:
image: {{ portainer_image }}
container_name: portainer
restart: unless-stopped
ports:
- "9002:9000"
- "8000:8000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /data/portainer:/data
networks:
- {{ docker_network_name }}
networks:
{{ docker_network_name }}:
external: true
+3
View File
@@ -0,0 +1,3 @@
---
rtlamr2mqtt_compose_dir: "{{ docker_compose_base }}/rtlamr2mqtt"
rtlamr2mqtt_image: "allangood/rtlamr2mqtt:2.3.4"
+14
View File
@@ -0,0 +1,14 @@
---
- name: Create rtlamr2mqtt compose directory
file:
path: "{{ rtlamr2mqtt_compose_dir }}"
state: directory
- name: Deploy rtlamr2mqtt compose file
template:
src: docker-compose.yml.j2
dest: "{{ rtlamr2mqtt_compose_dir }}/docker-compose.yml"
notify: restart rtlamr2mqtt
- name: Start rtlamr2mqtt
community.docker.docker_compose_v2:
project_src: "{{ rtlamr2mqtt_compose_dir }}"
state: present
@@ -0,0 +1,16 @@
services:
rtlamr2mqtt:
image: {{ rtlamr2mqtt_image }}
container_name: rtlamr2mqtt
restart: unless-stopped
devices:
- /dev/bus/usb:/dev/bus/usb
volumes:
- /data/rtlamr2mqtt/rtlamr2mqtt.yaml:/etc/rtlamr2mqtt.yaml:ro
- /data/rtlamr2mqtt/data:/var/lib/rtlamr2mqtt
networks:
- {{ docker_network_name }}
networks:
{{ docker_network_name }}:
external: true
@@ -16,7 +16,7 @@ services:
traefik.enable: "true"
traefik.http.routers.semaphore.rule: "Host(`{{ semaphore_host }}`)"
traefik.http.routers.semaphore.entrypoints: websecure
traefik.http.routers.semaphore.tls.certresolver: cloudflare
traefik.http.routers.semaphore.tls.certresolver: letsencrypt
traefik.http.services.semaphore.loadbalancer.server.port: "3000"
networks:
- {{ docker_network_name }}
+3
View File
@@ -0,0 +1,3 @@
---
telegraf_compose_dir: "{{ docker_compose_base }}/telegraf"
telegraf_image: "telegraf:latest"
+14
View File
@@ -0,0 +1,14 @@
---
- name: Create telegraf compose directory
file:
path: "{{ telegraf_compose_dir }}"
state: directory
- name: Deploy telegraf compose file
template:
src: docker-compose.yml.j2
dest: "{{ telegraf_compose_dir }}/docker-compose.yml"
notify: restart telegraf
- name: Start telegraf
community.docker.docker_compose_v2:
project_src: "{{ telegraf_compose_dir }}"
state: present
@@ -0,0 +1,14 @@
services:
telegraf:
image: {{ telegraf_image }}
container_name: telegraf
restart: unless-stopped
volumes:
- /data/telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- {{ docker_network_name }}
networks:
{{ docker_network_name }}:
external: true
+12 -10
View File
@@ -6,13 +6,14 @@ services:
security_opt:
- no-new-privileges:true
ports:
- "80:80"
- "443:443"
- "8081:8080"
environment:
CF_DNS_API_TOKEN: {{ cloudflare_api_token }}
CLOUDFLARE_EMAIL: {{ traefik_acme_email }}
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- {{ traefik_data_dir }}/acme/acme.json:/acme.json
- {{ traefik_data_dir }}/letsencrypt:/letsencrypt
- {{ traefik_data_dir }}/dynamic:/dynamic
command:
- --api.dashboard={{ traefik_dashboard_enabled | lower }}
@@ -22,19 +23,20 @@ services:
- --providers.docker.network={{ docker_network_name }}
- --providers.file.directory=/dynamic
- --providers.file.watch=true
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.websecure.address=:443
- --certificatesresolvers.cloudflare.acme.dnschallenge=true
- --certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare
- --certificatesresolvers.cloudflare.acme.email={{ traefik_acme_email }}
- --certificatesresolvers.cloudflare.acme.storage=/acme.json
- --certificatesresolvers.cloudflare.acme.dnschallenge.resolvers=1.1.1.1:53,1.0.0.1:53
- --entrypoints.websecure.http.tls.certResolver=letsencrypt
- --entrypoints.websecure.http.tls.domains[0].main=hobbs.farm
- --entrypoints.websecure.http.tls.domains[0].sans=*.hobbs.farm
- --certificatesresolvers.letsencrypt.acme.dnschallenge=true
- --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare
- --certificatesresolvers.letsencrypt.acme.email={{ traefik_acme_email }}
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/hf.json
- --certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,1.0.0.1:53
labels:
traefik.enable: "true"
traefik.http.routers.traefik.rule: "Host(`{{ traefik_dashboard_host }}`)"
traefik.http.routers.traefik.entrypoints: websecure
traefik.http.routers.traefik.tls.certresolver: cloudflare
traefik.http.routers.traefik.tls.certresolver: letsencrypt
traefik.http.routers.traefik.tls.domains[0].main: "{{ domain }}"
traefik.http.routers.traefik.tls.domains[0].sans: "*.{{ domain }}"
traefik.http.routers.traefik.service: api@internal
@@ -14,7 +14,7 @@ services:
traefik.enable: "true"
traefik.http.routers.vaultwarden.rule: "Host(`{{ vaultwarden_host }}`)"
traefik.http.routers.vaultwarden.entrypoints: websecure
traefik.http.routers.vaultwarden.tls.certresolver: cloudflare
traefik.http.routers.vaultwarden.tls.certresolver: letsencrypt
traefik.http.services.vaultwarden.loadbalancer.server.port: "80"
networks:
- {{ docker_network_name }}
+3
View File
@@ -0,0 +1,3 @@
---
watchtower_compose_dir: "{{ docker_compose_base }}/watchtower"
watchtower_image: "containrrr/watchtower"
+14
View File
@@ -0,0 +1,14 @@
---
- name: Create watchtower compose directory
file:
path: "{{ watchtower_compose_dir }}"
state: directory
- name: Deploy watchtower compose file
template:
src: docker-compose.yml.j2
dest: "{{ watchtower_compose_dir }}/docker-compose.yml"
notify: restart watchtower
- name: Start watchtower
community.docker.docker_compose_v2:
project_src: "{{ watchtower_compose_dir }}"
state: present
@@ -0,0 +1,14 @@
services:
watchtower:
image: {{ watchtower_image }}
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /data/watchtower/config.json:/config.json
networks:
- {{ docker_network_name }}
networks:
{{ docker_network_name }}:
external: true