literate-dotfiles/homelab.org

99 KiB

Home Server

Hardware

Part Model
Case Fractal Design Node 804
Motherboard ASUS PRIME B450M-A
CPU AMD Ryzen 3 3200G
RAM Corsair Vengeance LPX 16Go (2x8Go) DDR4 3200MHz
Cooler ARCTIC Freezer 34 eSports DUO
PSU Corsair SF450
SSD M.2 Samsung 970 EVO Plus 250Gb
Disk Drives Various drives ranging from 3Tb to 8Tb
Home Server Hardware

Installation

Ubuntu

  • Download Ubuntu Server 20.04 LTS (link).
  • Activate OpenSSH and add SSH Keys
  • Account: thomas, hostname: homelab

Install Important software

sudo apt install neovim tmux fd-find ripgrep fzf apache2-utils unrar ranger

Terminal Problem

On the local host, using Termite:

infocmp > termite.terminfo  # export Termite's Terminfo
scp termite.terminfo user@remote-host:~/  # or any other method to copy to the remote host

On the remote host, in the directory where you copied termite.terminfo:

tic -x termite.terminfo  # import Terminfo for current user
rm termite.terminfo  # optional: remove Terminfo file

Minor Modifications of ~/.inputrc

Modify ~/.inputrc, like so:

"\e[A": history-search-backward            # arrow up
"\e[B": history-search-forward             # arrow down

Partition and Format Disk Drives

A nice tutorial is available here.

lsblk
sudo parted /dev/sda mklabel gpt
sudo parted -a opt /dev/sda mkpart "partitionname" ext4 0% 100%
sudo mkfs.ext4 -L partitionname /dev/sda1

MergerFS and FStab

MergerFS is a transparent layer that sits on top of the data drives providing a single mount point for reads / writes (link).

sudo apt install mergerfs

Create mount points

sudo mkdir /mnt/disk0
sudo mkdir /mnt/disk1
sudo mkdir /mnt/parity

Create folder where disks will be merged.

sudo mkdir /srv/storage

Edit /etc/fstab.

/dev/disk/by-uuid/7fb7873c-83bd-4805-98ab-506e6c7b56fa /mnt/disk0  ext4 defaults 0 0
/dev/disk/by-uuid/6574b7ae-321c-4078-9793-bc41a4fa5588 /mnt/disk1  ext4 defaults 0 0
/dev/disk/by-uuid/6fcd38b9-0886-46bd-900d-cb1f170dbcee /mnt/parity ext4 defaults 0 0

/mnt/disk* /srv/storage fuse.mergerfs direct_io,defaults,allow_other,minfreespace=50G,fsname=mergerfs 0 0

Automating with SnapRAID Runner

SnapRAID is a snapshot parity calculation tool which acts at the block level independent of filesystem (link).

SnapRAID is here used inside a Docker container (link).

Install Docker

The procedure is well explained here.

If docker is already installed, remove it:

sudo apt remove docker

Executing the Docker Command Without Sudo

sudo usermod -aG docker ${USER}

To apply the new group membership, log out of the server and back in, or type the following:

su - ${USER}

Install Docker-Compose

sudo apt install docker-compose

Setup Docker Networks

docker network create --gateway 192.168.90.1 --subnet 192.168.90.0/24 t2_proxy
docker network create docker_default

Change Timezone

sudo timedatectl set-timezone Europe/Paris

Secure the Web Server

Most of it comes from here.

  • Set PasswordAuthentication no in /etc/ssh/sshd_config

Automatic Security Updates

The procedure is well explained here.

sudo apt install unattended-upgrades update-notifier-common

Edit /etc/apt/apt.conf.d/50unattended-upgrades, and change the following lines:

Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";

Edit /etc/apt/apt.conf.d/20auto-upgrades:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

Setup cronjobs

Create a folder ~/cron with all the scripts and logs related to cron.

To edit the cron jobs, type crontab -e and add a line like:

*/5 * * * * /home/thomas/cron/caddy_update.sh >> /home/thomas/cron/caddy_update.log 2>&1

That will run every 5 minutes. To check how the first part of the crontab works, check this website.

Run docker-compose

cd ~/docker && docker-compose up -d

Docker config ~/.docker/config.json

{
  "psFormat": "table {{ .ID }}\\t{{ .Names }}\t{{ .Status }}"
}

Maintenance - How To

Update System/Packages

sudo -- sh -c 'apt-get update; apt-get upgrade -y; apt-get dist-upgrade -y; apt-get autoremove -y; apt-get autoclean -y'

Docker Commands

  • Starting a container: $ docker start homeassistant
  • Stopping a container: $ docker stop homeassistant
  • Restarting a container: $ docker restart homeassistant
  • Listing the running containers: $ docker ps or $ cd ~/docker/ && docker-compose ps
  • View the logs of a container: $ docker logs -f homeassistant
  • Drop a shell into a container: $ docker exec -it homeassistant /bin/bash
  • Update specific container: docker-compose pull --ignore-pull-failures homeassistant

Update All Containers

cd ~/docker/ && docker-compose pull --ignore-pull-failures && docker-compose up -d

Clean up Docker environment This will delete all unused images, volumes and networks.

docker system prune -f && docker image prune -f && docker volume prune -f

snapraid

To see all files "backed up" by snapraid, use:

docker exec -ti snapraid snapraid list | fzf

In reality, snapraid is ran from the docker container:

docker exec -ti snapraid snapraid fix -f <path_to_file>

The path to file should be relative: /srv/storage/Cloud/org/file.org -> Cloud/org/file.org

Restore Online backup with restic

To list backups:

docker exec restic restic snapshots
ID        Time                 Host          Tags        Paths
--------------------------------------------------------------------------------
a7b98408  2020-09-03 21:18:00  4803c2af7d4e              /data/documents/manuals
088e31a4  2020-09-03 21:50:26  4803c2af7d4e              /data/documents/manuals
9cf0b480  2020-09-03 22:05:47  4803c2af7d4e              /data/documents/manuals
--------------------------------------------------------------------------------
3 snapshots

Force backup of folder:

docker exec restic restic backup /data/documents/manuals

Files:           0 new,     2 changed,  8475 unmodified
Dirs:            0 new,     2 changed,     0 unmodified
Added to the repo: 1.010 KiB

processed 8477 files, 589.800 MiB in 0:02
snapshot 9cf0b480 saved

Find the path to the file within the snapshot:

docker exec restic restic find file_name

Find files only for a specific snapshot:

docker exec restic restic find -s latest file_name

Restore files/folders (replace file/folders):

docker exec restic restic restore --include /data/documents/manuals --target / 088e31a4

You can use latest instead of the ID.

If indeed, we want to make a copy of the file, we can use the backup folder

docker exec restic restic restore --include /data/documents/manuals --target /backup 088e31a4

Add wireguard client

With an Android client

Show the QRcode corresponding the a specific peer with:

docker exec -it wireguard /app/show-peer 1

Then, simply scan the QRcode with the Wireguard application.

With a Linux client

Copy the file $CONFIGDIR/wireguard/peeri/peeri.conf, e.g.:

[Interface]
Address = 10.13.13.4/24
DNS = 10.13.1.1
PrivateKey = ****
ListenPort = 51820

[Peer]
PublicKey = ****
Endpoint = wireguard.tdehaeze.xyz:51820
AllowedIPs = 0.0.0.0/0, ::0/0

Then, paste the file to /etc/wireguard/interfacename.conf. And then:

  • sudo chmod 600 /etc/wireguard/interfacename.conf
  • sudo chown root:root /etc/wireguard/interfacename.conf

Then, start the tunnel with:

wg-quick up interfacename

Docker-Compose

Basic Config

version: "3.4"
networks:
  t2_proxy:
    external:
      name: t2_proxy
  backend:
    external: false
  default:
    driver: bridge
x-logging:
  &default-logging
  driver: "json-file"
  options:
    max-size: "200k"
    max-file: "10"
services:

Docker Config and Tools

