From 3b859555e727b47e34adc214cba62e39237fe9f3 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 26 Mar 2026 17:37:02 +0100 Subject: [PATCH] initial commit --- README.md | 28 +++++++- containers/aria2/docker-compose.yml | 52 ++++++++++++++ containers/immich/.env | 24 +++++++ containers/immich/docker-compose.yml | 72 ++++++++++++++++++++ containers/navidrome/docker-compose.yml | 31 +++++++++ containers/traefik/docker-compose.yml | 40 +++++++++++ containers/traefik/dynamic.yml | 6 ++ crontab/certs.crontab | 1 + scripts/backup-navidrome-music.sh | 21 ++++++ scripts/cert_sync.sh | 8 +++ systemd-units/backup-navidrome-music.service | 9 +++ systemd-units/backup-navidrome-music.timer | 9 +++ 12 files changed, 299 insertions(+), 2 deletions(-) create mode 100644 containers/aria2/docker-compose.yml create mode 100644 containers/immich/.env create mode 100644 containers/immich/docker-compose.yml create mode 100644 containers/navidrome/docker-compose.yml create mode 100644 containers/traefik/docker-compose.yml create mode 100644 containers/traefik/dynamic.yml create mode 100644 crontab/certs.crontab create mode 100755 scripts/backup-navidrome-music.sh create mode 100755 scripts/cert_sync.sh create mode 100644 systemd-units/backup-navidrome-music.service create mode 100644 systemd-units/backup-navidrome-music.timer diff --git a/README.md b/README.md index 5a36793..72300ff 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,26 @@ -# docker -docker compose and settings on my homelab +# docker compose and settings on my homelab. + +This repo is a save of the evolution and changes of my docker host in my home-lab. +it is structured around this principles: +- portainer is used on the host, but only as a front-end to docker without ssh'ing needed. +- every services is located in /srv with a subfolder per app + - /srv/immich + - /srv/navidrome + - /srv/traefik +- The container should as musch as possible rely on trafik to expose the service. limit the ports exposed on the host to the minimum +- Certificates are generated on the vps by certbot, and copied every nights to the docker host via a script to keep the locally served file protected with the same certs +- pi-hole is used to define local dns entries for each publicly exposed service, implementing a split-zone and avoiding nat hairpin + +# Folder structure +/containers +: Contain the docker-compose.yml file with dependencies and .env + +/crontab +: The content of a custom crontab file used + +/scripts +: Manually written scripts, deployed in **/usr/local/bin** + +/systemd-units +: Systemd services and timers to deploy to **/etc/systemd/system** + diff --git a/containers/aria2/docker-compose.yml b/containers/aria2/docker-compose.yml new file mode 100644 index 0000000..2231bb4 --- /dev/null +++ b/containers/aria2/docker-compose.yml @@ -0,0 +1,52 @@ +version: "3.8" + +services: + + Aria2-Pro: + container_name: aria2-pro + image: p3terx/aria2-pro + environment: + - PUID=1000 + - PGID=1000 + - UMASK_SET=002 + - RPC_SECRET=kaiThoh6 + - RPC_PORT=6800 + - LISTEN_PORT=6888 + - DISK_CACHE=64M + - IPV6_MODE=false + - UPDATE_TRACKERS=true + - CUSTOM_TRACKER_URL= + - TZ=Europe/Zurich + volumes: + - ${PWD}/aria2-config:/config + #- ${PWD}/aria2-downloads:/downloads + - /mnt/bulk/aria2:/downloads +# If you use host network mode, then no port mapping is required. +# This is the easiest way to use IPv6 networks. + network_mode: host +# network_mode: bridge +# ports: +# - 6800:6800 +# - 6888:6888 +# - 6888:6888/udp + restart: unless-stopped +# Since Aria2 will continue to generate logs, limit the log size to 1M to prevent your hard disk from running out of space. + logging: + driver: json-file + options: + max-size: 1m + +# AriaNg is just a static web page, usually you only need to deploy on a single host. + AriaNg: + container_name: ariang + image: p3terx/ariang + command: --port 6880 --ipv6 + network_mode: host +# network_mode: bridge +# ports: +# - 6880:6880 + restart: unless-stopped + logging: + driver: json-file + options: + max-size: 1m diff --git a/containers/immich/.env b/containers/immich/.env new file mode 100644 index 0000000..ddff3f5 --- /dev/null +++ b/containers/immich/.env @@ -0,0 +1,24 @@ +# You can find documentation for all the supported env variables at https://docs.immich.app/install/environment-variables + +# The location where your uploaded files are stored +UPLOAD_LOCATION=/srv/immich/library + +# The location where your database files are stored. Network shares are not supported for the database +DB_DATA_LOCATION=/srv/immich/postgres + +# To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List +# TZ=Etc/UTC +TZ=Europe/Zurich + +# The Immich version to use. You can pin this to a specific version like "v2.1.0" +IMMICH_VERSION=release + +# Connection secret for postgres. You should change it to a random password +# Please use only the characters `A-Za-z0-9`, without special characters or spaces +DB_PASSWORD=kaiThoh0 + +# The values below this line do not need to be changed +################################################################################### +DB_USERNAME=postgres +DB_DATABASE_NAME=immich + diff --git a/containers/immich/docker-compose.yml b/containers/immich/docker-compose.yml new file mode 100644 index 0000000..f2edcc6 --- /dev/null +++ b/containers/immich/docker-compose.yml @@ -0,0 +1,72 @@ +name: immich + +services: + immich-server: + container_name: immich_server + image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release} + volumes: + # On laisse Immich utiliser UPLOAD_LOCATION depuis .env + - ${UPLOAD_LOCATION}:/data + - /etc/localtime:/etc/localtime:ro + - /mnt/rsync/instantUpload:/mnt/InstantUpload + env_file: + - .env + labels: + - "traefik.enable=true" + - "traefik.docker.network=proxy" + - "traefik.http.routers.immich.rule=Host(`immich.schork.ch`)" + - "traefik.http.routers.immich.entrypoints=websecure" + - "traefik.http.routers.immich.tls=true" + - "traefik.http.services.immich.loadbalancer.server.port=2283" + depends_on: + - redis + - database + restart: always + healthcheck: + disable: false + networks: + - default + - proxy + + immich-machine-learning: + container_name: immich_machine_learning + image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release} + volumes: + - model-cache:/cache + env_file: + - .env + restart: always + healthcheck: + disable: false + + redis: + container_name: immich_redis + image: docker.io/valkey/valkey:9@sha256:546304417feac0874c3dd576e0952c6bb8f06bb4093ea0c9ca303c73cf458f63 + healthcheck: + test: redis-cli ping || exit 1 + restart: always + + database: + container_name: immich_postgres + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23 + environment: + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_USER: ${DB_USERNAME} + POSTGRES_DB: ${DB_DATABASE_NAME} + POSTGRES_INITDB_ARGS: '--data-checksums' + # DB_STORAGE_TYPE: 'HDD' # à activer si ton stockage n'est pas SSD (dans ton cas, NVMe => laisse commenté) + volumes: + - ${DB_DATA_LOCATION}:/var/lib/postgresql/data + shm_size: 128mb + restart: always + #ports: + # - '5432:5432' + healthcheck: + disable: false + +volumes: + model-cache: + +networks: + proxy: + external: true diff --git a/containers/navidrome/docker-compose.yml b/containers/navidrome/docker-compose.yml new file mode 100644 index 0000000..8e9e399 --- /dev/null +++ b/containers/navidrome/docker-compose.yml @@ -0,0 +1,31 @@ +services: + navidrome: + image: deluan/navidrome:latest + user: 1000:1000 # should be owner of volumes + restart: unless-stopped + environment: + ND_LOGLEVEL: info + ND_ENABLESHARING: true + ND_SCANNER_ENABLED: true + ND_SCANNER_SCHEDULE: '@every 24h' + ND_SPOTIFY_ID: "34332c79b9574d8fae5cc2ea2da164f9" + ND_SPOTIFY_SECRET: "12c7d266343844b4a05deeb1420ed857" + ND_LASTFM_ENABLED: true + ND_LASTFM_APIKEY: "f59ca50517732f5adc21aca83d7b5605" + ND_LASTFM_SECRET: "876f6f25e1847cb0d7bf2b58ce78b02e" + volumes: + - "/srv/navidrome/cfg:/data" + - "/srv/navidrome/music:/music:ro" + networks: + - proxy + labels: + - "traefik.enable=true" + - "traefik.docker.network=proxy" + - "traefik.http.routers.navidrome.rule=Host(`navidrome.schork.ch`)" + - "traefik.http.routers.navidrome.entrypoints=websecure" + - "traefik.http.routers.navidrome.tls=true" + - "traefik.http.services.navidrome.loadbalancer.server.port=4533" + +networks: + proxy: + external: true diff --git a/containers/traefik/docker-compose.yml b/containers/traefik/docker-compose.yml new file mode 100644 index 0000000..16d95cc --- /dev/null +++ b/containers/traefik/docker-compose.yml @@ -0,0 +1,40 @@ +services: + traefik: + image: traefik:v3.6 + container_name: traefik + restart: unless-stopped + environment: + DOCKER_API_VERSION: "1.45" + command: + - --api.dashboard=true + - --providers.docker=true + - --providers.docker.exposedbydefault=false + + - --entrypoints.web.address=:80 + - --entrypoints.websecure.address=:443 + + - --entrypoints.web.http.redirections.entrypoint.to=websecure + - --entrypoints.web.http.redirections.entrypoint.scheme=https + + - --providers.file.filename=/etc/traefik/dynamic.yml + + # Exemple DNS challenge - provider à adapter + - --certificatesresolvers.letsencrypt.acme.dnschallenge=true + - --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare + + # utile pendant le debug + - --accesslog=true + - --log.level=INFO + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /srv/certs:/certs:ro + - ./dynamic.yml:/etc/traefik/dynamic.yml:ro + networks: + - proxy + +networks: + proxy: + external: true diff --git a/containers/traefik/dynamic.yml b/containers/traefik/dynamic.yml new file mode 100644 index 0000000..a26f8e5 --- /dev/null +++ b/containers/traefik/dynamic.yml @@ -0,0 +1,6 @@ +tls: + certificates: + - certFile: /certs/immich.schork.ch/fullchain.pem + keyFile: /certs/immich.schork.ch/privkey.pem + - certFile: /certs/navidrome.schork.ch/fullchain.pem + keyFile: /certs/navidrome.schork.ch/privkey.pem diff --git a/crontab/certs.crontab b/crontab/certs.crontab new file mode 100644 index 0000000..3aee832 --- /dev/null +++ b/crontab/certs.crontab @@ -0,0 +1 @@ +30 0 * * * /usr/local/bin/cert_sync.sh 2>&1 diff --git a/scripts/backup-navidrome-music.sh b/scripts/backup-navidrome-music.sh new file mode 100755 index 0000000..f0eb931 --- /dev/null +++ b/scripts/backup-navidrome-music.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -euo pipefail + +SRC="/srv/navidrome/music/" +DST="zh3289@zh3289.rsync.net:/data1/home/zh3289/navidrome/Music/" +LOCKFILE="/run/backup-navidrome-music.lock" + +exec 9>"$LOCKFILE" +flock -n 9 || { + echo "Backup already running" + exit 1 +} + +echo "==== $(date -Is) backup start ====" + +rsync -aH --delete \ + --info=progress2,stats \ + "$SRC" "$DST" + +echo "==== $(date -Is) backup done ====" + diff --git a/scripts/cert_sync.sh b/scripts/cert_sync.sh new file mode 100755 index 0000000..0c603b0 --- /dev/null +++ b/scripts/cert_sync.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e + +#scp -r schork.ch:/etc/letsencrypt/live/* /srv/certs/ +rsync -avzL schork.ch:/etc/letsencrypt/live/ /srv/certs +chmod -R 600 /srv/certs + +docker restart traefik diff --git a/systemd-units/backup-navidrome-music.service b/systemd-units/backup-navidrome-music.service new file mode 100644 index 0000000..4ca0d2d --- /dev/null +++ b/systemd-units/backup-navidrome-music.service @@ -0,0 +1,9 @@ +[Unit] +Description=Backup Navidrome music to rsync.net +Wants=network-online.target +After=network-online.target + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/backup-navidrome-music.sh + diff --git a/systemd-units/backup-navidrome-music.timer b/systemd-units/backup-navidrome-music.timer new file mode 100644 index 0000000..a23df13 --- /dev/null +++ b/systemd-units/backup-navidrome-music.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Daily backup of Navidrome music to rsync.net + +[Timer] +OnCalendar=*-*-* 03:30:00 +Persistent=true + +[Install] +WantedBy=timers.target