Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .claude/skills/debug-container-service/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ scripts/labctl.py service logs category/service-name --timestamps

**Key insight from logs:** Pay attention to the source location (e.g. `module.file:function:line`). An error in `process_chat` vs `images_endpoint` are different code paths that may require completely different fixes, even if the symptom looks the same.

**If docker logs return empty or near-empty output:** some services (notably Traefik) write all logs to files rather than stdout. Skip immediately to Step 3 — do not waste tool calls on alternative retrieval methods (e.g. querying an internal API).

If dependent services are suspected, check their logs too.

---
Expand Down Expand Up @@ -171,6 +173,8 @@ Choose the least invasive fix, in this order:
2. **Mount a patched file via Docker volume** — add a bind mount in the service YAML pointing to a patched copy stored in the repo (e.g. `./patched_file.py:/app/path/file.py:ro`); document clearly why it exists
3. **Custom Docker image** — only if a volume mount is not feasible; significantly increases maintenance burden

**When adding a service to the `proxy` network:** remove any host port binding for the port Traefik proxies (e.g. `8080:8080`). Traefik reaches the container via Docker networking — the host binding is redundant and will conflict if another service uses the same port. Ports needed for direct external access (e.g. BitTorrent, UDP services) should remain.

**Always revert wrong fixes before applying the correct one.** Accumulating incorrect changes obscures the actual state and makes future debugging harder.

After applying any fix, recreate the container to pick up changes:
Expand Down
14 changes: 11 additions & 3 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ tasks:
desc: Create a compressed backup of the configuration directory
cmds:
- mkdir -p ../infra-config-backup
- cd config && tar -cjf "../infra-config-backup/infra-config-$(date +%Y-%m-%d_%H-%M-%S).tar.bz2" --exclude=.git .
- cd config && tar -cjf "../../infra-config-backup/infra-config-$(date +%Y-%m-%d_%H-%M-%S).tar.bz2" --exclude=.git .
- echo "Recent backups:"
- ls -lh ../infra-config-backup/infra-config-*.tar.bz2 | tail -n 5
# Remove old backups (they are still kept in the repository - see the push-config-repo task)
Expand Down Expand Up @@ -171,8 +171,7 @@ tasks:
git push
fi

# Setup scheduling with command `crontab -e`, add this line:
# 0 1 * * * cd /home/buba/repos/infra && /home/linuxbrew/.linuxbrew/bin/task backup >> config/log/infra-backup.log 2>&1
# Run `task setup-crontab` to install the cron schedule automatically.
backup:
desc: Create backup of the configuration directory and the Obsidian vault
silent: true
Expand All @@ -186,6 +185,15 @@ tasks:
bash "config/sync-backup-to-remote.sh"
fi

setup-crontab:
desc: Install or update the daily backup cron job
cmds:
- |
MARKER="# managed:infra-backup"
CRON_ENTRY="0 1 * * * cd \"$(pwd)\" && PATH=\"/home/linuxbrew/.linuxbrew/bin:$HOME/.local/bin:\$PATH\" task backup >> $HOME/infra-backup.log 2>&1 $MARKER"
(crontab -l 2>/dev/null | grep -vF "$MARKER"; echo "$CRON_ENTRY") | crontab -
echo "Crontab entry installed/updated."
Comment thread
coderabbitai[bot] marked this conversation as resolved.

get-offline-data:
desc: Download data files for offline use (stored both local and on Proxmox)
cmds:
Expand Down
2 changes: 1 addition & 1 deletion ansible/inventory/group_vars/debian/vars.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ debian_tools_brew_packages_kubernetes:
- helm # https://helm.sh/docs/intro/install/

debian_samba_client_servers:
- hostname: 192.168.1.12
- hostname: colony
shares:
- media
- family-media
Expand Down
1 change: 0 additions & 1 deletion ansible/playbooks/homelab.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
- name: Setup Samba client
hosts:
- nest
- colony
roles:
- role: debian_samba_client

Expand Down
8 changes: 4 additions & 4 deletions config-example/docker/.env
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ MYDOMAIN_TLS_SANS=*.${MYDOMAIN},*.dev.${MYDOMAIN}

### Nodes - Also update in ansible/inventory/inventory.yaml