traefik - Application proxy (link)

  traefik:
    container_name: traefik
    image: traefik:2.2.1
    restart: unless-stopped
    depends_on:
      - authelia
    networks:
      t2_proxy:
        ipv4_address: 192.168.90.254 # You can specify a static IP
    security_opt:
      - no-new-privileges:true
    ports:
      - 80:80 # http
      - 443:443 # https
      - 8448:8448 # Matrix
    volumes:
      - $CONFIGDIR/traefik2/acme.json:/acme.json
      - $CONFIGDIR/traefik2/traefik.yaml:/etc/traefik/traefik.yaml
      - $CONFIGDIR/traefik2/services.yaml:/etc/traefik/services.yaml
      - /var/log/traefik:/var/log
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - CF_API_EMAIL=$CLOUDFLARE_EMAIL
      - CF_API_KEY=$CLOUDFLARE_API_KEY
    labels:
      - "traefik.enable=true"
      # HTTP-to-HTTPS Redirect
      - "traefik.http.routers.http-catchall.entrypoints=http"
      - "traefik.http.routers.http-catchall.rule=HostRegexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      # HTTP Routers
      - "traefik.http.routers.traefik-rtr.entrypoints=https"
      - "traefik.http.routers.traefik-rtr.rule=Host(`traefik.$DOMAINNAME`)"
      - "traefik.http.routers.traefik-rtr.tls=true"
      - "traefik.http.routers.traefik-rtr.middlewares=authelia@docker"
      - "traefik.http.routers.traefik-rtr.service=traefik-svc"
      - "traefik.http.routers.traefik-rtr.tls.domains[0].main=$DOMAINNAME"
      - "traefik.http.routers.traefik-rtr.tls.domains[0].sans=*.$DOMAINNAME"
      - "traefik.http.services.traefik-svc.loadbalancer.server.port=8080"
      # Services - API
      - "traefik.http.routers.traefik-rtr.service=api@internal"
      # Router
      - "traefik.http.routers.openwrt.entrypoints=https"
      - "traefik.http.routers.openwrt.rule=Host(`openwrt.$DOMAINNAME`)"
      - "traefik.http.routers.openwrt.tls=true"
      - "traefik.http.routers.openwrt.service=openwrt@file"
      # Valetudo
      - "traefik.http.routers.valetudo.entrypoints=https"
      - "traefik.http.routers.valetudo.rule=Host(`valetudo.$DOMAINNAME`)"
      - "traefik.http.routers.valetudo.tls=true"
      - "traefik.http.routers.valetudo.middlewares=authelia@docker"
      - "traefik.http.routers.valetudo.service=valetudo@file"
      # Basic Auth
      - "traefik.http.middlewares.wasabi-auth.basicauth.users=wasabi:$$2y$$05$$GWMXPYbVPtIbnKR8nJBUseTfuLn4vgMzMXtIqs.3.0Je9eUGbRiwG"
      - "traefik.http.middlewares.potaupho-auth.basicauth.users=potaupho:$$2y$$05$$0.hcoM36J1bhooHg6w/PBeP.HyxUpZwU7eFurq5RCrpaYNMVq1f4y"
    logging: *default-logging

traefik.yaml

global:
  checkNewVersion: true
  sendAnonymousUsage: false

entryPoints:
  http:
    address: :80
  https:
    address: :443
    forwardedHeaders:
      trustedIPs: 173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/12,172.64.0.0/13,131.0.72.0/22
  synapse:
    address: :8448

api:
  dashboard: true

log:
  level: ERROR

accessLog:
  filePath: /var/log/access.log
  filters:
    statusCodes: 400-499

providers:
  docker:
    endpoint: unix:///var/run/docker.sock
    defaultrule: Host(`{{ index .Labels "com.docker.compose.service" }}.$DOMAINNAME`)
    exposedByDefault: false
    network: t2_proxy
    swarmMode: false
  file:
    filename: /etc/traefik/services.yaml
    watch: true

certificatesResolvers:
  dns-cloudflare:
    acme:
      email: $CLOUDFLARE_EMAIL
      storage: /acme.json
      dnsChallenge:
        provider: cloudflare
        resolvers: 1.1.1.1:53,1.0.0.1:53
http:
  services:
    openwrt:
      loadBalancer:
        servers:
        - url: "http://192.168.1.1/"
    valetudo:
      loadBalancer:
        servers:
        - url: "http://192.168.1.110/"
        - url: "http://192.168.2.157/"
        healthCheck:
          path: /
          inverval: "60s"
          timeout: "3s"

authelia - Single Sign-On Multi-Factor portal (link)

  authelia:
    image: authelia/authelia:4.30
    container_name: authelia
    restart: unless-stopped
    networks:
      - t2_proxy
      - backend
    volumes:
      - $CONFIGDIR/authelia:/config
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - AUTHELIA_NOTIFIER_SMTP_PASSWORD=$AUTHELIA_NOTIFIER_SMTP_PASSWORD
      - AUTHELIA_JWT_SECRET=$AUTHELIA_JWT_SECRET
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.authelia-rtr.entrypoints=https"
      - "traefik.http.routers.authelia-rtr.tls=true"
      - "traefik.http.routers.authelia-rtr.service=authelia-svc"
      - "traefik.http.routers.authelia-rtr.rule=Host(`login.$DOMAINNAME`)"
      - "traefik.http.services.authelia-svc.loadbalancer.server.port=9091"
      - "traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://login.$DOMAINNAME/"
      - "traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true"
      - "traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User, Remote-Groups, Remote-Name, Remote-Email"
      - "treafik.http.middlewares.chain-authelia.chain.middlewares=middlewares-rate-limit, middlewares-secure-headers, middlewares-authelia"
      - "traefik.docker.network=t2_proxy"

configuration.yml

---
###############################################################
#                   Authelia configuration                    #
###############################################################

default_redirection_url: https://authelia.tdehaeze.xyz

server:
  host: 0.0.0.0
  port: 9091

log:
  level: debug

totp:
  issuer: authelia.com
  period: 30
  skew: 1

authentication_backend:
  file:
    path: /config/users_database.yml
    password:
      algorithm: argon2id
      iterations: 1
      salt_length: 16
      parallelism: 8
      memory: 1024

access_control:
  default_policy: deny
  rules:
    - domain: valetudo.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
      - ["group:family"]
    - domain: openwrt.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: traefik.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: scrutiny.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: portainer.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: sync-ju.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: sync-jm.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: sync-anne.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: syncthing.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: octoprint.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
      - ["group:family"]
    - domain: tina2.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
      - ["group:family"]
    - domain: uptime.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: wireguard.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: joal.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: change.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: esphome.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: node-red.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
      - ["group:family"]
    - domain: zigbee2mqtt.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
    - domain: qobuz.tdehaeze.xyz
      policy: bypass
      resources:
      - "^/download.*$"
    - domain: qobuz.tdehaeze.xyz
      policy: one_factor
      subject:
      - ["group:admins"]
      - ["group:friends"]
      - ["group:family"]

session:
  name: authelia_session
  expiration: 3600
  inactivity: 300
  domain: tdehaeze.xyz

regulation:
  max_retries: 3
  find_time: 120
  ban_time: 300

storage:
  local:
    path: /config/db.sqlite3

notifier:
  smtp:
    username: tdehaeze.xyz@gmail.com
    host: smtp.gmail.com
    port: 587
    sender: tdehaeze.xyz@gmail.com

portainer - Manage docker (link)

  portainer:
    container_name: portainer
    image: portainer/portainer
    restart: unless-stopped
    command: -H unix:///var/run/docker.sock
    command: --no-auth
    networks:
      - t2_proxy
    security_opt:
      - no-new-privileges:true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - $CONFIGDIR/portainer:/data
    environment:
      - TZ=$TZ
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.portainer-rtr.entrypoints=https"
      - "traefik.http.routers.portainer-rtr.rule=Host(`portainer.$DOMAINNAME`)"
      - "traefik.http.routers.portainer-rtr.tls=true"
      - "traefik.http.routers.portainer-rtr.service=portainer-svc"
      - "traefik.http.routers.portainer-rtr.middlewares=authelia@docker"
      - "traefik.http.services.portainer-svc.loadbalancer.server.port=9000"
    logging: *default-logging

uptime-kuma - Monitoring Tool (link)

  uptime-kuma:
    container_name: uptime-kuma
    image: louislam/uptime-kuma
    restart: unless-stopped
    networks:
      - t2_proxy
    volumes:
    environment:
      - TZ=$TZ
      - UID=$PUID
      - GID=$PGID
    volumes:
      - $CONFIGDIR/uptime-kuma:/app/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.uptime-rtr.entrypoints=https"
      - "traefik.http.routers.uptime-rtr.rule=Host(`uptime.$DOMAINNAME`)"
      - "traefik.http.routers.uptime-rtr.tls=true"
      - "traefik.http.routers.uptime-rtr.service=uptime-svc"
      - "traefik.http.routers.uptime-rtr.middlewares=authelia@docker"
      - "traefik.http.services.uptime-svc.loadbalancer.server.port=3001"
    logging: *default-logging

