From 0e45389c60f05c0618494c31c70c6765636f5e28 Mon Sep 17 00:00:00 2001 From: Lucas Faber Date: Thu, 1 Oct 2026 14:02:46 +0000 Subject: [PATCH] docker compose update mediahost --- .../audiobookshelf/docker-compose.yml | 26 + .../mediahost/authentik/docker-compose.yaml | 89 +++ .../mediahost/diun/docker-compose.yaml | 33 ++ .../mediahost/gitea/docker-compose.yaml | 19 + .../homeassistant/docker-compose.yaml | 23 + .../mediahost/jellyfin/docker-compose.yaml | 37 ++ .../custom-cont-init.d/10-custom-buffer.sh | 8 + .../mediahost/mediaarr/docker-compose.yaml | 276 ++++++++++ .../mediahost/mediaarr/gluetun-at/wg0.conf | 11 + .../mediahost/mediaarr/gluetun-de/wg0.conf | 11 + .../mediahost/mediaarr/recyclarr/secrets.yml | 9 + .../mediahost/mediaarr/recyclarr/settings.yml | 5 + .../rundfunkarr_fix_speed_script.sh | 2 + .../mediahost/monitoring/docker-compose.yaml | 70 +++ .../mediahost/monitoring/prometheus.yml | 37 ++ .../mediahost/navidrome/docker-compose.yaml | 34 ++ .../mediahost/nextcloud/diagnostic.sh | 172 ++++++ .../mediahost/nextcloud/docker-compose.yaml | 109 ++++ .../mediahost/nextcloud/fix_config.sh | 515 ++++++++++++++++++ .../nextcloud/hooks/nextcloud-init.sh | 102 ++++ .../mediahost/nextcloud/reverseHttps.sh | 2 + .../mediahost/nginx/conf.d/reverseProxy.conf | 304 +++++++++++ .../mediahost/nginx/docker-compose.yaml | 47 ++ .../mediahost/nginx/selfhost-ddns.sh | 19 + .../mediahost/redis/docker-compose.yaml | 22 + .../mediahost/vaultwarden/docker-compose.yaml | 27 + .../mediahost/wireguard/docker-compose.yaml | 20 + 27 files changed, 2029 insertions(+) create mode 100644 docker_compose/mediahost/audiobookshelf/docker-compose.yml create mode 100644 docker_compose/mediahost/authentik/docker-compose.yaml create mode 100644 docker_compose/mediahost/diun/docker-compose.yaml create mode 100644 docker_compose/mediahost/gitea/docker-compose.yaml create mode 100644 docker_compose/mediahost/homeassistant/docker-compose.yaml create mode 100644 docker_compose/mediahost/jellyfin/docker-compose.yaml create mode 100755 docker_compose/mediahost/mediaarr/custom-cont-init.d/10-custom-buffer.sh create mode 100644 docker_compose/mediahost/mediaarr/docker-compose.yaml create mode 100644 docker_compose/mediahost/mediaarr/gluetun-at/wg0.conf create mode 100644 docker_compose/mediahost/mediaarr/gluetun-de/wg0.conf create mode 100644 docker_compose/mediahost/mediaarr/recyclarr/secrets.yml create mode 100644 docker_compose/mediahost/mediaarr/recyclarr/settings.yml create mode 100755 docker_compose/mediahost/mediaarr/rundfunkarr-de/rundfunkarr_fix_speed_script.sh create mode 100644 docker_compose/mediahost/monitoring/docker-compose.yaml create mode 100644 docker_compose/mediahost/monitoring/prometheus.yml create mode 100755 docker_compose/mediahost/navidrome/docker-compose.yaml create mode 100644 docker_compose/mediahost/nextcloud/diagnostic.sh create mode 100644 docker_compose/mediahost/nextcloud/docker-compose.yaml create mode 100644 docker_compose/mediahost/nextcloud/fix_config.sh create mode 100755 docker_compose/mediahost/nextcloud/hooks/nextcloud-init.sh create mode 100644 docker_compose/mediahost/nextcloud/reverseHttps.sh create mode 100644 docker_compose/mediahost/nginx/conf.d/reverseProxy.conf create mode 100644 docker_compose/mediahost/nginx/docker-compose.yaml create mode 100644 docker_compose/mediahost/nginx/selfhost-ddns.sh create mode 100644 docker_compose/mediahost/redis/docker-compose.yaml create mode 100644 docker_compose/mediahost/vaultwarden/docker-compose.yaml create mode 100644 docker_compose/mediahost/wireguard/docker-compose.yaml diff --git a/docker_compose/mediahost/audiobookshelf/docker-compose.yml b/docker_compose/mediahost/audiobookshelf/docker-compose.yml new file mode 100644 index 0000000..4407030 --- /dev/null +++ b/docker_compose/mediahost/audiobookshelf/docker-compose.yml @@ -0,0 +1,26 @@ +services: + audiobookshelf: + image: ghcr.io/advplyr/audiobookshelf:latest + container_name: audiobookshelf + restart: unless-stopped + + ports: + - "13378:80" + + volumes: + - /data/audiobookshelf/data/audiobooks-englisch:/audiobooks + - /data/audiobookshelf//data/audiobooks-deutsch:/audiobooks-deutsch + - /data/audiobookshelf/data/audiobooks-kinder:/audiobooks-kinder + - /data/audiobookshelf/data/podcasts:/podcasts + - /docker/audiobookshelf/config:/config + - /docker/audiobookshelf/metadata:/metadata + networks: + - shared-proxy-services + - default + +networks: + default: + external: true + name: mediarr-services + shared-proxy-services: + external: true diff --git a/docker_compose/mediahost/authentik/docker-compose.yaml b/docker_compose/mediahost/authentik/docker-compose.yaml new file mode 100644 index 0000000..714fb59 --- /dev/null +++ b/docker_compose/mediahost/authentik/docker-compose.yaml @@ -0,0 +1,89 @@ +services: + postgresql: + image: docker.io/library/postgres:16 + restart: unless-stopped + environment: + POSTGRES_PASSWORD: authentik + POSTGRES_USER: authentik + POSTGRES_DB: authentik + volumes: + - database:/var/lib/postgresql/data + + server: + image: ghcr.io/goauthentik/server:2026.8 + restart: unless-stopped + command: server + environment: + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_REDIS__PASSWORD: PerCent7heStorySp0tInOrd3r + AUTHENTIK_REDIS__DB: 0 + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__USER: authentik + AUTHENTIK_POSTGRESQL__NAME: authentik + AUTHENTIK_POSTGRESQL__PASSWORD: authentik + AUTHENTIK_SECRET_KEY: "/jpOrfgtWEoLukXgpqC0JQWgmKUqyu5dG+oUwawVsLs83Wl5dC+nALtsosyrnBAIyWefKKQMcET2yy1RwhZO5w==" + AUTHENTIK_EMAIL__HOST: smtp.web.de + AUTHENTIK_EMAIL__PORT: 587 + AUTHENTIK_EMAIL__USERNAME: "ll_m@web.de" + AUTHENTIK_EMAIL__PASSWORD: "Mira2012" + AUTHENTIK_EMAIL__USE_TLS: true + AUTHENTIK_EMAIL__USE_SSL: false + AUTHENTIK_EMAIL__TIMEOUT: 30 + AUTHENTIK_EMAIL__FROM: "ll_m@web.de" + ports: + - "9000:9000" + - "9443:9443" + depends_on: + - postgresql + volumes: + - media:/media + - custom-templates:/templates + networks: + - shared-proxy-services + - default + + worker: + image: ghcr.io/goauthentik/server:2026.8 + restart: unless-stopped + command: worker + environment: + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_REDIS__PASSWORD: PerCent7heStorySp0tInOrd3r + AUTHENTIK_REDIS__DB: 0 + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__USER: authentik + AUTHENTIK_POSTGRESQL__NAME: authentik + AUTHENTIK_POSTGRESQL__PASSWORD: authentik + AUTHENTIK_SECRET_KEY: "/jpOrfgtWEoLukXgpqC0JQWgmKUqyu5dG+oUwawVsLs83Wl5dC+nALtsosyrnBAIyWefKKQMcET2yy1RwhZO5w==" + AUTHENTIK_EMAIL__HOST: smtp.web.de + AUTHENTIK_EMAIL__PORT: 587 + AUTHENTIK_EMAIL__USERNAME: "ll_m@web.de" + AUTHENTIK_EMAIL__PASSWORD: "Mira2012" + AUTHENTIK_EMAIL__USE_TLS: true + AUTHENTIK_EMAIL__USE_SSL: false + AUTHENTIK_EMAIL__TIMEOUT: 30 + AUTHENTIK_EMAIL__FROM: "ll_m@web.de" + depends_on: + - postgresql + volumes: + - media:/media + - certificates:/certificates + - custom-templates:/templates + networks: + - shared-proxy-services + - default + +volumes: + database: + redis: + media: + certificates: + custom-templates: + +networks: + default: + external: true + name: shared-redis-services + shared-proxy-services: + external: true + diff --git a/docker_compose/mediahost/diun/docker-compose.yaml b/docker_compose/mediahost/diun/docker-compose.yaml new file mode 100644 index 0000000..114714a --- /dev/null +++ b/docker_compose/mediahost/diun/docker-compose.yaml @@ -0,0 +1,33 @@ +services: + diun: + image: crazymax/diun:latest + container_name: diun + restart: unless-stopped + + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./data:/data + + environment: + TZ: Europe/Vienna + + # General + DIUN_WATCH_WORKERS: 20 + DIUN_WATCH_SCHEDULE: "0 0 * * *" + DIUN_PROVIDERS_DOCKER: "true" + DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT: "true" + + # Email notifications + DIUN_NOTIF_MAIL_HOST: smtp.web.de + DIUN_NOTIF_MAIL_PORT: 587 + DIUN_NOTIF_MAIL_USERNAME: ll_m@web.de + DIUN_NOTIF_MAIL_PASSWORD: Mira2012 + DIUN_NOTIF_MAIL_FROM: ll_m@web.de + DIUN_NOTIF_MAIL_TO: ll_m@web.de + DIUN_NOTIF_MAIL_INSECURESKIPVERIFY: "false" + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + diff --git a/docker_compose/mediahost/gitea/docker-compose.yaml b/docker_compose/mediahost/gitea/docker-compose.yaml new file mode 100644 index 0000000..aa75d76 --- /dev/null +++ b/docker_compose/mediahost/gitea/docker-compose.yaml @@ -0,0 +1,19 @@ +services: + gitea: + image: gitea/gitea:latest + container_name: gitea + restart: always + + environment: + USER_UID: 1000 + USER_GID: 1000 + + ports: + - "3001:3000" + - "2222:22" + + volumes: + - /data/gitea:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + diff --git a/docker_compose/mediahost/homeassistant/docker-compose.yaml b/docker_compose/mediahost/homeassistant/docker-compose.yaml new file mode 100644 index 0000000..585bef7 --- /dev/null +++ b/docker_compose/mediahost/homeassistant/docker-compose.yaml @@ -0,0 +1,23 @@ +services: + homeassistant: + container_name: homeassistant + image: ghcr.io/home-assistant/home-assistant:stable + restart: unless-stopped + privileged: true + network_mode: host + environment: + TZ: Europe/Vienna + volumes: + - /srv/homeassistant:/config + + mosquitto: + container_name: mosquitto + image: eclipse-mosquitto:latest + restart: unless-stopped + ports: + - "1883:1883" + - "9001:9001" + volumes: + - ./mosquitto/config:/mosquitto/config + - ./mosquitto/data:/mosquitto/data + - ./mosquitto/log:/mosquitto/log diff --git a/docker_compose/mediahost/jellyfin/docker-compose.yaml b/docker_compose/mediahost/jellyfin/docker-compose.yaml new file mode 100644 index 0000000..3a106be --- /dev/null +++ b/docker_compose/mediahost/jellyfin/docker-compose.yaml @@ -0,0 +1,37 @@ + +services: + jellyfin: + image: jellyfin/jellyfin + container_name: jellyfin + volumes: + - /docker/jellyfin/config:/config + - /docker/jellyfin/cache:/cache + - /data/jellyfin/media:/media + restart: unless-stopped + ports: + - "8096:8096" + environment: + - TZ=Europe/Berlin + networks: + - default + - shared-proxy-services + + seerr: + image: ghcr.io/seerr-team/seerr:latest + container_name: seerr + restart: unless-stopped + ports: + - "5055:5055" + volumes: + - /docker/jellyfin/seerr:/app/config + environment: + TZ: Europe/Berlin + networks: + - default + - shared-proxy-services +networks: + default: + external: true + name: mediarr-services + shared-proxy-services: + external: true diff --git a/docker_compose/mediahost/mediaarr/custom-cont-init.d/10-custom-buffer.sh b/docker_compose/mediahost/mediaarr/custom-cont-init.d/10-custom-buffer.sh new file mode 100755 index 0000000..a322ad4 --- /dev/null +++ b/docker_compose/mediahost/mediaarr/custom-cont-init.d/10-custom-buffer.sh @@ -0,0 +1,8 @@ +#!/usr/bin/with-contenv bash + +echo "Creating /buffer..." + +mkdir -p /buffer +chown -R abc:users /buffer +chmod 775 /buffer + diff --git a/docker_compose/mediahost/mediaarr/docker-compose.yaml b/docker_compose/mediahost/mediaarr/docker-compose.yaml new file mode 100644 index 0000000..763b62e --- /dev/null +++ b/docker_compose/mediahost/mediaarr/docker-compose.yaml @@ -0,0 +1,276 @@ + +services: + sonarr-en: + image: lscr.io/linuxserver/sonarr:latest + container_name: sonarr-en + restart: unless-stopped + ports: + - "8989:8989" + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Berlin + volumes: + - ./sonarr_EN:/config + - /data/jellyfin/media/series-english:/JellyfinSeriesEnglish + - /data/jellyfin/media/anime-english:/JellyfinAnimeEnglish + - /data/jellyfin/media/documentaries-english:/JellyfinDocus + - /data/jellyfin/buffer:/buffer + - /torrent/qbittorrent/downloads:/downloads + sonarr-de: + image: lscr.io/linuxserver/sonarr:latest + container_name: sonarr-de + restart: unless-stopped + ports: + - "8990:8989" + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Berlin + volumes: + - ./sonarr_DE:/config + - /data/jellyfin/media/series-german:/JellyfinSeriesGerman + - /data/jellyfin/media/anime-german:/JellyfinAnimeGerman + - /data/jellyfin/media/series-kids:/JellyfinKinder + - /data/jellyfin/buffer:/buffer + - /torrent/qbittorrent/downloads:/downloads + - /torrent/rundfunkarr:/downloads-rundfunkarr + radarr-en: + image: lscr.io/linuxserver/radarr:latest + container_name: radarr-en + restart: unless-stopped + ports: + - "7878:7878" + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Berlin + volumes: + - ./radarr_EN:/config + - /data/jellyfin/media/movies-english:/JellyfinMoviesEnglish + - /data/jellyfin/media/comedy-english:/JellyfinComedy + - /torrent/qbittorrent/downloads:/downloads + - /data/jellyfin/buffer:/buffer + radarr-de: + image: lscr.io/linuxserver/radarr:latest + container_name: radarr-de + restart: unless-stopped + ports: + - "7879:7878" + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Berlin + volumes: + - ./radarr_DE:/config + - /data/jellyfin/media/movies-german:/JellyfinMoviesGerman + - /data/jellyfin/media/movies-kids:/JellyfinKinder + - /data/jellyfin/buffer:/buffer + - /torrent/qbittorrent/downloads:/downloads + - /torrent/rundfunkarr:/downloads-rundfunkarr + prowlarr: + image: lscr.io/linuxserver/prowlarr:latest + container_name: prowlarr + restart: unless-stopped + ports: + - "9696:9696" + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Vienna + volumes: + - ./prowlarr:/config + + gluetun-rundfunkarr-de: + image: qmcgaw/gluetun + container_name: gluetun-rundfunkarr-de + cap_add: + - NET_ADMIN + restart: unless-stopped + + volumes: + - ./gluetun-de:/gluetun/wireguard + + environment: + - VPN_SERVICE_PROVIDER=custom + - VPN_TYPE=wireguard + ports: + - "6767:6767" # Rundfunkarr UI + + rundfunkarr-de: + image: ghcr.io/rundfunkarr/rundfunkarr:latest + container_name: rundfunkarr-de + environment: + - TZ=Europe/Berlin + - PUID=1000 # User ID (id -u) + - PGID=1000 # Group ID (id -g) + - DOWNLOAD_FOLDER_PATH=/downloads-rundfunkarr + volumes: + - ./rundfunkarr-de:/app/prisma/data + - /torrent/rundfunkarr-de:/downloads-rundfunkarr +# ports: +# - 6767:6767 + restart: unless-stopped + network_mode: "service:gluetun-rundfunkarr-de" + depends_on: + - gluetun-rundfunkarr-de + + gluetun-at: + image: qmcgaw/gluetun:latest + container_name: gluetun-at + restart: unless-stopped + + cap_add: + - NET_ADMIN + + devices: + - /dev/net/tun:/dev/net/tun + + ports: + - "8888:8888" # HTTP Proxy + - "1080:1080" # SOCKS5 Proxy + environment: + - VPN_SERVICE_PROVIDER=custom + - VPN_TYPE=wireguard + - SOCKS5_ENABLED=on + - HTTPPROXY=on + - SOCKSPROXY=on + + - TZ=Europe/Vienna + + volumes: + - ./gluetun-at:/gluetun/wireguard +# lazylibrarian: +# image: lscr.io/linuxserver/lazylibrarian:latest +# container_name: lazylibrarian +# restart: unless-stopped +# environment: +# - PUID=1000 +# - PGID=1000 +# - TZ=Europe/Vienna +# ports: +# - "5299:5299" +# volumes: +# - ./lazylibrarian:/config +# - /data/audiobookshelf/podcasts:/podcasts +# - /data/audiobookshelf/audiobooks-englisch:/audiobooks +# - /torrent/qbittorrent/downloads:/downloads + + flaresolverr: + image: ghcr.io/flaresolverr/flaresolverr:latest + container_name: flaresolverr + restart: unless-stopped + environment: + - LOG_LEVEL=info + - LOG_HTML=false + - CAPTCHA_SOLVER=none + - TZ=Europe/Vienna + ports: + - "8191:8191" + + lidarr: + image: lscr.io/linuxserver/lidarr:latest + container_name: lidarr + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Berlin + volumes: + - ./lidarr:/config + - /data/navidrome/music:/music + - /torrent/qbittorrent/downloads:/downloads + + ports: + - 8686:8686 + restart: unless-stopped + + umlautadaptarr: + image: pcjones/umlautadaptarr:latest + restart: unless-stopped + container_name: umlautadaptarr + ports: + - "5006:5006" # can be removed if you use the recommended prowlarr+proxy configuration + environment: + - TZ=Europe/Berlin + - SONARR_0_ENABLED=true + - SONARR_0_HOST=http://sonarr-en:8989 + - SONARR_0_APIKEY=e2005c5c204840878f8eb37d1ac6aa0e + - RADARR_0_ENABLED=true + - RADARR_0_HOST=http://radar-en:7878 + - RADARR_0_APIKEY=4332aee075c748f2acee41aa26f06007 + - SONARR_1_ENABLED=true + - SONARR_1_HOST=http://sonarr-de:8990 + - SONARR_1_APIKEY=707d4fcbb3d74822ba990c6a8b7278e9 + - RADARR_1_ENABLED=true + - RADARR_1_HOST=http://radarr-de:7879 + - RADARR_1_APIKEY=29f67f0ac8894357b22ea3f7fc1abfdf + - LIDARR__ENABLED=true + - LIDARR__HOST=http://lidarr:8686 + - LIDARR__APIKEY=bb41cbf2b17e439290f90ecfaeb24dff + recyclarr: + image: ghcr.io/recyclarr/recyclarr:8 + container_name: recyclarr + user: 1000:1000 + restart: unless-stopped + environment: + - TZ=Europe/Berlin + volumes: + - ./recyclarr:/config + + listenarr: + image: ghcr.io/listenarrs/listenarr:canary + container_name: listenarr + ports: + - "4545:4545" + environment: + - PUID=1000 + - PGID=1000 + - UMASK=022 + # - LISTENARR_PUBLIC_URL=https://your-domain.com ## OPTIONAL: Used by Discord Bot + volumes: + - ./listenarr:/app/config + - /data/audiobookshelf/data/audiobooks-englisch:/audiobooks-english + - /data/audiobookshelf/data/audiobooks-deutsch:/audiobooks-german + - /data/audiobookshelf/data/audiobooks-kinder:/audiobooks-kinder + - /torrent/qbittorrent/downloads:/downloads + restart: unless-stopped + upload-assistant: + image: ghcr.io/audionut/upload-assistant:latest + container_name: upload-assistant + restart: unless-stopped + ports: + - "5000:5000" + environment: + - PUID=1000 + - PGID=1000 + # Paths visible in the file browser + - UA_BROWSE_ROOTS=/downloads,/data + + command: ["--webui", "0.0.0.0:5000"] + + volumes: + - ./upload-assistant:/Upload-Assistant/data + # Sonarr/Radarr libraries + - /data/jellyfin/media/series-english:/data/series-english:ro + - /data/jellyfin/media/anime-english:/data/anime-english:ro + - /data/jellyfin/media/documentaries-english:/docus-english:ro + - /data/jellyfin/media/series-german:/series-german:ro + - /data/jellyfin/media/anime-german:/anime-german:ro + - /data/jellyfin/media/series-kids:/series-kids:ro + - /data/jellyfin/media/movies-english:/data/movies-english:ro + - /data/jellyfin/media/comedy-english:/data/comedy-english:ro + - /data/jellyfin/media/movies-german:/data/movies-german:ro + - /data/jellyfin/media/movies-kids:/data/movies-kids:ro +# - /data/audiobookshelf/podcasts:/podcasts +# - /data/audiobookshelf/audiobooks-englisch:/audiobooks + - /data/navidrome/music:/data/music:ro + - /data/audiobookshelf/data/audiobooks-englisch:/data/audiobooks-english:ro + - /data/audiobookshelf/data/audiobooks-deutsch:/data/audiobooks-german:ro + - /data/audiobookshelf/data/audiobooks-kinder:/data/audiobooks-kinder:ro + # Download location + - /torrent/qbittorrent/downloads:/downloads + +networks: + default: + external: true + name: mediarr-services diff --git a/docker_compose/mediahost/mediaarr/gluetun-at/wg0.conf b/docker_compose/mediahost/mediaarr/gluetun-at/wg0.conf new file mode 100644 index 0000000..4cef4f3 --- /dev/null +++ b/docker_compose/mediahost/mediaarr/gluetun-at/wg0.conf @@ -0,0 +1,11 @@ +# +# Use this configuration with WireGuard client +# +[Interface] +Address = 10.14.0.2/16 +PrivateKey = mKeDQdxtM1iFJS7WLlb2jPGabZIt3NkoA/nGCcNOU1I= +DNS = 162.252.172.57, 149.154.159.92 +[Peer] +PublicKey = dPZe8Jq3Hu0k07MDk+Y4+AS2XHSLYalyg91TSFXRYEA= +AllowedIPs = 0.0.0.0/0 +Endpoint = 87.249.133.4:51820 diff --git a/docker_compose/mediahost/mediaarr/gluetun-de/wg0.conf b/docker_compose/mediahost/mediaarr/gluetun-de/wg0.conf new file mode 100644 index 0000000..679849c --- /dev/null +++ b/docker_compose/mediahost/mediaarr/gluetun-de/wg0.conf @@ -0,0 +1,11 @@ +# +# Use this configuration with WireGuard client +# +[Interface] +Address = 10.14.0.2/16 +PrivateKey = mKeDQdxtM1iFJS7WLlb2jPGabZIt3NkoA/nGCcNOU1I= +DNS = 162.252.172.57, 149.154.159.92 +[Peer] +PublicKey = d3ldwEjnFcbLwD1o8uC5xC3DaSNek8DGeTpOb/h/IE4= +AllowedIPs = 0.0.0.0/0 +Endpoint = 86.38.98.8:51820 diff --git a/docker_compose/mediahost/mediaarr/recyclarr/secrets.yml b/docker_compose/mediahost/mediaarr/recyclarr/secrets.yml new file mode 100644 index 0000000..7e8d795 --- /dev/null +++ b/docker_compose/mediahost/mediaarr/recyclarr/secrets.yml @@ -0,0 +1,9 @@ +# secrets.yml +sonarr_en_url: http://sonarr-en:8989 +sonarr_en_apikey: e2005c5c204840878f8eb37d1ac6aa0e +radarr_en_url: http://radarr-en:7878 +radarr_en_apikey: 4332aee075c748f2acee41aa26f06007 +sonarr_de_url: http://sonarr-de:8989 +sonarr_de_apikey: 707d4fcbb3d74822ba990c6a8b7278e9 +radarr_de_url: http://radarr-de:7878 +radarr_de_apikey: 29f67f0ac8894357b22ea3f7fc1abfdf diff --git a/docker_compose/mediahost/mediaarr/recyclarr/settings.yml b/docker_compose/mediahost/mediaarr/recyclarr/settings.yml new file mode 100644 index 0000000..cc94b99 --- /dev/null +++ b/docker_compose/mediahost/mediaarr/recyclarr/settings.yml @@ -0,0 +1,5 @@ +# yaml-language-server: $schema=https://schemas.recyclarr.dev/latest/settings-schema.json + +# Edit this file to customize the behavior of Recyclarr beyond its defaults +# For the settings file reference guide, visit the link to the wiki below: +# https://recyclarr.dev/reference/settings/ \ No newline at end of file diff --git a/docker_compose/mediahost/mediaarr/rundfunkarr-de/rundfunkarr_fix_speed_script.sh b/docker_compose/mediahost/mediaarr/rundfunkarr-de/rundfunkarr_fix_speed_script.sh new file mode 100755 index 0000000..b6a2e67 --- /dev/null +++ b/docker_compose/mediahost/mediaarr/rundfunkarr-de/rundfunkarr_fix_speed_script.sh @@ -0,0 +1,2 @@ +sed -i 's/speed:a/speed:BigInt(Math.round(a))/g' /app/.next/server/chunks/*.js +sed -i 's/speed,/speed: BigInt(Math.round(speed)),/' /app/src/server/download-manager.ts diff --git a/docker_compose/mediahost/monitoring/docker-compose.yaml b/docker_compose/mediahost/monitoring/docker-compose.yaml new file mode 100644 index 0000000..a2bc6ee --- /dev/null +++ b/docker_compose/mediahost/monitoring/docker-compose.yaml @@ -0,0 +1,70 @@ +services: + prometheus: + image: prom/prometheus:latest + container_name: prometheus + restart: unless-stopped + ports: + - "9090:9090" + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro + - prometheus-data:/prometheus + + grafana: + image: grafana/grafana:latest + container_name: grafana + restart: unless-stopped + ports: + - "3000:3000" + + environment: + GF_SECURITY_ADMIN_USER: lucas + GF_SECURITY_ADMIN_PASSWORD: Mira2012 + GF_SMTP_ENABLED: "true" + GF_SMTP_HOST: "smtp.web.de:587" + GF_SMTP_USER: "ll_m@web.de" + GF_SMTP_PASSWORD: "Mira2012" + GF_SMTP_FROM_ADDRESS: "ll_m@web.de" + GF_SMTP_FROM_NAME: "Grafana" + GF_SMTP_SKIP_VERIFY: "false" + GF_SERVER_ROOT_URL: https://status.someoneslinux.selfhost.eu + GF_SERVER_PROTOCOL: http + volumes: + - grafana-data:/var/lib/grafana + - ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources + - ./grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards + - ./grafana/provisioning/dashboards/imported:/var/lib/grafana/dashboards + networks: + - default + - shared-proxy-services + node-exporter: + container_name: node-exporter + image: prom/node-exporter:latest + restart: unless-stopped + pid: host + command: + - '--path.rootfs=/host' + - '--collector.textfile.directory=/textfile' + volumes: + - '/:/host:ro,rslave' + - ./node_exporter/textfile_collector:/textfile:ro + ports: + - '9100:9100' + + cadvisor: + image: gcr.io/cadvisor/cadvisor:latest + container_name: cadvisor + restart: unless-stopped + ports: + - "8080:8080" + volumes: + - /:/rootfs:ro + - /data:/data:ro + - /docker:/docker:ro + +volumes: + prometheus-data: + grafana-data: +networks: + default: + shared-proxy-services: + external: true diff --git a/docker_compose/mediahost/monitoring/prometheus.yml b/docker_compose/mediahost/monitoring/prometheus.yml new file mode 100644 index 0000000..2e72443 --- /dev/null +++ b/docker_compose/mediahost/monitoring/prometheus.yml @@ -0,0 +1,37 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + + - job_name: "prometheus" + static_configs: + - targets: + - "prometheus:9090" + + - job_name: "node-exporter" + static_configs: + - targets: + - "192.168.0.2:9100" + labels: + host: serverhost + - targets: + - "node-exporter:9100" + labels: + host: "mediahost" + + - job_name: "cadvisor" + static_configs: + - targets: + - "cadvisor:8080" + - job_name: "smartctl" + static_configs: + - targets: + - "192.168.0.2:9633" + labels: + host: serverhost + - targets: + - "192.168.0.4:9633" + labels: + host: "mediahost" + diff --git a/docker_compose/mediahost/navidrome/docker-compose.yaml b/docker_compose/mediahost/navidrome/docker-compose.yaml new file mode 100755 index 0000000..88e7fe3 --- /dev/null +++ b/docker_compose/mediahost/navidrome/docker-compose.yaml @@ -0,0 +1,34 @@ + +services: + navidrome: + image: deluan/navidrome:latest + container_name: navidrome + restart: unless-stopped + + ports: + - "4533:4533" + + environment: + ND_SCANSCHEDULE: 1h + ND_LOGLEVEL: info + ND_SESSIONTIMEOUT: 24h + ND_MUSICFOLDER: /music + ND_DATAFOLDER: /data + ND_SCANNER_PURGEMISSING: always + + volumes: + - /data/navidrome/data:/data + - /data/navidrome/music/music:/music:ro + - /data/navidrome/music/kinder:/kinder:ro + - /data/navidrome/music/kamerun:/kamerun:ro + - /data/navidrome/music/weihnachten:/weihnachten:ro + - /data/navidrome/music/playlists:/playlists:ro + networks: + - shared-proxy-services + - default + +networks: + default: + shared-proxy-services: + external: true + diff --git a/docker_compose/mediahost/nextcloud/diagnostic.sh b/docker_compose/mediahost/nextcloud/diagnostic.sh new file mode 100644 index 0000000..35568bf --- /dev/null +++ b/docker_compose/mediahost/nextcloud/diagnostic.sh @@ -0,0 +1,172 @@ +( +echo "============================================================" +echo "NEXTCLOUD DIAGNOSTIC" +echo "Date: $(date)" +echo "============================================================" +echo + +run() { + echo + echo "============================================================" + echo "COMMAND:" + echo "$*" + echo "============================================================" + "$@" 2>&1 + echo + echo "EXIT CODE: $?" +} + +echo "### 1. Docker containers" +run docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}' + +echo "### 2. Nextcloud status" +run docker exec nextcloud php occ status + +echo "### 3. Effective Nextcloud configuration" +echo 'COMMAND: docker exec nextcloud php -r ...' +docker exec nextcloud php -r ' +$CONFIG = []; + +include "/var/www/html/config/config.php"; + +if (file_exists("/var/www/html/config/zz-docker-env.config.php")) { + include "/var/www/html/config/zz-docker-env.config.php"; +} + +echo "dbtype: " . ($CONFIG["dbtype"] ?? "MISSING") . PHP_EOL; +echo "dbhost: " . ($CONFIG["dbhost"] ?? "MISSING") . PHP_EOL; +echo "dbname: " . ($CONFIG["dbname"] ?? "MISSING") . PHP_EOL; +echo "dbuser: " . ($CONFIG["dbuser"] ?? "MISSING") . PHP_EOL; +echo "dbpassword: " . (empty($CONFIG["dbpassword"] ?? "") ? "EMPTY" : "SET") . PHP_EOL; +echo "instanceid: " . ($CONFIG["instanceid"] ?? "MISSING") . PHP_EOL; +echo "version: " . ($CONFIG["version"] ?? "MISSING") . PHP_EOL; +echo "datadirectory: " . ($CONFIG["datadirectory"] ?? "MISSING") . PHP_EOL; +echo "overwrite.cli.url: " . ($CONFIG["overwrite.cli.url"] ?? "MISSING") . PHP_EOL; +echo "overwriteprotocol: " . ($CONFIG["overwriteprotocol"] ?? "MISSING") . PHP_EOL; + +echo "redis.host: " . ($CONFIG["redis"]["host"] ?? "MISSING") . PHP_EOL; +echo "redis.password: " . (empty($CONFIG["redis"]["password"] ?? "") ? "EMPTY" : "SET") . PHP_EOL; + +echo "trusted_domains:" . PHP_EOL; +foreach (($CONFIG["trusted_domains"] ?? []) as $domain) { + echo " - " . $domain . PHP_EOL; +} + +echo "trusted_proxies:" . PHP_EOL; +foreach (($CONFIG["trusted_proxies"] ?? []) as $proxy) { + echo " - " . $proxy . PHP_EOL; +} +' 2>&1 +echo "EXIT CODE: $?" + +echo +echo "### 4. Test MariaDB connection" +echo 'COMMAND: docker exec nextcloud php -r ...' +docker exec nextcloud php -r ' +$CONFIG = []; + +include "/var/www/html/config/config.php"; + +if (file_exists("/var/www/html/config/zz-docker-env.config.php")) { + include "/var/www/html/config/zz-docker-env.config.php"; +} + +try { + $dsn = "mysql:host=" . $CONFIG["dbhost"] . + ";dbname=" . $CONFIG["dbname"]; + + $pdo = new PDO( + $dsn, + $CONFIG["dbuser"], + $CONFIG["dbpassword"], + [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + ] + ); + + echo "PASS: MariaDB connection works" . PHP_EOL; + +} catch (Throwable $e) { + echo "FAIL: MariaDB connection failed" . PHP_EOL; + echo $e->getMessage() . PHP_EOL; +} +' 2>&1 +echo "EXIT CODE: $?" + +echo +echo "### 5. Nextcloud config directory" +run docker exec nextcloud ls -la /var/www/html/config + +echo "### 6. Nextcloud root directory" +run docker exec nextcloud ls -la /var/www/html + +echo "### 7. Nextcloud data directory" +run docker exec nextcloud sh -c 'ls -ld /var/www/html/data && ls -la /var/www/html/data | head -30' + +echo "### 8. Config.php syntax" +run docker exec nextcloud php -l /var/www/html/config/config.php + +echo "### 9. zz-docker-env.config.php syntax" +run docker exec nextcloud php -l /var/www/html/config/zz-docker-env.config.php + +echo "### 10. Docker environment" +echo 'COMMAND: docker exec nextcloud env' +docker exec nextcloud env 2>&1 | + sed -E \ + -e 's/(MYSQL_PASSWORD=).*/\1[REDACTED]/' \ + -e 's/(MYSQL_ROOT_PASSWORD=).*/\1[REDACTED]/' \ + -e 's/(REDIS_PASSWORD=).*/\1[REDACTED]/' \ + -e 's/(NEXTCLOUD_ADMIN_PASSWORD=).*/\1[REDACTED]/' +echo "EXIT CODE: ${PIPESTATUS[0]}" + +echo "### 11. Nextcloud container logs" +run docker logs --tail 20 nextcloud + +echo "### 12. MariaDB container logs" +run docker logs --tail 20 nextcloud-db + +echo "### 13. Nextcloud application log" +echo 'COMMAND: docker exec nextcloud sh -c ...' +docker exec nextcloud sh -c ' +if [ -f /var/www/html/data/nextcloud.log ]; then + tail -2 /var/www/html/data/nextcloud.log +else + echo "nextcloud.log not found" +fi +' 2>&1 +echo "EXIT CODE: $?" + +echo "### 14. Network" +run docker inspect nextcloud --format '{{json .NetworkSettings.Networks}}' + +echo "### 15. Database network" +run docker inspect nextcloud-db --format '{{json .NetworkSettings.Networks}}' + +echo "### 16. Docker Compose configuration" +echo 'COMMAND: docker compose config' +docker compose config 2>&1 | + sed -E \ + -e 's/(MYSQL_PASSWORD:).*/\1 [REDACTED]/' \ + -e 's/(MYSQL_ROOT_PASSWORD:).*/\1 [REDACTED]/' \ + -e 's/(REDIS_PASSWORD:).*/\1 [REDACTED]/' \ + -e 's/(NEXTCLOUD_ADMIN_PASSWORD:).*/\1 [REDACTED]/' + +echo "### 17. Disk space" +run df -h + +echo "### 18. Docker volumes/mounts" +echo 'COMMAND: docker inspect nextcloud --format ...' +docker inspect nextcloud \ + --format '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}' \ + 2>&1 + +echo +echo "============================================================" +echo "END OF DIAGNOSTIC" +echo "============================================================" + +) 2>&1 | tee nextcloud-diagnostic.txt + +echo +echo "Diagnostic saved to:" +echo " $(pwd)/nextcloud-diagnostic.txt" diff --git a/docker_compose/mediahost/nextcloud/docker-compose.yaml b/docker_compose/mediahost/nextcloud/docker-compose.yaml new file mode 100644 index 0000000..fd62bcf --- /dev/null +++ b/docker_compose/mediahost/nextcloud/docker-compose.yaml @@ -0,0 +1,109 @@ +services: + nextcloud: + image: nextcloud:latest + container_name: nextcloud + restart: unless-stopped + depends_on: + db: + condition: service_healthy + environment: + # PHP + - PHP_UPLOAD_LIMIT=16G + # APACHE2 + - APACHE_BODY_LIMIT=0 + # MYSQL + - MYSQL_HOST=db + - MYSQL_DATABASE=nextcloud + - MYSQL_USER=nextcloud + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + # REDIS + - REDIS_HOST=redis + - REDIS_HOST_PASSWORD=${REDIS_PASSWORD} + # NEXTCLOUD + - NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER} + - NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD} + - NEXTCLOUD_TRUSTED_DOMAINS=${NEXTCLOUD_DOMAIN} 192.168.0.4 + - NEXTCLOUD_MIMI_PASSWORD=${NEXTCLOUD_MIMI_PASSWORD} + - OVERWRITEPROTOCOL=https + - OVERWRITECLIURL=https://${NEXTCLOUD_DOMAIN} + - OVERWRITEHOST=${NEXTCLOUD_DOMAIN} + - TRUSTED_PROXIES=nginx 172.16.0.0/12 + # SMTP + - SMTP_HOST=${SMTP_HOST} + - SMTP_PORT=${SMTP_PORT} + - SMTP_SECURE=${SMTP_SECURE} + - SMTP_USER=${SMTP_USER} + - SMTP_PASSWORD=${SMTP_PASSWORD} + - SMTP_FROM_ADDRESS=${SMTP_FROM_ADDRESS} + - SMTP_FROM_DOMAIN=${SMTP_FROM_DOMAIN} + + # Collabora + - COLLABORA_DOMAIN=${COLLABORA_DOMAIN} + - LUCAS_EMAIL=${LUCAS_EMAIL} + volumes: + - ./html:/var/www/html + - /data/nextcloud/data:/var/www/html/data + # Post-installation hooks + - ./hooks/nextcloud-init.sh:/docker-entrypoint-hooks.d/post-installation/nextcloud-init.sh:ro + + ports: + - "8082:80" + networks: + - default + - shared-proxy-services + - shared-redis-services + + db: + image: mariadb:11 + container_name: nextcloud-db + restart: unless-stopped + command: + - --transaction-isolation=READ-COMMITTED + - --log-bin=binlog + - --binlog-format=ROW + - --slow-query-log=ON + - --slow-query-log-file=/var/lib/mysql/slow-query.log + - --long-query-time=2 + - --innodb-log-file-size=256M + environment: + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - MYSQL_DATABASE=nextcloud + - MYSQL_USER=nextcloud + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + volumes: + - ./db:/var/lib/mysql + healthcheck: + test: + [ + "CMD-SHELL", + "healthcheck.sh --connect --innodb_initialized" + ] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + + collabora: + image: collabora/code + expose: + - 9980 + cap_add: + - MKNOD + environment: + - domain=${NEXTCLOUD_DOMAIN} + - VIRTUAL_HOST=${COLLABORA_DOMAIN} + - VIRTUAL_PORT=9980 + - VIRTUAL_PROTO=https + - LETSENCRYPT_HOST=${COLLABORA_DOMAIN} + - LETSENCRYPT_EMAIL=${LUCAS_EMAIL} + networks: + - shared-proxy-services + restart: always +networks: + default: + driver: bridge + shared-redis-services: + external: true + shared-proxy-services: + external: true + diff --git a/docker_compose/mediahost/nextcloud/fix_config.sh b/docker_compose/mediahost/nextcloud/fix_config.sh new file mode 100644 index 0000000..422157a --- /dev/null +++ b/docker_compose/mediahost/nextcloud/fix_config.sh @@ -0,0 +1,515 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +CONTAINER="nextcloud" +OVERRIDE="/var/www/html/config/zz-docker-env.config.php" + +FIX=false + +if [[ "${1:-}" == "--fix" ]]; then + FIX=true +fi + +PASS=0 +FAIL=0 + +pass() { + echo "PASS: $1" + PASS=$((PASS + 1)) +} + +fail() { + echo "FAIL: $1" + FAIL=$((FAIL + 1)) +} + +# ============================================================ +# Basic checks +# ============================================================ + +echo "==========================================" +echo " Nextcloud Docker configuration checker" +echo "==========================================" +echo + +if ! docker inspect "$CONTAINER" >/dev/null 2>&1; then + echo "FAIL: Docker container '$CONTAINER' does not exist" + exit 1 +fi + +if [[ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER")" != "true" ]]; then + echo "FAIL: Docker container '$CONTAINER' is not running" + exit 1 +fi + +if [[ ! -f ".env" ]]; then + echo "FAIL: .env not found" + exit 1 +fi + +# ============================================================ +# Read .env +# ============================================================ + +declare -A ENV + +while IFS='=' read -r key value; do + + key="${key//$'\r'/}" + value="${value//$'\r'/}" + + # Ignore empty lines + [[ -z "${key// /}" ]] && continue + + # Ignore comments + [[ "$key" =~ ^[[:space:]]*# ]] && continue + + # Trim whitespace + key="${key#"${key%%[![:space:]]*}"}" + key="${key%"${key##*[![:space:]]}"}" + + # Remove "export " + key="${key#export }" + + # Remove quotes + value="${value#\"}" + value="${value%\"}" + value="${value#\'}" + value="${value%\'}" + + ENV["$key"]="$value" + +done < .env + +MYSQL_PASSWORD="${ENV[MYSQL_PASSWORD]:-}" +MYSQL_ROOT_PASSWORD="${ENV[MYSQL_ROOT_PASSWORD]:-}" +REDIS_PASSWORD="${ENV[REDIS_PASSWORD]:-}" +NEXTCLOUD_ADMIN_USER="${ENV[NEXTCLOUD_ADMIN_USER]:-}" +NEXTCLOUD_ADMIN_PASSWORD="${ENV[NEXTCLOUD_ADMIN_PASSWORD]:-}" +NEXTCLOUD_DOMAIN="${ENV[NEXTCLOUD_DOMAIN]:-}" + +# ============================================================ +# Environment checks +# ============================================================ + +echo "Checking .env variables" +echo "-----------------------" + +for name in \ + MYSQL_PASSWORD \ + MYSQL_ROOT_PASSWORD \ + REDIS_PASSWORD \ + NEXTCLOUD_ADMIN_USER \ + NEXTCLOUD_ADMIN_PASSWORD \ + NEXTCLOUD_DOMAIN +do + if [[ -n "${ENV[$name]:-}" ]]; then + pass "$name is defined" + else + fail "$name is missing" + fi +done + +echo + +# ============================================================ +# Read config.php +# +# PHP runs ONLY inside Docker. +# No PHP is required on the host. +# ============================================================ + +CONFIG_JSON="$( + docker exec "$CONTAINER" php -r ' + $CONFIG = []; + + include "/var/www/html/config/config.php"; + + echo json_encode([ + "dbhost" => + $CONFIG["dbhost"] ?? "", + + "dbname" => + $CONFIG["dbname"] ?? "", + + "dbuser" => + $CONFIG["dbuser"] ?? "", + + "dbpassword" => + $CONFIG["dbpassword"] ?? "", + + "redis_host" => + $CONFIG["redis"]["host"] ?? "", + + "redis_password" => + $CONFIG["redis"]["password"] ?? "", + + "overwriteprotocol" => + $CONFIG["overwriteprotocol"] ?? "", + + "overwrite_cli_url" => + $CONFIG["overwrite.cli.url"] ?? "", + + "trusted_domains" => + $CONFIG["trusted_domains"] ?? [], + + "trusted_proxies" => + $CONFIG["trusted_proxies"] ?? [] + ]); + ' +)" + +# ============================================================ +# Extract configuration +# ============================================================ + +DB_HOST="$(jq -r '.dbhost' <<< "$CONFIG_JSON")" +DB_NAME="$(jq -r '.dbname' <<< "$CONFIG_JSON")" +DB_USER="$(jq -r '.dbuser' <<< "$CONFIG_JSON")" +DB_PASSWORD="$(jq -r '.dbpassword' <<< "$CONFIG_JSON")" + +REDIS_HOST="$(jq -r '.redis_host' <<< "$CONFIG_JSON")" +REDIS_PASSWORD_CONFIG="$(jq -r '.redis_password' <<< "$CONFIG_JSON")" + +OVERWRITE_PROTOCOL="$(jq -r '.overwriteprotocol' <<< "$CONFIG_JSON")" +OVERWRITE_CLI_URL="$(jq -r '.overwrite_cli_url' <<< "$CONFIG_JSON")" + +# ============================================================ +# Database +# ============================================================ + +echo "Checking database configuration" +echo "-------------------------------" + +if [[ "$DB_HOST" == "mariadb" ]]; then + pass "dbhost = mariadb" +else + fail "dbhost: expected mariadb, got $DB_HOST" +fi + +if [[ "$DB_NAME" == "nextcloud" ]]; then + pass "dbname = nextcloud" +else + fail "dbname: expected nextcloud, got $DB_NAME" +fi + +if [[ "$DB_USER" == "nextcloud" ]]; then + pass "dbuser = nextcloud" +else + fail "dbuser: expected nextcloud, got $DB_USER" +fi + +if [[ "$MYSQL_PASSWORD" == "$DB_PASSWORD" ]]; then + pass "dbpassword" +else + fail "dbpassword" +fi + +echo + +# ============================================================ +# Redis +# ============================================================ + +echo "Checking Redis configuration" +echo "----------------------------" + +if [[ "$REDIS_HOST" == "redis" ]]; then + pass "redis host = redis" +else + fail "redis host: expected redis, got $REDIS_HOST" +fi + +if [[ "$REDIS_PASSWORD" == "$REDIS_PASSWORD_CONFIG" ]]; then + pass "redis password" +else + fail "redis password" + echo " expected: [hidden]" + echo " actual: [hidden]" +fi + +echo + +# ============================================================ +# URL +# ============================================================ + +echo "Checking Nextcloud URL configuration" +echo "------------------------------------" + +if [[ "$OVERWRITE_PROTOCOL" == "https" ]]; then + pass "overwriteprotocol = https" +else + fail "overwriteprotocol: expected https, got $OVERWRITE_PROTOCOL" +fi + +EXPECTED_URL="https://${NEXTCLOUD_DOMAIN}" + +if [[ "$OVERWRITE_CLI_URL" == "$EXPECTED_URL" ]]; then + pass "overwrite.cli.url = $EXPECTED_URL" +else + fail "overwrite.cli.url" + echo " expected: $EXPECTED_URL" + echo " actual: $OVERWRITE_CLI_URL" +fi + +echo + +# ============================================================ +# Trusted domains +# ============================================================ + +echo "Checking trusted domains" +echo "-----------------------" + +if jq -e \ + --arg domain "$NEXTCLOUD_DOMAIN" \ + '.trusted_domains | index($domain) != null' \ + <<< "$CONFIG_JSON" >/dev/null +then + pass "trusted_domains contains $NEXTCLOUD_DOMAIN" +else + fail "trusted_domains does not contain $NEXTCLOUD_DOMAIN" +fi + +echo + +# ============================================================ +# Trusted proxies +# ============================================================ + +echo "Checking trusted proxies" +echo "------------------------" + +if jq -e \ + '.trusted_proxies | index("172.16.0.0/12") != null' \ + <<< "$CONFIG_JSON" >/dev/null +then + pass "trusted_proxies contains 172.16.0.0/12" +else + fail "trusted_proxies does not contain 172.16.0.0/12" +fi + +echo + +# ============================================================ +# FIX +# ============================================================ + +if [[ "$FIX" == true ]]; then + + echo "==========================================" + echo " Fixing Nextcloud configuration" + echo "==========================================" + echo + + # -------------------------------------------------------- + # Backup existing override + # -------------------------------------------------------- + + if docker exec "$CONTAINER" test -f "$OVERRIDE"; then + + BACKUP="/var/www/html/config/zz-docker-env.config.php.backup.$(date +%Y%m%d-%H%M%S)" + + docker exec "$CONTAINER" \ + cp "$OVERRIDE" "$BACKUP" + + echo "Backup created:" + echo " $BACKUP" + echo + fi + + # -------------------------------------------------------- + # Generate override INSIDE the container. + # + # Therefore PHP is NOT required on the host. + # -------------------------------------------------------- + + docker exec \ + -e MYSQL_PASSWORD="$MYSQL_PASSWORD" \ + -e REDIS_PASSWORD="$REDIS_PASSWORD" \ + -e NEXTCLOUD_DOMAIN="$NEXTCLOUD_DOMAIN" \ + "$CONTAINER" \ + php -r ' + $mysql = getenv("MYSQL_PASSWORD"); + $redis = getenv("REDIS_PASSWORD"); + $domain = getenv("NEXTCLOUD_DOMAIN"); + + $file = "/var/www/html/config/zz-docker-env.config.php"; + + $config = " $CONFIG["dbhost"] ?? "", + "dbname" => $CONFIG["dbname"] ?? "", + "dbuser" => $CONFIG["dbuser"] ?? "", + "dbpassword" => $CONFIG["dbpassword"] ?? "", + + "redis_host" => $CONFIG["redis"]["host"] ?? "", + "redis_password" => $CONFIG["redis"]["password"] ?? "", + + "overwriteprotocol" => + $CONFIG["overwriteprotocol"] ?? "", + + "overwrite_cli_url" => + $CONFIG["overwrite.cli.url"] ?? "", + + "trusted_domains" => + $CONFIG["trusted_domains"] ?? [], + + "trusted_proxies" => + $CONFIG["trusted_proxies"] ?? [] + ]); + ' + )" + + EFFECTIVE_REDIS_PASSWORD="$( + jq -r '.redis_password' <<< "$EFFECTIVE_JSON" + )" + + EFFECTIVE_DOMAINS="$( + jq -r '.trusted_domains[]?' <<< "$EFFECTIVE_JSON" + )" + + if [[ "$EFFECTIVE_REDIS_PASSWORD" == "$REDIS_PASSWORD" ]]; then + pass "Redis password fixed" + else + fail "Redis password was not fixed" + fi + + if grep -Fxq "$NEXTCLOUD_DOMAIN" <<< "$EFFECTIVE_DOMAINS"; then + pass "trusted_domains fixed" + else + fail "trusted_domains was not fixed" + fi + + echo + + echo "==========================================" + echo " Fix complete" + echo "==========================================" + + exit 0 +fi + +# ============================================================ +# Summary +# ============================================================ + +echo "==========================================" +echo "Result" +echo "==========================================" + +echo "PASS: $PASS" +echo "FAIL: $FAIL" + +if [[ "$FAIL" -eq 0 ]]; then + echo + echo "PASS: configuration is consistent." + exit 0 +else + echo + echo "FAIL: configuration has mismatches." + echo + echo "Run:" + echo + echo " $0 --fix" + echo + exit 1 +fi diff --git a/docker_compose/mediahost/nextcloud/hooks/nextcloud-init.sh b/docker_compose/mediahost/nextcloud/hooks/nextcloud-init.sh new file mode 100755 index 0000000..0ac481d --- /dev/null +++ b/docker_compose/mediahost/nextcloud/hooks/nextcloud-init.sh @@ -0,0 +1,102 @@ +#!/bin/bash +set -e + +echo "==> Configuring Nextcloud" + +OCC="php /var/www/html/occ" + +# ------------------------- +# SMTP +# ------------------------- + +$OCC config:system:set mail_smtpmode \ + --value="smtp" + +$OCC config:system:set mail_smtphost \ + --value="${SMTP_HOST}" + +$OCC config:system:set mail_smtpport \ + --value="${SMTP_PORT}" + +$OCC config:system:set mail_smtpsecure \ + --value="${SMTP_SECURE}" + +# Require SMTP authentication +$OCC config:system:set mail_smtpauth \ + --type=boolean \ + --value="true" + +$OCC config:system:set mail_smtpname \ + --value="${SMTP_USER}" + +$OCC config:system:set mail_smtppassword \ + --value="${SMTP_PASSWORD}" + +$OCC config:system:set mail_from_address \ + --value="${SMTP_FROM_ADDRESS}" + +$OCC config:system:set mail_domain \ + --value="${SMTP_FROM_DOMAIN}" + + +# ------------------------- +# Apps +# ------------------------- + +apps=( + calendar + contacts + tasks + notes + richdocuments + cookbook +) + +for app in "${apps[@]}"; do + echo "==> Installing app: ${app}" + + if ! $OCC app:list | grep -q "^ ${app}:"; then + $OCC app:install "${app}" || true + fi + + $OCC app:enable "${app}" || true +done + + +# ------------------------- +# Collabora +# ------------------------- + +if [ -n "${COLLABORA_DOMAIN}" ]; then + echo "==> Configuring Collabora" + + $OCC config:app:set richdocuments wopi_url \ + --value="https://${COLLABORA_DOMAIN}" +fi + + + +echo "==> Creating users" +export OC_PASS="${NEXTCLOUD_MIMI_PASSWORD}" +# Mimi +if ! $OCC user:info Mimi >/dev/null 2>&1; then + $OCC user:add \ + --password-from-env \ + --display-name="Mimi" \ + Mimi +fi + +$OCC user:setting Mimi settings email "miriam.m.faber@gmail.com" + +# Lucas +if ! $OCC user:info lucas >/dev/null 2>&1; then + echo "ERROR: User 'lucas' does not exist." + exit 1 +fi + +$OCC user:setting lucas settings email "${LUCAS_EMAIL}" + +echo "Set backroundjob to cron" +$OCC background:cron + +echo "==> Nextcloud post-installation configuration complete" diff --git a/docker_compose/mediahost/nextcloud/reverseHttps.sh b/docker_compose/mediahost/nextcloud/reverseHttps.sh new file mode 100644 index 0000000..d8c35c6 --- /dev/null +++ b/docker_compose/mediahost/nextcloud/reverseHttps.sh @@ -0,0 +1,2 @@ +docker compose exec nextcloud php occ config:system:set overwriteprotocol --value=https +docker compose exec nextcloud php occ config:system:set overwrite.cli.url --value=https://someoneslinux.ddns.net diff --git a/docker_compose/mediahost/nginx/conf.d/reverseProxy.conf b/docker_compose/mediahost/nginx/conf.d/reverseProxy.conf new file mode 100644 index 0000000..534ba70 --- /dev/null +++ b/docker_compose/mediahost/nginx/conf.d/reverseProxy.conf @@ -0,0 +1,304 @@ +# WebSocket support +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +# Redirect HTTP -> HTTPS +server { + listen 80; + server_name + cloud.someoneslinux.selfhost.eu + media.someoneslinux.selfhost.eu + audiobooks.someoneslinux.selfhost.eu + login.someoneslinux.selfhost.eu + auto.someoneslinux.selfhost.eu + music.someoneslinux.selfhost.eu + passwd.someoneslinux.selfhost.eu + requests.someoneslinux.selfhost.eu + torrent.someoneslinux.selfhost.eu + status.someoneslinux.selfhost.eu; + #ssl_conf_command Groups X25519:secp256r1:secp384r1; +# ssl_conf_command Groups X25519:prime256v1:secp384r1; + location /.well-known/acme-challenge/ { + root /usr/share/nginx/html; + try_files $uri =404; + } + + location / { + return 301 https://$host$request_uri; + } +} + +# Common SSL settings +ssl_protocols TLSv1.2 TLSv1.3; +ssl_prefer_server_ciphers on; + +################### +# NEXTCLOUD +################### +server { + listen 443 ssl;http2 on; + server_name cloud.someoneslinux.selfhost.eu; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + ssl_certificate /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/privkey.pem; + + client_max_body_size 10G; + + location / { + proxy_pass http://nextcloud:80; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + } +} + +################### +# JELLYFIN +################### +server { + listen 443 ssl;http2 on; + server_name media.someoneslinux.selfhost.eu; + + add_header Content-Security-Policy "frame-ancestors 'self';" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + ssl_certificate /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/privkey.pem; + + location / { + proxy_pass http://jellyfin:8096; + proxy_http_version 1.1; + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + } +} + +################### +# AUDIOBOOKSHELF +################### +server { + listen 443 ssl;http2 on; + server_name audiobooks.someoneslinux.selfhost.eu; + + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + ssl_certificate /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/privkey.pem; + + location / { + proxy_pass http://audiobookshelf:80; + proxy_http_version 1.1; + + proxy_set_header Host $host; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header X-Forwarded-Proto https; + } +} + +################### +# AUTHENTIK +################### +server { + listen 443 ssl;http2 on; + server_name login.someoneslinux.selfhost.eu; + + add_header Content-Security-Policy "frame-ancestors 'self';" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + ssl_certificate /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/privkey.pem; + + location / { + proxy_pass http://authentik-server-1:9000; + proxy_http_version 1.1; + + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} + +#################### +## HOME ASSISTANT +#################### +#server { +# listen 443 ssl;http2 on; +# server_name auto.someoneslinux.selfhost.eu; +# +# add_header Content-Security-Policy "default-src 'self'; frame-ancestors 'self';" always; +# add_header X-Frame-Options "SAMEORIGIN" always; +# add_header X-Content-Type-Options "nosniff" always; +# add_header Referrer-Policy "strict-origin-when-cross-origin" always; +# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; +# +# ssl_certificate /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/fullchain.pem; +# ssl_certificate_key /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/privkey.pem; +# +# location / { +# proxy_pass http://192.168.0.4:8123; +# proxy_http_version 1.1; +# +# proxy_set_header Host $host; +# proxy_set_header Upgrade $http_upgrade; +# proxy_set_header Connection $connection_upgrade; +# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +# proxy_set_header X-Forwarded-Proto https; +# } +#} + +################### +# NAVIDROME +################### +server { + listen 443 ssl;http2 on; + server_name music.someoneslinux.selfhost.eu; + + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + ssl_certificate /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/privkey.pem; + + location / { + proxy_pass http://navidrome:4533; + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Real-IP $remote_addr; + } +} + +################### +# VAULTWARDEN +################### +server { + listen 443 ssl;http2 on; + server_name passwd.someoneslinux.selfhost.eu; + + add_header Content-Security-Policy "default-src 'self'; frame-ancestors 'self';" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + ssl_certificate /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/privkey.pem; + + location / { + proxy_pass http://vaultwarden:80; + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /notifications/hub { + proxy_pass http://vaultwarden:3012; + proxy_http_version 1.1; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + } +} + +################### +# SEERR +################### +server { + listen 443 ssl;http2 on; + server_name requests.someoneslinux.selfhost.eu; + + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + ssl_certificate /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/privkey.pem; + + location / { + proxy_pass http://seerr:5055; + proxy_http_version 1.1; + + proxy_set_header Host $host; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header X-Forwarded-Proto https; + } +} + +################### +# QBITTORRENT +################### +server { + listen 443 ssl;http2 on; + server_name torrent.someoneslinux.selfhost.eu; + + add_header Content-Security-Policy "frame-ancestors 'self';" always; +# add_header Content-Security-Policy "default-src 'self'; frame-ancestors 'self';" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + ssl_certificate /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/privkey.pem; + + location / { + proxy_pass http://192.168.0.2:8080; + proxy_http_version 1.1; + + proxy_set_header Host $host; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header X-Forwarded-Proto https; + } +} + +################### +# GRFANA +################### +server { + listen 443 ssl;http2 on; + server_name status.someoneslinux.selfhost.eu; + + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + ssl_certificate /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/live/cloud.someoneslinux.selfhost.eu/privkey.pem; + + location / { + proxy_pass http://grafana:3000; + proxy_http_version 1.1; + + proxy_set_header Host $host; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header X-Forwarded-Proto https; + } +} diff --git a/docker_compose/mediahost/nginx/docker-compose.yaml b/docker_compose/mediahost/nginx/docker-compose.yaml new file mode 100644 index 0000000..693f30d --- /dev/null +++ b/docker_compose/mediahost/nginx/docker-compose.yaml @@ -0,0 +1,47 @@ +services: + nginx: + image: nginx:stable + container_name: nginx + restart: unless-stopped + + ports: + - "80:80" + - "443:443" + + volumes: + - ./conf.d:/etc/nginx/conf.d + - ./certs:/etc/nginx/certs + - ./html:/usr/share/nginx/html + # Access and error logs + - ./logs:/var/log/nginx + networks: + - shared-proxy-services + + crowdsec: + image: crowdsecurity/crowdsec:latest + container_name: crowdsec + restart: unless-stopped + environment: + COLLECTIONS: > + crowdsecurity/nginx + crowdsecurity/http-cve + volumes: + - crowdsec-db:/var/lib/crowdsec/data + - ./logs:/var/log/nginx:ro + - /etc/localtime:/etc/localtime:ro + + + certbot: + image: certbot/certbot:latest + container_name: certbot + volumes: + - ./certs:/etc/letsencrypt + - ./html:/var/www/certbot + networks: + - shared-proxy-services + +networks: + shared-proxy-services: + external: true +volumes: + crowdsec-db: diff --git a/docker_compose/mediahost/nginx/selfhost-ddns.sh b/docker_compose/mediahost/nginx/selfhost-ddns.sh new file mode 100644 index 0000000..7ecdc90 --- /dev/null +++ b/docker_compose/mediahost/nginx/selfhost-ddns.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# selfHOST DynDNS Update Script + +URL='https://549335:GoviYPrfj4XI@carol.selfhost.de/nic/update?myip=' + +# Get current public IPv4 address +PUBLIC_IP=$(curl -4 -s https://api.ipify.org) + +# Exit if IP detection failed +if [ -z "$PUBLIC_IP" ]; then + echo "$(date): Failed to determine public IP" + exit 1 +fi + +# Send update to selfHOST +RESULT=$(curl -s "${URL}${PUBLIC_IP}") + +echo "$(date): IP=${PUBLIC_IP}" diff --git a/docker_compose/mediahost/redis/docker-compose.yaml b/docker_compose/mediahost/redis/docker-compose.yaml new file mode 100644 index 0000000..e388045 --- /dev/null +++ b/docker_compose/mediahost/redis/docker-compose.yaml @@ -0,0 +1,22 @@ +services: + redis: + image: redis:7 + container_name: redis + restart: unless-stopped + command: redis-server --appendonly yes --requirepass PerCent7heStorySp0tInOrd3r + + healthcheck: + test: + [ + "CMD-SHELL", + "redis-cli -a \"$${REDIS_PASSWORD}\" ping | grep PONG" + ] + interval: 10s + timeout: 5s + retries: 10 + start_period: 10s + +networks: + default: + external: true + name: shared-redis-services diff --git a/docker_compose/mediahost/vaultwarden/docker-compose.yaml b/docker_compose/mediahost/vaultwarden/docker-compose.yaml new file mode 100644 index 0000000..b7fa254 --- /dev/null +++ b/docker_compose/mediahost/vaultwarden/docker-compose.yaml @@ -0,0 +1,27 @@ +services: + vaultwarden: + image: vaultwarden/server:latest + container_name: vaultwarden + restart: unless-stopped + environment: + WEBSOCKET_ENABLED: "true" + SIGNUPS_ALLOWED: "true" # Registrierung deaktivieren (optional) + DOMAIN: ${DOMAIN} + ADMIN_TOKEN: ${ADMIN_TOKEN} + SMTP_HOST: "smtp.web.de" + SMTP_FROM: "ll_m@web.de" + SMTP_PORT: 587 + SMTP_SSL: "false" + SMTP_SECURITY: starttls + SMTP_USERNAME: ll_m@web.de + SMTP_PASSWORD: ${SMTP_PASSWORD} + volumes: + - ./vaultwarden:/data + ports: + - "8081:80" + networks: + - shared-proxy-services +networks: + shared-proxy-services: + external: true + diff --git a/docker_compose/mediahost/wireguard/docker-compose.yaml b/docker_compose/mediahost/wireguard/docker-compose.yaml new file mode 100644 index 0000000..9f37107 --- /dev/null +++ b/docker_compose/mediahost/wireguard/docker-compose.yaml @@ -0,0 +1,20 @@ +services: + wireguard: + image: lscr.io/linuxserver/wireguard:latest + container_name: wireguard + cap_add: + - NET_ADMIN + - SYS_MODULE + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Vienna + - SERVERURL=someoneslinux.selfhost.eu + - SERVERPORT=51820 + - PEERS=5 + volumes: + - ./config:/config + - /lib/modules:/lib/modules:ro + ports: + - 51820:51820/udp + restart: unless-stopped