MAIN_NODE=nest
MAIN_NODE_IP=xxx.xxx.xxx.xxx
PRIMARY_NODE=nest
PRIMARY_NODE_IP=xxx.xxx.xxx.xxx

SECOND_NODE=hive
SECOND_NODE_IP=xxx.xxx.xxx.xxx
SECONDARY_NODE=hive
SECONDARY_NODE_IP=xxx.xxx.xxx.xxx

### Local accounts

Expand Down
2 changes: 1 addition & 1 deletion docker/Taskfile.apps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ tasks:
LOCAL_PORT: 11434
cmds:
- echo "Forwarding remote port (${REMOTE_PORT}) to local Ollama port (${LOCAL_PORT}). Press Ctrl+C to to stop."
- ssh -N -o 'ExitOnForwardFailure yes' -R "*:${REMOTE_PORT}:localhost:${LOCAL_PORT}" ${ADMIN_USER}@${MAIN_NODE}
- ssh -N -o 'ExitOnForwardFailure yes' -R "*:${REMOTE_PORT}:localhost:${LOCAL_PORT}" ${ADMIN_USER}@${PRIMARY_NODE}
4 changes: 2 additions & 2 deletions docker/Taskfile.docker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ tasks:
cmds:
# Process .env files
- |
SOURCE_HOSTDIR="${CONFIG_DIR}/${MAIN_NODE}"
SOURCE_HOSTDIR="${CONFIG_DIR}/${PRIMARY_NODE}"
TARGET_HOSTDIR="${EXAMPLE_DIR}/myhost"
mkdir -p "${TARGET_HOSTDIR}"
echo "Processing: ${CONFIG_DIR}/.env -> ${EXAMPLE_DIR}/.env"
Expand All @@ -89,5 +89,5 @@ tasks:
python3 "$SCRIPT" "$env_file" "$target_filename"
done
# Process services.yaml, services-schema.yaml
- sed "s/${MAIN_NODE}/myhost/g" config/docker/${MAIN_NODE}/services.yaml > config-example/docker/myhost/services.yaml
- sed "s/${PRIMARY_NODE}/myhost/g" config/docker/${PRIMARY_NODE}/services.yaml > config-example/docker/myhost/services.yaml
- cp config/docker/services-schema.yaml config-example/docker/services-schema.yaml
14 changes: 14 additions & 0 deletions docker/ai/ollama/ollama-amdgpu.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# GPU override for Ollama — AMD ROCm hardware inference.
# Enable by setting GPU_COMPOSE_SUFFIX=amdgpu in config/docker/<hostname>/.env
# Switches to the :rocm image tag and passes both DRI and KFD devices.
---
name: ollama
services:
ollama:
image: ollama/ollama:0.30.10-rocm
devices:
- /dev/dri/renderD128:/dev/dri/renderD128
- /dev/kfd:/dev/kfd
group_add:
- "${GPU_RENDER_GID}" # render group — numeric GID required (name may not exist in container)
- "${GPU_VIDEO_GID}" # video group
10 changes: 1 addition & 9 deletions docker/ai/ollama/ollama.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,7 @@
name: ollama
services:
ollama:
# Enable for GPU support (see ollama documentation for CUDA driver setup)
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# count: 1
# capabilities:
# - gpu
# GPU acceleration: set GPU_COMPOSE_SUFFIX=amdgpu in host .env to load ollama-amdgpu.yaml (ROCm/AMD)
image: ollama/ollama:0.30.10
container_name: ollama
restart: unless-stopped
Expand Down
2 changes: 1 addition & 1 deletion docker/ai/sillytavern/sillytavern.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ services:
initContainer:
image: busybox
container_name: sillytavern-init
command: grep -q '^\s*-\s*172\.18\.0\.1\/16' /home/node/app/config/config.yaml || sed -i '/whitelist:/a \ \ - 172.18.0.1\/16' /home/node/app/config/config.yaml
command: sh -c 'grep -q "172\.16\.0\.0/12" /home/node/app/config/config.yaml || sed -i "s|172\\.18\\.0\\.1/16|172.16.0.0/12|; /^whitelist:/a - 172.16.0.0/12" /home/node/app/config/config.yaml'
volumes:
- ${DOCKER_VOLUMES}/sillytavern/config:/home/node/app/config
network_mode: none
Expand Down
4 changes: 2 additions & 2 deletions docker/automation/homeassistant/homeassistant.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# http:
# use_x_forwarded_for: true
# trusted_proxies:
# - 172.18.0.0/24
# - 172.16.0.0/12 # All Docker bridge networks
Comment thread
bubacoder marked this conversation as resolved.
# ```
#
# Links:
Expand All @@ -21,7 +21,7 @@ services:
container_name: homeassistant
restart: unless-stopped
network_mode: host
hostname: ${MAIN_NODE}
hostname: ${PRIMARY_NODE}
environment:
PUID: ${PUID}
PGID: ${PGID}
Expand Down
4 changes: 2 additions & 2 deletions docker/dashboard/homepage/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