gotify - Notification service (link)

In order to have notifications on Linux desktop use gotify-dunst.

  gotify:
    container_name: gotify
    image: gotify/server
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - GOTIFY_DEFAULTUSER_NAME=$GOTIFY_DEFAULTUSER_NAME
      - GOTIFY_DEFAULTUSER_PASS=$GOTIFY_DEFAULTUSER_PASS
    volumes:
      - $CONFIGDIR/gotify:/app/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.gotify-rtr.entrypoints=https"
      - "traefik.http.routers.gotify-rtr.rule=Host(`gotify.$DOMAINNAME`)"
      - "traefik.http.routers.gotify-rtr.tls=true"
      - "traefik.http.routers.gotify-rtr.service=gotify-svc"
      - "traefik.http.services.gotify-svc.loadbalancer.server.port=80"

snapraid - Manage local backup with parity disk (link)

  snapraid:
    container_name: snapraid
    image: xagaba/snapraid
    restart: unless-stopped
    privileged: true
    volumes:
      - /mnt:/mnt
      - $CONFIGDIR/snapraid:/config
      - type: "bind"
        source: /dev/disk
        target: /dev/disk
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
    logging: *default-logging

snapraid.conf

# Defines the file to use as parity storage
# It must NOT be in a data disk
# Format: "parity FILE_PATH"
parity /mnt/parity/snapraid.parity

# Defines the files to use as content list
# You can use multiple specification to store more copies
# You must have least one copy for each parity file plus one. Some more don't
# hurt
# They can be in the disks used for data, parity or boot,
# but each file must be in a different disk
# Format: "content FILE_PATH"
content /var/snapraid.content
content /mnt/disk0/.snapraid.content
content /mnt/disk1/.snapraid.content

# Defines the data disks to use
# The order is relevant for parity, do not change it
# Format: "disk DISK_NAME DISK_MOUNT_POINT"
disk d0 /mnt/disk0
disk d1 /mnt/disk1

# Excludes hidden files and directories (uncomment to enable).
#nohidden

# Defines files and directories to exclude
# Remember that all the paths are relative at the mount points
# Format: "exclude FILE"
# Format: "exclude DIR/"
# Format: "exclude /PATH/FILE"
# Format: "exclude /PATH/DIR/"
exclude *.unrecoverable
exclude /tmp/
exclude /lost+found/
exclude *.!sync
exclude .AppleDouble
exclude ._AppleDouble
exclude .DS_Store
exclude ._.DS_Store
exclude .Thumbs.db
exclude .fseventsd
exclude .Spotlight-V100
exclude .TemporaryItems
exclude .Trashes
exclude .AppleDB

snapraid-runner.conf

[snapraid]
; path to the snapraid executable (e.g. /bin/snapraid)
executable = /usr/bin/snapraid
; path to the snapraid config to be used
config = /config/snapraid.conf
; abort operation if there are more deletes than this, set to -1 to disable
deletethreshold = -1
; if you want touch to be ran each time
touch = false

[logging]
; logfile to write to, leave empty to disable
file = /config/snapraid.log
; maximum logfile size in KiB, leave empty for infinite
maxsize = 5000

; [email]
; ; when to send an email, comma-separated list of [success, error]
; sendon = success,error
; ; set to false to get full programm output via email
; short = true
; subject = [SnapRAID] Status Report:
; from =
; to =
; ; maximum email size in KiB
; maxsize = 500
;
; [smtp]
; host =
; ; leave empty for default port
; port =
; ; set to "true" to activate
; ssl = false
; tls = false
; user =
; password =

[scrub]
; set to true to run scrub after sync
enabled = false
percentage = 12
older-than = 10

scrutiny - Hard drive monitoring (link)

  scrutiny:
    container_name: scrutiny
    image: linuxserver/scrutiny
    restart: unless-stopped
    networks:
      - t2_proxy
    cap_add:
      - SYS_RAWIO
      - SYS_ADMIN
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - SCRUTINY_WEB=true
      - SCRUTINY_COLLECTOR=false
    volumes:
      - $CONFIGDIR/scrutiny:/config
      - /run/udev:/run/udev:ro
    devices:
      - /dev/sda:/dev/sda
      - /dev/sdb:/dev/sdb
      - /dev/sdc:/dev/sdc
      - /dev/sdd:/dev/sdd
      - /dev/nvme0n1:/dev/nvme0n1
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.scrutiny-rtr.entrypoints=https"
      - "traefik.http.routers.scrutiny-rtr.rule=Host(`scrutiny.$DOMAINNAME`)"
      - "traefik.http.routers.scrutiny-rtr.tls=true"
      - "traefik.http.routers.scrutiny-rtr.service=scrutiny-svc"
      - "traefik.http.routers.scrutiny-rtr.middlewares=authelia@docker"
      - "traefik.http.services.scrutiny-svc.loadbalancer.server.port=8080"
    logging: *default-logging

wireguard - VPN (link)

  wireguard:
    container_name: wireguard
    image: linuxserver/wireguard
    restart: unless-stopped
    networks:
      - t2_proxy
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - SERVERURL=wireguard.tdehaeze.xyz
      - SERVERPORT=51820
      - PEERS=4
      - PEERDNS=8.8.8.8
    volumes:
      - $CONFIGDIR/wireguard:/config
      - /lib/modules:/lib/modules
    ports:
      - 51820:51820/udp
    logging: *default-logging
  wireguard:
    container_name: wireguard
    image: weejewel/wg-easy
    restart: unless-stopped
    networks:
      - t2_proxy
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - WG_HOST=wireguard.tdehaeze.xyz
      - PASSWORD=$WIREGUARD_PASS
    volumes:
      - $CONFIGDIR/wg-easy:/etc/wireguard
      - /lib/modules:/lib/modules
    ports:
      - 51820:51820/udp
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.wireguard-rtr.entrypoints=https"
      - "traefik.http.routers.wireguard-rtr.rule=Host(`wireguard.$DOMAINNAME`)"
      - "traefik.http.routers.wireguard-rtr.tls=true"
      - "traefik.http.routers.wireguard-rtr.service=wireguard-svc"
      - "traefik.http.routers.wireguard-rtr.middlewares=authelia@docker"
      - "traefik.http.services.wireguard-svc.loadbalancer.server.port=51821"
    logging: *default-logging

nginx - Root (used for Matrix)

  root:
    container_name: root
    image: nginx
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
    volumes:
      - $CONFIGDIR/root/nginx.conf:/etc/nginx/nginx.conf
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.root-rtr.entrypoints=https"
      - "traefik.http.routers.root-rtr.rule=Host(`$DOMAINNAME`)"
      - "traefik.http.routers.root-rtr.tls=true"
      - "traefik.http.routers.root-rtr.service=root-svc"
      - "traefik.http.services.root-svc.loadbalancer.server.port=8080"
    logging: *default-logging

nginx.conf

events {

}