- Connections:
- Router Web UI:
href: https://router.{{HOMEPAGE_VAR_MYDOMAIN}}/cgi-bin/luci/
href: https://router.{{HOMEPAGE_VAR_MYDOMAIN}}/
siteMonitor: https://router.{{HOMEPAGE_VAR_MYDOMAIN}}/
description: OpenWRT LuCi
description: Mikrotik hEX Router
icon: router.png
- Proxmox Web UI:
href: https://proxmox.{{HOMEPAGE_VAR_MYDOMAIN}}/
Expand Down
2 changes: 1 addition & 1 deletion docker/fileshare/qbittorrent/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# kics-scan ignore-line - Missing User Instruction
FROM lscr.io/linuxserver/qbittorrent:4.6.7
FROM lscr.io/linuxserver/qbittorrent:5.2.2

WORKDIR /usr
RUN curl -LO https://github.com/WDaan/VueTorrent/releases/latest/download/vuetorrent.zip && \
Expand Down
26 changes: 21 additions & 5 deletions docker/fileshare/qbittorrent/qbittorrent.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# The qBittorrent project aims to provide an open-source software alternative to µTorrent. qBittorrent is based on the Qt toolkit and libtorrent-rasterbar library.
# The default username/password is `admin` / `adminadmin`.
# The default username/password is printed to the terminal on container start.
#
# Recommended setup steps:
# - Configure "Bypass authentication for clients in whitelisted IP subnets" (Settings -> Webui) - Authelia handles the authentication
Expand All @@ -17,8 +17,21 @@ services:
build: .
container_name: qbittorrent
restart: unless-stopped
# network_mode: host
# hostname: ${MAIN_NODE}
networks:
- proxy
# If running on a separate host (not the Traefik host), remove the networks section above,
# add "- 8080:8080" to the ports section below so the WebUI is reachable on the host,
# and add a router+service entry in docker/security/traefik/config/dynamic/external-services.yml:
# routers:
# qbittorrent:
# rule: 'Host(`qbittorrent.${MYDOMAIN}`)'
# service: qbittorrent
# middlewares: [localaccess@file]
# services:
# qbittorrent:
# loadBalancer:
# servers:
# - url: http://<host>:8080
environment:
PUID: ${PUID}
PGID: ${PGID}
Expand All @@ -28,9 +41,8 @@ services:
- ${DOCKER_VOLUMES}/qbittorrent:/config
- ${STORAGE_DOWNLOADS}/torrent-downloads:/downloads
- ${STORAGE_MEDIA}:/media
# Not needed if 'network_mode: host' is set
ports:
- 8080:8080 # Web UI
# 8080 not exposed on host - Traefik reaches it via proxy network
- 6881:6881/tcp # Bittorrent incoming connections
- 6881:6881/udp # Bittorrent incoming connections
labels:
Expand All @@ -43,3 +55,7 @@ services:
homepage.href: https://qbittorrent.${MYDOMAIN}/
homepage.description: "BitTorrent client"
com.centurylinklabs.watchtower.enable: false

networks:
proxy:
external: true
4 changes: 2 additions & 2 deletions docker/infra/adguardhome-sync/adguardhome-sync.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ services:
environment:
# Config reference: https://github.com/bakito/adguardhome-sync?tab=readme-ov-file#config-via-environment-variables
CRON: "0 */2 * * *"
ORIGIN_URL: "http://${MAIN_NODE_IP}:3000"
ORIGIN_URL: "http://${PRIMARY_NODE_IP}:3000"
ORIGIN_USERNAME: ${ADGUARDHOME_USERNAME}
ORIGIN_PASSWORD: ${ADGUARDHOME_PASSWORD}
REPLICA1_URL: "http://${SECOND_NODE_IP}:3000"
REPLICA1_URL: "http://${SECONDARY_NODE_IP}:3000"
REPLICA1_USERNAME: ${ADGUARDHOME_USERNAME}
REPLICA1_PASSWORD: ${ADGUARDHOME_PASSWORD}
API_PORT: 8080
Expand Down
12 changes: 12 additions & 0 deletions docker/media/video/jellyfin/jellyfin-amdgpu.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# GPU override for Jellyfin — AMD VAAPI hardware transcoding.
# Enable by setting GPU_COMPOSE_SUFFIX=amdgpu in config/docker/<hostname>/.env
# After deploying, configure VA-API in Jellyfin UI: Dashboard → Playback → Transcoding → VA-API
---
name: jellyfin
services:
jellyfin:
devices:
- /dev/dri/renderD128:/dev/dri/renderD128
group_add:
- "${GPU_RENDER_GID}" # render group — numeric GID required (name may not exist in container)
- "${GPU_VIDEO_GID}" # video group
4 changes: 3 additions & 1 deletion docker/media/video/jellyfin/jellyfin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@
name: jellyfin
services:
jellyfin:
# GPU acceleration: set GPU_COMPOSE_SUFFIX=amdgpu in host .env to load jellyfin-amdgpu.yaml (VAAPI/AMD).
# Then enable VA-API in Jellyfin UI: Dashboard → Playback → Transcoding.
image: lscr.io/linuxserver/jellyfin:10.11.11
container_name: jellyfin
restart: unless-stopped
environment:
PUID: ${PUID}
PGID: ${PGID}
TZ: ${TIMEZONE}
JELLYFIN_PublishedServerUrl: ${MAIN_NODE_IP}
JELLYFIN_PublishedServerUrl: https://jellyfin.${MYDOMAIN}
volumes:
- ${DOCKER_VOLUMES}/jellyfin:/config
- ${STORAGE_MEDIA}:/media:ro
Expand Down
7 changes: 7 additions & 0 deletions docker/security/frigate/config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ tls:
# auth:
# enabled: false

# Docs: https://docs.frigate.video/configuration/hardware_acceleration_video
# Docs: https://docs.frigate.video/configuration/ffmpeg_presets
# AMD VAAPI hardware acceleration for video decode
# Enable GPU_COMPOSE_SUFFIX=amdgpu in host .env to load frigate-amdgpu.yaml (VAAPI/AMD).
ffmpeg:
hwaccel_args: preset-vaapi
Comment thread
bubacoder marked this conversation as resolved.

# Docs: https://docs.frigate.video/configuration/cameras
cameras:
reolink_front:
Expand Down
14 changes: 14 additions & 0 deletions docker/security/frigate/frigate-amdgpu.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# GPU override for Frigate — AMD VAAPI video decode for camera streams.
# Enable by setting GPU_COMPOSE_SUFFIX=amdgpu in config/docker/<hostname>/.env
# Also set hwaccel in config/config.yml:
# ffmpeg:
# hwaccel_args: preset-vaapi
---
name: frigate
services:
frigate:
devices:
- /dev/dri/renderD128:/dev/dri/renderD128
group_add:
- "${GPU_RENDER_GID}" # render group — numeric GID required (name may not exist in container)
- "${GPU_VIDEO_GID}" # video group
3 changes: 2 additions & 1 deletion docker/security/frigate/frigate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
# - Install: https://docs.frigate.video/frigate/installation#docker
# - Config reference: https://docs.frigate.video/configuration/
#
# GPU acceleration: set GPU_COMPOSE_SUFFIX=amdgpu in host .env to load frigate-amdgpu.yaml (VAAPI/AMD).
#
# TODO: Configure cameras and detection settings in config.yml
# TODO: Set up hardware acceleration (Coral TPU, Intel QuickSync, etc.) by adding device mappings
# TODO: Adjust shm-size based on camera count and resolution (calculate: width × height × 1.5 × 20 + 270480 bytes + 40MB)
# TODO: Consider integration with Home Assistant for enhanced automation
# TODO: Configure motion detection zones to reduce false positives
Expand Down
10 changes: 0 additions & 10 deletions docker/security/traefik/config/dynamic/external-services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ http:
service: proxmox
middlewares:
- localaccess@file
qbittorrent:
rule: 'Host(`qbittorrent.{{env "MYDOMAIN"}}`)'
service: qbittorrent
middlewares:
- localaccess@file