http {
    server {
        server_name tdehaeze.xyz;
        listen 8080;

        location /.well-known/matrix/client {
            proxy_pass https://matrix.tdehaeze.xyz/.well-known/matrix/client;
            proxy_set_header X-Forwarded-For $remote_addr;
        }

        location /.well-known/matrix/server {
            proxy_pass https://matrix.tdehaeze.xyz/.well-known/matrix/server;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }
}

diun - Notification for Docker image updates (link)

  diun:
    container_name: diun
    image: crazymax/diun
    restart: unless-stopped
    networks:
      - backend
    environment:
      - TZ=$TZ
      - LOG_LEVEL=info
      - LOG_JSON=false
      - DIUN_WATCH_WORKERS=20
      - DIUN_WATCH_SCHEDULE=0 7 * * 6
      - DIUN_PROVIDERS_DOCKER=true
      - DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT=true
      - DIUN_NOTIF_GOTIFY_ENDPOINT=$GOTIFY_URL
      - DIUN_NOTIF_GOTIFY_TOKEN=$DIUN_GOTIFY_TOKEN
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - $CONFIGDIR/diun:/data
    logging: *default-logging

Websites

wasabi - Affichtoo

  wasabi:
    container_name: wasabi
    image: tdehaeze/affichtoo
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - UID=$PUID
      - GID=$PGID
      - TZ=$TZ
    volumes:
      - $CONFIGDIR/wasabi/config:/app/config
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.wasabi-rtr.entrypoints=https"
      - "traefik.http.routers.wasabi-rtr.rule=Host(`wasabi.$DOMAINNAME`)"
      - "traefik.http.routers.wasabi-rtr.tls=true"
      - "traefik.http.routers.wasabi-rtr.service=wasabi-svc"
      - "traefik.http.services.wasabi-svc.loadbalancer.server.port=8000"
      - "traefik.http.routers.wasabi-rtr.middlewares=wasabi-auth"
    logging: *default-logging

potaupho - Affichtoo

  potaupho:
    container_name: potaupho
    image: tdehaeze/affichtoo
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - UID=$PUID
      - GID=$PGID
      - TZ=$TZ
    volumes:
      - $CONFIGDIR/potaupho/config:/app/config
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.potaupho-rtr.entrypoints=https"
      - "traefik.http.routers.potaupho-rtr.rule=Host(`potaupho.$DOMAINNAME`)"
      - "traefik.http.routers.potaupho-rtr.tls=true"
      - "traefik.http.routers.potaupho-rtr.service=potaupho-svc"
      - "traefik.http.services.potaupho-svc.loadbalancer.server.port=8000"
      - "traefik.http.routers.potaupho-rtr.middlewares=potaupho-auth"
    logging: *default-logging

homer - Home page (link)

  homer:
    container_name: homer
    image: b4bz/homer
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - UID=$PUID
      - GID=$PGID
      - TZ=$TZ
    volumes:
      - $CONFIGDIR/homer/assets/:/www/assets
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.homer-rtr.entrypoints=https"
      - "traefik.http.routers.homer-rtr.rule=Host(`homer.$DOMAINNAME`)"
      - "traefik.http.routers.homer-rtr.tls=true"
      - "traefik.http.routers.homer-rtr.service=homer-svc"
      - "traefik.http.services.homer-svc.loadbalancer.server.port=8080"
    logging: *default-logging

config.yml

---
title: "Homepage"
subtitle: ""
logo: "assets/homer.png"
header: false
footer: false

columns: "auto"
connectivityCheck: false

theme: default

links: []

services:
  - name: "Websites"
    icon: "fas fa-desktop"
    items:
    - name: "Brain"
      logo: "/assets/tools/brain.png"
      subtitle: "Digital Brain"
      url: "https://brain.tdehaeze.xyz"
    - name: "Wiki"
      logo: "/assets/tools/wikijs.png"
      subtitle: "Shared Wiki"
      url: "https://wiki.tdehaeze.xyz"
    - name: "Research"
      logo: "/assets/tools/orgmode.png"
      subtitle: "Research Pages"
      url: "https://research.tdehaeze.xyz"
    - name: "Dotfiles"
      logo: "/assets/tools/dotfiles.png"
      subtitle: "My Literate Dotfiles"
      url: "https://dotfiles.tdehaeze.xyz"
    - name: "Miam"
      logo: "/assets/tools/miam.png"
      subtitle: "Personnal Recipes"
      url: "https://miam.tdehaeze.xyz"
  - name: "Multimedia"
    icon: "fas fa-photo-video"
    items:
    - name: "Jellyfin"
      logo: "/assets/tools/jellyfin.png"
      subtitle: "Media Library"
      url: "https://jellyfin.tdehaeze.xyz"
    - name: "Audioserve"
      logo: "/assets/tools/audiobook.png"
      subtitle: "Audiobook Server"
      url: "https://audiobook.tdehaeze.xyz"
    - name: "Kavita"
      logo: "/assets/tools/kavita.png"
      subtitle: "Book Library"
      url: "https://kavita.tdehaeze.xyz"
  - name: "Cloud"
    icon: "fas fa-cloud"
    items:
    - name: "File Browser"
      logo: "/assets/tools/cloud.png"
      subtitle: "Simple Personnal Could"
      url: "https://cloud.tdehaeze.xyz"
    - name: "Syncthing"
      logo: "/assets/tools/syncthing.png"
      subtitle: "P2P Sync"
      url: "https://syncthing.tdehaeze.xyz"
    - name: "Radicale"
      logo: "/assets/tools/radicale.png"
      subtitle: "CalDAV/CardDAV Server"
      url: "https://radicale.tdehaeze.xyz"
    - name: "Miniflux"
      logo: "/assets/tools/miniflux.png"
      subtitle: "RSS Feeds"
      url: "https://rss.tdehaeze.xyz"
    - name: "LinkDing"
      logo: "/assets/tools/linkding.png"
      subtitle: "Bookmark Manager"
      url: "https://bm.tdehaeze.xyz"
    - name: "Gitea"
      logo: "/assets/tools/gitea.png"
      subtitle: "Git Server"
      url: "https://git.tdehaeze.xyz"
  - name: "Download"
    icon: "fas fa-download"
    items:
    - name: "Down"
      logo: "/assets/tools/down.png"
      subtitle: "Torrent Download"
      url: "https://down.tdehaeze.xyz/"
    - name: "Qobuz"
      subtitle: "Music Download"
      logo: "/assets/tools/qobuz.png"
      url: "https://qobuz.tdehaeze.xyz"
    - name: "Transmission"
      logo: "/assets/tools/transmission.png"
      subtitle: "Torrent Client"
      url: "http://torrent.tdehaeze.xyz:9091/transmission/web/"
    - name: "Joal"
      logo: "/assets/tools/joal.png"
      subtitle: "Increase Ratio"
      url: "https://joal.tdehaeze.xyz/joal/ui/#/"
  - name: "Config"
    icon: "fas fa-cog"
    items:
    - name: "Portainer"
      logo: "/assets/tools/portainer.png"
      subtitle: "Manger Docker"
      url: "https://portainer.tdehaeze.xyz/#/containers"
    - name: "Traefik"
      logo: "/assets/tools/traefik.png"
      subtitle: "Reverse Proxy"
      url: "https://traefik.tdehaeze.xyz"
    - name: "Uptime"
      logo: "/assets/tools/uptime.png"
      subtitle: "Monitoring"
      url: "https://uptime.tdehaeze.xyz"
    - name: "Commento"
      logo: "/assets/tools/commento.png"
      subtitle: "Commenting System"
      url: "https://commento.tdehaeze.xyz"
    - name: "Gotify"
      logo: "/assets/tools/gotify.png"
      subtitle: "Messaging System"
      url: "https://gotify.tdehaeze.xyz"
    - name: "JFA-Go"
      logo: "/assets/tools/jellyfin.png"
      subtitle: "Manage Jellyfin Users"
      url: "http://jfa.tdehaeze.xyz/"
    - name: "Scrutiny"
      logo: "/assets/tools/scrutiny.png"
      subtitle: "S.M.A.R.T"
      url: "http://scrutiny.tdehaeze.xyz/web/dashboard"
  - name: "Home"
    icon: "fas fa-home"
    items:
    - name: "OpenWRT"
      logo: "/assets/tools/openwrt.png"
      subtitle: "Router"
      url: "http://192.168.1.1/"
    - name: "Home Assistant"
      logo: "/assets/tools/homeassistant.png"
      subtitle: "Home Assistant"
      url: "http://home.tdehaeze.xyz:8123"
    - name: "Changedetection.io"
      logo: "/assets/tools/changedetection.png"
      subtitle: "Detect change in websites"
      url: "https://change.tdehaeze.xyz"
    - name: "Zigbee2MQTT"
      logo: "/assets/tools/zigbee2mqtt.png"
      subtitle: "Zigbee2MQTT"
      url: "https://zigbee2mqtt.tdehaeze.xyz/"
    - name: "Node Red"
      logo: "/assets/tools/node-red.png"
      subtitle: "Event-driven applications"
      url: "https://node-red.tdehaeze.xyz/"
    - name: "ESPHome"
      logo: "/assets/tools/esphome.png"
      subtitle: "System to control ESP8266/ESP32"
      url: "https://esphome.tdehaeze.xyz/"
    - name: "OctoPrint"
      logo: "/assets/tools/octoprint.png"
      subtitle: "3D-Printing"
      url: "https://octoprint.tdehaeze.xyz/"

hugo - Wiki + Blog (link)

  hugo:
    container_name: hugo
    image: tdehaeze/hugo-caddy
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
        - REPO=git.tdehaeze.xyz/tdehaeze/digital-brain
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.hugo-rtr.entrypoints=https"
      - "traefik.http.routers.hugo-rtr.rule=Host(`brain.$DOMAINNAME`)"
      - "traefik.http.routers.hugo-rtr.tls=true"
      - "traefik.http.routers.hugo-rtr.service=hugo-svc"
      - "traefik.http.services.hugo-svc.loadbalancer.server.port=2015"
    logging: *default-logging

research - Research Pages (link)

  caddy:
    container_name: caddy
    image: abiosoft/caddy:1.0.3-no-stats
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - PLUGINS=git
    volumes:
      - $CONFIGDIR/caddy/Caddyfile:/etc/Caddyfile
      - $CONFIGDIR/web:/srv
        # - ~/.ssh:/root/.ssh
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.caddy-rtr.entrypoints=https"
      - "traefik.http.routers.caddy-rtr.rule=Host(`research.$DOMAINNAME`)"
      - "traefik.http.routers.caddy-rtr.tls=true"
      - "traefik.http.routers.caddy-rtr.service=caddy-svc"
      - "traefik.http.services.caddy-svc.loadbalancer.server.port=2015"
    logging: *default-logging

Caddyfile

0.0.0.0:2015 {
    root /srv/www/

    git {
        repo     https://git.tdehaeze.xyz/tdehaeze/research-home-page
        path     /srv/www/
        interval -1
        hook     /research-home-page/webhook QHZgAKjD8q2v54Ru
        then     git submodule update --init --recursive --merge
    }
}

dotfiles - Dotfiles (link)

  dotfiles:
    container_name: dotfiles
    image: abiosoft/caddy:1.0.3-no-stats
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - PLUGINS=git
    volumes:
      - $CONFIGDIR/dotfiles/Caddyfile:/etc/Caddyfile
      - $CONFIGDIR/dotfiles/www:/srv/www
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dotfiles-rtr.entrypoints=https"
      - "traefik.http.routers.dotfiles-rtr.rule=Host(`dotfiles.$DOMAINNAME`)"
      - "traefik.http.routers.dotfiles-rtr.tls=true"
      - "traefik.http.routers.dotfiles-rtr.service=dotfiles-svc"
      - "traefik.http.services.dotfiles-svc.loadbalancer.server.port=2015"
    logging: *default-logging

Caddyfile

0.0.0.0:2015 {
    root /srv/www/docs/

    git {
        repo     https://git.tdehaeze.xyz/tdehaeze/literate-dotfiles
        path     /srv/www/
        interval -1
        hook     /literate-dotfiles/webhook QHZgAKjD8q2v54Ru
    }
}

family-page - Dotfiles (link)

  family-page:
    container_name: family-page
    image: abiosoft/caddy:1.0.3-no-stats
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - PLUGINS=git
    volumes:
      - $CONFIGDIR/family-page/Caddyfile:/etc/Caddyfile
      - $CONFIGDIR/family-page/www:/srv/www
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.family-page-rtr.entrypoints=https"
      - "traefik.http.routers.family-page-rtr.rule=Host(`help.$DOMAINNAME`)"
      - "traefik.http.routers.family-page-rtr.tls=true"
      - "traefik.http.routers.family-page-rtr.service=family-page-svc"
      - "traefik.http.services.family-page-svc.loadbalancer.server.port=2015"
    logging: *default-logging

Caddyfile

0.0.0.0:2015 {
    root /srv/www/

    git {
        repo     https://git.tdehaeze.xyz/tdehaeze/family-page
        path     /srv/www/
        interval -1
        hook     /family-page/webhook 0fdVzNShbcmw
    }
}

wikijs - Wiki App (link)

  wikijs:
    image: ghcr.io/linuxserver/wikijs:version-2.5.201
    container_name: wikijs
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
    volumes:
      - $CONFIGDIR/wikijs/config:/config
      - $CONFIGDIR/wikijs/data:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.wikijs-rtr.entrypoints=https"
      - "traefik.http.routers.wikijs-rtr.rule=Host(`wiki.$DOMAINNAME`)"
      - "traefik.http.routers.wikijs-rtr.tls=true"
      - "traefik.http.routers.wikijs-rtr.service=wikijs-svc"
      - "traefik.http.services.wikijs-svc.loadbalancer.server.port=3000"
    logging: *default-logging

commento - Commenting system (link)

  commento:
    container_name: commento
    image: registry.gitlab.com/commento/commento
    restart: unless-stopped
    networks:
      - t2_proxy
      - backend
    # ports:
    #   - 8080:8080
    environment:
      - TZ=$TZ
      - UID=$PUID
      - GID=$PGID
      - COMMENTO_ORIGIN=https://commento.tdehaeze.xyz/
      - COMMENTO_PORT=8080
      - COMMENTO_POSTGRES=postgres://postgres:$COMMENTO_DB_PASSWORD@commento_db:5432/commento?sslmode=disable
      - COMMENTO_SMTP_HOST=smtp.gmail.com
      - COMMENTO_SMTP_PORT=587
      - COMMENTO_SMTP_USERNAME=tdehaeze.xyz@gmail.com
      - COMMENTO_SMTP_PASSWORD=$GMAIL_PASS
      - COMMENTO_SMTP_FROM_ADDRESS=tdehaeze.xyz@gmail.com
    depends_on:
      - commento_db
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.commento-rtr.entrypoints=https"
      - "traefik.http.routers.commento-rtr.rule=Host(`commento.$DOMAINNAME`)"
      - "traefik.http.routers.commento-rtr.tls=true"
      - "traefik.http.routers.commento-rtr.service=commento-svc"
      - "traefik.http.services.commento-svc.loadbalancer.server.port=8080"
    logging: *default-logging
  commento_db:
    container_name: commento_db
    image: postgres:13
    restart: unless-stopped
    networks:
      - backend
    environment:
      - POSTGRES_DB=commento
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=$COMMENTO_DB_PASSWORD
    volumes:
      - $CONFIGDIR/commento_db:/var/lib/postgresql/data
    logging: *default-logging

mealie - Recipe Manager (link)

  miam:
    container_name: miam
    image: hkotel/mealie
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - db_type=sqlite
      - UID=$PUID
      - GID=$PGID
      - TZ=$TZ
    volumes:
      - $CONFIGDIR/mealie:/app/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.miam-rtr.entrypoints=https"
      - "traefik.http.routers.miam-rtr.rule=Host(`miam.$DOMAINNAME`)"
      - "traefik.http.routers.miam-rtr.tls=true"
      - "traefik.http.routers.miam-rtr.service=miam-svc"
      - "traefik.http.services.miam-svc.loadbalancer.server.port=80"
    logging: *default-logging

gitea - Git server (link)

  gitea:
    container_name: git
    image: gitea/gitea:1.13.2
    depends_on:
      - gitea_db
    restart: unless-stopped
    networks:
      - t2_proxy
      - backend
    volumes:
      - $CONFIGDIR/gitea:/data
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - SSH_PORT=$GITEA_SSH_PORT
    ports:
      - "2222:22"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.git-rtr.entrypoints=https"
      - "traefik.http.routers.git-rtr.rule=Host(`git.$DOMAINNAME`)"
      - "traefik.http.routers.git-rtr.tls=true"
      - "traefik.http.routers.git-rtr.service=git-svc"
      - "traefik.http.services.git-svc.loadbalancer.server.port=3000"
    logging: *default-logging
  gitea_db:
    container_name: gitea_db
    image: mariadb:10
    restart: unless-stopped
    networks:
      - backend
    ports:
      - 3306:3306
    environment:
      - MYSQL_ROOT_PASSWORD=$GITEA_DB_MYSQL_ROOT_PASSWORD
      - MYSQL_DATABASE=gitea
      - MYSQL_USER=gitea
      - MYSQL_PASSWORD=$GITEA_DB_MYSQL_PASSWORD
    volumes:
      - $CONFIGDIR/mariadb:/var/lib/mysql

changedetection - Detect change in websites (link)

  changedetection:
    container_name: changedetection
    image: ghcr.io/dgtlmoon/changedetection.io
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - UID=$PUID
      - GID=$PGID
      - TZ=$TZ
      - BASE_URL=https://change.tdehaeze.xyz
    volumes:
      - $CONFIGDIR/changedetection:/datastore
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.changedetection-rtr.entrypoints=https"
      - "traefik.http.routers.changedetection-rtr.rule=Host(`change.$DOMAINNAME`)"
      - "traefik.http.routers.changedetection-rtr.tls=true"
      - "traefik.http.routers.changedetection-rtr.service=changedetection-svc"
      - "traefik.http.routers.changedetection-rtr.middlewares=authelia@docker"
      - "traefik.http.services.changedetection-svc.loadbalancer.server.port=5000"
    logging: *default-logging

Multimedia

jellyfin - Media server (link)

  jellyfin:
    container_name: jellyfin
    image: linuxserver/jellyfin
    restart: unless-stopped
    networks:
      - t2_proxy
    volumes:
      - $CONFIGDIR/jellyfin:/config
      - /srv/storage/TVShows:/data/tvshows
      - /srv/storage/Documentaries:/data/documentaries
      - /srv/storage/LiveMusic:/data/livemusic
      - /srv/storage/Animes:/data/animes
      - /srv/storage/Movies:/data/movies
      - /srv/storage/Music:/data/music
      - /srv/storage/StandUp:/data/standup
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
    group_add:
      - 109
    devices:
      # VAAPI Devices
      - /dev/dri/renderD128:/dev/dri/renderD128
      - /dev/dri/card0:/dev/dri/card0
    ports:
      - 8096:8096
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.jellyfin-rtr.entrypoints=https"
      - "traefik.http.routers.jellyfin-rtr.rule=Host(`jellyfin.$DOMAINNAME`)"
      - "traefik.http.routers.jellyfin-rtr.tls=true"
      - "traefik.http.routers.jellyfin-rtr.service=jellyfin-svc"
      - "traefik.http.services.jellyfin-svc.loadbalancer.server.port=8096"
    logging: *default-logging

jfa-go - Manage Jellyfin Users (link)

  jfa:
    container_name: jfa
    image: hrfee/jfa-go
    restart: unless-stopped
    depends_on:
      - jellyfin
    networks:
      - t2_proxy
    volumes:
      - $CONFIGDIR/jfa:/data
      - $CONFIGDIR/jellyfin:/jf
      - /etc/localtime:/etc/localtime:ro
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.jfa-rtr.entrypoints=https"
      - "traefik.http.routers.jfa-rtr.rule=Host(`jfa.$DOMAINNAME`)"
      - "traefik.http.routers.jfa-rtr.tls=true"
      - "traefik.http.routers.jfa-rtr.service=jfa-svc"
      - "traefik.http.services.jfa-svc.loadbalancer.server.port=8056"
    logging: *default-logging

audioserve - Audiobook server (link)

  audioserve:
    container_name: audioserve
    image: izderadicka/audioserve
    restart: unless-stopped
    command: /audiobooks
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - AUDIOSERVE_SHARED_SECRET=$AUDIOSERVE_SHARED_SECRET
    volumes:
      - /srv/storage/AudioBooks:/audiobooks
      - /etc/localtime:/etc/localtime:ro
      - $CONFIGDIR/audioserve:/home/audioserve/.audioserve
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.audioserve-rtr.entrypoints=https"
      - "traefik.http.routers.audioserve-rtr.rule=Host(`audiobook.$DOMAINNAME`)"
      - "traefik.http.routers.audioserve-rtr.tls=true"
      - "traefik.http.routers.audioserve-rtr.service=audioserve-svc"
      - "traefik.http.services.audioserve-svc.loadbalancer.server.port=3000"
    logging: *default-logging

TODO kavita - Reading server (link)

  kavita:
    container_name: kavita
    image: kizaing/kavita:latest
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - TZ=$TZ
      - UID=$PUID
      - GID=$PGID
    volumes:
      - $CONFIGDIR/kavita:/kavita/config
      - /srv/storage/Books:/books
      - /srv/storage/Scans:/scans
      - /srv/storage/Comics:/comics
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.kavita-rtr.entrypoints=https"
      - "traefik.http.routers.kavita-rtr.rule=Host(`kavita.$DOMAINNAME`)"
      - "traefik.http.routers.kavita-rtr.tls=true"
      - "traefik.http.routers.kavita-rtr.service=kavita-svc"
      - "traefik.http.services.kavita-svc.loadbalancer.server.port=5000"
    logging: *default-logging

Cloud

syncthing - File Synchronization (link)

  syncthing:
    container_name: syncthing
    image: linuxserver/syncthing
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - UMASK_SET=022
    volumes:
      - $CONFIGDIR/syncthing:/config
      - /srv/storage/Cloud:/Cloud
      - /srv/storage/Cloud/pictures/phone:/Pictures
      - /srv/storage/Cloud/pdfs:/Onyx/Download
      - /srv/storage/Cloud/pdfs-notes:/Onyx/note
      - /srv/storage/Cloud/.stfolder:/Onyx/.stfolder
      - /srv/storage/.password-store:/.password-store
    ports:
      - 22000:22000
      - 21027:21027/udp
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.syncthing-rtr.entrypoints=https"
      - "traefik.http.routers.syncthing-rtr.rule=Host(`syncthing.$DOMAINNAME`)"
      - "traefik.http.routers.syncthing-rtr.tls=true"
      - "traefik.http.routers.syncthing-rtr.service=syncthing-svc"
      - "traefik.http.routers.syncthing-rtr.middlewares=authelia@docker"
      - "traefik.http.services.syncthing-svc.loadbalancer.server.port=8384"
    logging: *default-logging

sync-anne - File Synchronization (link)

  sync-anne:
    container_name: sync-anne
    image: linuxserver/syncthing
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - UMASK_SET=022
    volumes:
      - $CONFIGDIR/sync-anne:/config
      - /srv/storage/Users/anne:/Cloud
      - /srv/storage/Users/anne/Photos/telephone:/telephone
    ports:
      - 22001:22001
      - 21028:21028/udp
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.sync-anne-rtr.entrypoints=https"
      - "traefik.http.routers.sync-anne-rtr.rule=Host(`sync-anne.$DOMAINNAME`)"
      - "traefik.http.routers.sync-anne-rtr.tls=true"
      - "traefik.http.routers.sync-anne-rtr.service=sync-anne-svc"
      - "traefik.http.routers.sync-anne-rtr.middlewares=authelia@docker"
      - "traefik.http.services.sync-anne-svc.loadbalancer.server.port=8384"
    logging: *default-logging

sync-jm - File Synchronization (link)

  sync-jm:
    container_name: sync-jm
    image: linuxserver/syncthing
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - UMASK_SET=022
    volumes:
      - $CONFIGDIR/sync-jm:/config
      - /srv/storage/Users/jean-marie:/Cloud
      # - /srv/storage/Users/jean-marie/Photos/telephone:/telephone
    ports:
      - 22002:22002
      - 21029:21029/udp
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.sync-jm-rtr.entrypoints=https"
      - "traefik.http.routers.sync-jm-rtr.rule=Host(`sync-jm.$DOMAINNAME`)"
      - "traefik.http.routers.sync-jm-rtr.tls=true"
      - "traefik.http.routers.sync-jm-rtr.service=sync-jm-svc"
      - "traefik.http.routers.sync-jm-rtr.middlewares=authelia@docker"
      - "traefik.http.services.sync-jm-svc.loadbalancer.server.port=8384"
    logging: *default-logging

sync-ju - File Synchronization (link)

  sync-ju:
    container_name: sync-ju
    image: linuxserver/syncthing
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - UMASK_SET=022
    volumes:
      - $CONFIGDIR/sync-ju:/config
      - /srv/storage/Users/juliette:/Cloud
    ports:
      - 22003:22003
      - 21030:21030/udp
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.sync-ju-rtr.entrypoints=https"
      - "traefik.http.routers.sync-ju-rtr.rule=Host(`sync-ju.$DOMAINNAME`)"
      - "traefik.http.routers.sync-ju-rtr.tls=true"
      - "traefik.http.routers.sync-ju-rtr.service=sync-ju-svc"
      - "traefik.http.routers.sync-ju-rtr.middlewares=authelia@docker"
      - "traefik.http.services.sync-ju-svc.loadbalancer.server.port=8384"
    logging: *default-logging

filebrowser - Web file browser (link)

  filebrowser:
    container_name: filebrowser
    image: filebrowser/filebrowser
    restart: unless-stopped
    networks:
      - t2_proxy
    volumes:
      - $CONFIGDIR/filebrowser/database.db:/database.db
      - $CONFIGDIR/filebrowser/.filebrowser.json:/.filebrowser.json
      - /srv/storage:/srv/storage
    user: "${PUID}:${PGID}"
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.filebrowser-rtr.entrypoints=https"
      - "traefik.http.routers.filebrowser-rtr.rule=Host(`cloud.$DOMAINNAME`)"
      - "traefik.http.routers.filebrowser-rtr.tls=true"
      - "traefik.http.routers.filebrowser-rtr.service=filebrowser-svc"
      - "traefik.http.services.filebrowser-svc.loadbalancer.server.port=80"
    logging: *default-logging

.filebrowser.json

{
  "port": 80,
  "baseURL": "",
  "address": "",
  "log": "stdout",
  "database": "/database.db",
  "root": "/srv/storage"
}

TODO photoprism (link)

  photoprism:
    container_name: photoprism
    image: photoprism/photoprism:latest
    depends_on:
      - photoprism_db
    networks:
      - t2_proxy
      - backend
    security_opt:
      - seccomp:unconfined
      - apparmor:unconfined
    user: "${PUID}:${PGID}"
    environment:
      PHOTOPRISM_ADMIN_PASSWORD: "pi3DnacevhCQ"
      PHOTOPRISM_SITE_URL: "https://photos.tdehaeze.xyz/"
      PHOTOPRISM_ORIGINALS_LIMIT: 500
      PHOTOPRISM_READONLY: "false"
      PHOTOPRISM_DISABLE_WEBDAV: "true"
      PHOTOPRISM_DISABLE_TENSORFLOW: "true"
      PHOTOPRISM_DISABLE_FACES: "true"
      PHOTOPRISM_DISABLE_CLASSIFICATION: "true"
      PHOTOPRISM_DARKTABLE_PRESETS: "false"          # Enables Darktable presets and disables concurrent RAW conversion
      PHOTOPRISM_DATABASE_DRIVER: "mysql"
      PHOTOPRISM_DATABASE_SERVER: "photoprism_db:3306"
      PHOTOPRISM_DATABASE_NAME: "photoprism"
      PHOTOPRISM_DATABASE_USER: "photoprism"
      PHOTOPRISM_DATABASE_PASSWORD: "PMsLF5577UagH08c"
      PHOTOPRISM_UID: $PUID
      PHOTOPRISM_GID: $PGID
      HOME: "/photoprism"
    working_dir: "/photoprism"
    volumes:
      - "/srv/storage/Cloud/pictures:/photoprism/originals"               # original media files (photos and videos)
      - "$CONFIGDIR/photoprism/storage:/photoprism/storage"                  # *writable* storage folder for cache, database, and sidecar files (never remove)
      # - "$CONFIGDIR/photoprism/originals:/photoprism/originals"               # original media files (photos and videos)
      # - "/example/family:/photoprism/originals/family" # *additional* media folders can be mounted like this
      # - "~/Import:/photoprism/import"                  # *optional* base folder from which files can be imported to originals
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.photos-rtr.entrypoints=https"
      - "traefik.http.routers.photos-rtr.rule=Host(`photos.$DOMAINNAME`)"
      - "traefik.http.routers.photos-rtr.tls=true"
      - "traefik.http.routers.photos-rtr.service=photos-svc"
      - "traefik.http.services.photos-svc.loadbalancer.server.port=2342"
    logging: *default-logging
  photoprism_db:
    container_name: photoprism_db
    restart: unless-stopped
    image: mariadb:10.6
    networks:
      - backend
    security_opt:
      - seccomp:unconfined
      - apparmor:unconfined
    command: mysqld --innodb-buffer-pool-size=128M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
    volumes:
      - "$CONFIGDIR/photoprism_db/database:/var/lib/mysql"
    environment:
      MYSQL_ROOT_PASSWORD: ZyZ4vhBmnERp5Amt
      MYSQL_DATABASE: photoprism
      MYSQL_USER: photoprism
      MYSQL_PASSWORD: PMsLF5577UagH08c

radicale - CalDAV/CardDAV server (link)

  radicale:
    container_name: radicale
    image: tomsquest/docker-radicale:latest
    restart: unless-stopped
    networks:
      - t2_proxy
    volumes:
      - $CONFIGDIR/radicale/config:/config:ro
      - $CONFIGDIR/radicale/data:/data
    environment:
      - TZ=$TZ
      - UID=$PUID
      - GID=$PGID
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - SETUID
      - SETGID
      - CHOWN
      - KILL
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.radicale-rtr.entrypoints=https"
      - "traefik.http.routers.radicale-rtr.rule=Host(`radicale.$DOMAINNAME`)"
      - "traefik.http.routers.radicale-rtr.tls=true"
      - "traefik.http.routers.radicale-rtr.service=radicale-svc"
      - "traefik.http.services.radicale-svc.loadbalancer.server.port=5232"
    logging: *default-logging

config

[server]
hosts = 0.0.0.0:5232

[auth]
type = htpasswd
htpasswd_filename = /config/users
htpasswd_encryption = md5

[storage]
filesystem_folder = /data/collections

linkding - Bookmark manager (link)

  linkding:
    container_name: linkding
    image: sissbruecker/linkding:latest
    restart: unless-stopped
    networks:
      - t2_proxy
    volumes:
      - $CONFIGDIR/linkding:/etc/linkding/data
    environment:
      - TZ=$TZ
      - PUID=$PUID
      - PGID=$PGID
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.linkding-rtr.entrypoints=https"
      - "traefik.http.routers.linkding-rtr.rule=Host(`bm.$DOMAINNAME`)"
      - "traefik.http.routers.linkding-rtr.tls=true"
      - "traefik.http.routers.linkding-rtr.service=linkding-svc"
      - "traefik.http.services.linkding-svc.loadbalancer.server.port=9090"
    logging: *default-logging

restic - Automatic online backups (link)

  restic:
    container_name: restic
    image: mazzolino/restic
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - BACKUP_CRON=0 30 0 * * *
      - RESTIC_REPOSITORY=b2:tdehaeze:/restic
      - RESTIC_PASSWORD=$RESTIC_PASSWORD
      - RESTIC_BACKUP_SOURCES=/source
      - RESTIC_FORGET_ARGS=--group-by tag --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune
      - RESTIC_BACKUP_ARGS=--tag local --exclude-file /exclude.txt
      - B2_ACCOUNT_ID=$RESTIC_B2_ACCOUNT_ID
      - B2_ACCOUNT_KEY=$RESTIC_B2_ACCOUNT_KEY
      - UID=$PUID
      - GID=$PGID
      - TZ=$TZ
    volumes:
      - $CONFIGDIR/restic/exclude.txt:/exclude.txt:ro
      - /srv/storage/Cloud/thesis:/source/Cloud/thesis:ro
      - /home/thomas/docker:/source/docker:ro
    logging: *default-logging

exclude.txt - Exclude files

*.db
*.log
*.log.*
/source/docker/config/gitea/git/
/source/docker/config/guacamole/
/source/docker/config/guacamole_db/
/source/docker/config/mariadb/
/source/docker/config/miniflux_db/
/source/docker/config/jellyfin/data/
/source/docker/config/dotfiles/www/
/source/docker/config/web/www/

miniflux - RSS reader (link)

  miniflux:
    container_name: miniflux
    image: miniflux/miniflux
    restart: unless-stopped
    networks:
      - t2_proxy
      - backend
    depends_on:
      - miniflux_db
    environment:
      - DATABASE_URL=postgres://miniflux:SCJWWXqHwehP7f8g@miniflux_db/miniflux?sslmode=disable
      - RUN_MIGRATIONS=1
      - CREATE_ADMIN=1
      - ADMIN_USERNAME=$MINIFLUX_ADMIN_NAME
      - ADMIN_PASSWORD=$MINIFLUX_ADMIN_PASS
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.miniflux-rtr.entrypoints=https"
      - "traefik.http.routers.miniflux-rtr.rule=Host(`rss.$DOMAINNAME`)"
      - "traefik.http.routers.miniflux-rtr.tls=true"
      - "traefik.http.routers.miniflux-rtr.service=miniflux-svc"
      - "traefik.http.services.miniflux-svc.loadbalancer.server.port=8080"
    logging: *default-logging
  miniflux_db:
    container_name: miniflux_db
    image: postgres:12
    restart: unless-stopped
    networks:
      - backend
    environment:
      - POSTGRES_USER=miniflux
      - POSTGRES_PASSWORD=$MINIFLUX_POSTGRES_PASSWORD
    volumes:
      - $CONFIGDIR/miniflux_db:/var/lib/postgresql/data
    logging: *default-logging

Home

homeassistant - Home Automation (link)

  homeassistant:
    container_name: homeassistant
    image: homeassistant/home-assistant
    restart: unless-stopped
    #networks:
    #  - t2_proxy
    #ports:
    #  - target: 8123
    #    published: 8123
    #    protocol: tcp
    #    mode: host
    privileged: true
    ports:
      - 8123:8123
    # network_mode: host
    volumes:
      - $CONFIGDIR/homeassistant:/config
      - /etc/localtime:/etc/localtime:ro
      - /dev/bus/usb:/dev/bus/usb
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.homeassistant-rtr.entrypoints=https"
      - "traefik.http.routers.homeassistant-rtr.rule=Host(`home.$DOMAINNAME`)"
      - "traefik.http.routers.homeassistant-rtr.tls=true"
      - "traefik.http.routers.homeassistant-rtr.service=homeassistant-svc"
      - "traefik.http.services.homeassistant-svc.loadbalancer.server.port=8123"
      # - "traefik.http.services.homeassistant-svc.loadbalancer.servers.url=http://172.17.0.1:8123"
    logging: *default-logging

mosquitto - MQTT broker (link)

  mosquitto:
    container_name: mosquitto
    image: eclipse-mosquitto
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - UID=$PUID
      - GID=$PGID
      - TZ=$TZ
    expose:
      - 1883
      - 9001
    ports:
      - 1883:1883
      - 9001:9001
    volumes:
      - $CONFIGDIR/mosquitto/config:/mosquitto/config
      - $CONFIGDIR/mosquitto/log:/mosquitto/log
      - $CONFIGDIR/mosquitto/data:/mosquitto/data
    logging: *default-logging

zigbee2mqtt - Zigbee to MQTT bridge (link)

  zigbee2mqtt:
    container_name: zigbee2mqtt
    image: koenkk/zigbee2mqtt
    restart: unless-stopped
    privileged: true
    depends_on:
      - mosquitto
    networks:
      - t2_proxy
    environment:
      - UID=$PUID
      - GID=$PGID
      - TZ=$TZ
    volumes:
      - $CONFIGDIR/zigbee2mqtt:/app/data
      - /run/udev:/run/udev:ro
    devices:
      - /dev/serial/by-id/usb-Silicon_Labs_slae.sh_cc2652rb_stick_-_slaesh_s_iot_stuff_00_12_4B_00_23_93_39_57-if00-port0:/dev/ttyUSB0
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.zigbee2mqtt-rtr.entrypoints=https"
      - "traefik.http.routers.zigbee2mqtt-rtr.rule=Host(`zigbee2mqtt.$DOMAINNAME`)"
      - "traefik.http.routers.zigbee2mqtt-rtr.tls=true"
      - "traefik.http.routers.zigbee2mqtt-rtr.service=zigbee2mqtt-svc"
      - "traefik.http.routers.zigbee2mqtt-rtr.middlewares=authelia@docker"
      - "traefik.http.services.zigbee2mqtt-svc.loadbalancer.server.port=8080"
    logging: *default-logging

node-red - Automation tool

  node-red:
    container_name: node-red
    image: nodered/node-red:latest
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
    volumes:
      - $CONFIGDIR/node-red:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.node-red-rtr.entrypoints=https"
      - "traefik.http.routers.node-red-rtr.rule=Host(`node-red.$DOMAINNAME`)"
      - "traefik.http.routers.node-red-rtr.tls=true"
      - "traefik.http.routers.node-red-rtr.service=node-red-svc"
      - "traefik.http.routers.node-red-rtr.middlewares=authelia@docker"
      - "traefik.http.services.node-red-svc.loadbalancer.server.port=1880"
    logging: *default-logging

esphome - Automation tool

  esphome:
    container_name: esphome
    image: esphome/esphome:latest
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - ESPHOME_DASHBOARD_USE_PING=true
    volumes:
      - $CONFIGDIR/esphome:/config
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.esphome-rtr.entrypoints=https"
      - "traefik.http.routers.esphome-rtr.rule=Host(`esphome.$DOMAINNAME`)"
      - "traefik.http.routers.esphome-rtr.tls=true"
      - "traefik.http.routers.esphome-rtr.service=esphome-svc"
      - "traefik.http.routers.esphome-rtr.middlewares=authelia@docker"
      - "traefik.http.services.esphome-svc.loadbalancer.server.port=6052"
    logging: *default-logging

alfawiseu20 - Web interface for 3D printing (link)

  octoprint:
    container_name: alfawiseu20
    image: octoprint/octoprint
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - UID=$PUID
      - GID=$PGID
      - TZ=$TZ
    privileged: true
    volumes:
      - $CONFIGDIR/alfawiseu20:/octoprint
      - /dev/bus/usb:/dev/bus/usb
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.alfawiseu20-rtr.entrypoints=https"
      - "traefik.http.routers.alfawiseu20-rtr.rule=Host(`alfawiseu20.$DOMAINNAME`)"
      - "traefik.http.routers.alfawiseu20-rtr.tls=true"
      - "traefik.http.routers.alfawiseu20-rtr.service=alfawiseu20-svc"
      - "traefik.http.routers.alfawiseu20-rtr.middlewares=authelia@docker"
      - "traefik.http.services.alfawiseu20-svc.loadbalancer.server.port=80"
    logging: *default-logging

tina2 - Web interface for 3D printing (link)

  tina2:
    container_name: tina2
    image: octoprint/octoprint
    restart: unless-stopped
    networks:
      - t2_proxy
    environment:
      - UID=$PUID
      - GID=$PGID
      - TZ=$TZ
    privileged: true
    volumes:
      - $CONFIGDIR/tina2:/octoprint
      - /dev/bus/usb:/dev/bus/usb
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.tina2-rtr.entrypoints=https"
      - "traefik.http.routers.tina2-rtr.rule=Host(`tina2.$DOMAINNAME`)"
      - "traefik.http.routers.tina2-rtr.tls=true"
      - "traefik.http.routers.tina2-rtr.service=tina2-svc"
      - "traefik.http.routers.tina2-rtr.middlewares=authelia@docker"
      - "traefik.http.services.tina2-svc.loadbalancer.server.port=80"
    logging: *default-logging

Download

gluetun - Provide VPN connection to other containers (link)

  gluetun:
    image: qmcgaw/gluetun
    container_name: gluetun
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    network_mode: bridge
    ports:
      - 8065:8065 # For transmission
      - 9091:9091 # For transmission
      - 51413:51413 # For transmission
      - 51413:51413/udp # For transmission
    environment:
      - OPENVPN_USER=$NORDVPN_NAME
      - OPENVPN_PASSWORD=$NORDVPN_PASS
      - VPNSP=nordvpn
      - REGION=France
      - SERVER_NUMBER=684
      - TZ=$TZ
    volumes:
      - $CONFIGDIR/gluetun:/config
    logging: *default-logging

transmission - Torrent client (link)

  transmission:
    container_name: transmission
    image: ghcr.io/linuxserver/transmission
    restart: unless-stopped
    network_mode: container:gluetun
    depends_on:
      - gluetun
    environment:
      - PUID=$PUID
      - PGID=$PGID
      - TZ=$TZ
      - USER=$TRANSMISSION_NAME
      - PASS=$TRANSMISSION_PASS
    volumes:
      - $CONFIGDIR/transmission:/config
      - /srv/storage/Downloads:/downloads
      - /srv/storage/Downloads/watch:/watch
    logging: *default-logging

Cron Jobs

Caddy Update

Create a script ~/cron/caddy_update.sh with:

docker exec caddy /bin/sh -c "cd /srv/www && echo -e \"Update repo $(date)\" && git submodule update --recursive --remote --merge"

Type crontab -e and add this line:

*/5 * * * * /home/thomas/cron/caddy_update.sh >> /home/thomas/cron/caddy_update.log 2>&1