services:
router:
Expand All @@ -33,8 +28,3 @@ http:
serversTransport: insecureTransport
servers:
- url: https://192.168.1.50:8006
qbittorrent:
loadBalancer:
serversTransport: insecureTransport
servers:
- url: http://nas:8080
3 changes: 2 additions & 1 deletion docker/security/traefik/config/traefik.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,5 @@ experimental:
# https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin
crowdsec-bouncer-traefik-plugin:
moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
version: "v1.4.1"
# renovate: datasource=github-releases depName=maxlerebourg/crowdsec-bouncer-traefik-plugin
version: "v1.6.0"
1 change: 0 additions & 1 deletion docker/security/traefik/traefik.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ services:
environment:
TZ: ${TIMEZONE}
MYDOMAIN: ${MYDOMAIN}
MAIN_NODE_IP: ${MAIN_NODE_IP}
CLOUDFLARE_DNS_API_TOKEN: ${CLOUDFLARE_DNS_API_TOKEN}
# Generate key within the crowdsec container: cscli bouncers add traefik-bouncer
CROWDSEC_BOUNCER_API_KEY: ${CROWDSEC_BOUNCER_API_KEY}
Expand Down
2 changes: 1 addition & 1 deletion docker/security/wg-easy/wg-easy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ services:
WG_HOST: vpn.${MYDOMAIN} # The public hostname of your VPN server
# PASSWORD: ${ADMIN_PASSWORD} # When set, requires a password when logging in to the Web UI -> Disabled, Authelia SSO is configured
# WG_DEFAULT_DNS: 1.1.1.1,8.8.8.8 # DNS server clients will use
WG_DEFAULT_DNS: ${MAIN_NODE_IP} # DNS server clients will use
WG_DEFAULT_DNS: ${PRIMARY_NODE_IP} # DNS server clients will use
WG_DEFAULT_ADDRESS: 10.8.0.x # Clients IP address range
# WG_ALLOWED_IPS: "0.0.0.0/0,::/0" # Allowed IPs clients will use
WG_ALLOWED_IPS: "192.168.0.0/16" # Allowed IPs clients will use
Expand Down
2 changes: 1 addition & 1 deletion docker/storage/syncthing/syncthing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ services:
container_name: syncthing
restart: unless-stopped
network_mode: host
hostname: ${MAIN_NODE}
hostname: ${PRIMARY_NODE}
environment:
PUID: ${PUID}
PGID: ${PGID}
Expand Down
11 changes: 10 additions & 1 deletion renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@
"# renovate: datasource=(?<datasource>[a-z-]+?) depName=(?<depName>[^\\s]+?)(?:\\s+extractVersion=(?<extractVersion>[^\\s]+))?\\s+[a-z_]+:\\s*\"(?<currentValue>[^\"]+)\""
],
"extractVersionTemplate": "{{#if extractVersion}}{{{extractVersion}}}{{else}}^v?(?<version>.+)${{/if}}"
},
{
"description": "Update tool versions set via inline renovate comments in Traefik static config",
"customType": "regex",
"managerFilePatterns": ["/^docker/security/traefik/config/traefik\\.yml$/"],
"matchStrings": [
"# renovate: datasource=(?<datasource>[a-z-]+?) depName=(?<depName>[^\\s]+?)(?:\\s+extractVersion=(?<extractVersion>[^\\s]+))?\\s+[a-z_]+:\\s*\"(?<currentValue>[^\"]+)\""
],
"extractVersionTemplate": "{{#if extractVersion}}{{{extractVersion}}}{{else}}^v?(?<version>.+)${{/if}}"
}
],
"packageRules": [
Expand Down Expand Up @@ -171,7 +180,7 @@
"matchPackageNames": [
"lscr.io/linuxserver/qbittorrent"
],
"allowedVersions": "<5"
"allowedVersions": "<6"
},
{
"description": "Disable invalid version tags",
Expand Down
Loading
Loading