Compare commits

...

144 Commits

Author SHA1 Message Date
gilgamezh 2f24f75752 fix(nzbget): wait for gluetun tunnel before starting (prevents queue cancellation)
On pod start the nzbget container raced gluetun: /etc/resolv.conf points at
10.128.0.1 (reachable only via the WireGuard tunnel), so for the ~20s gluetun
needs to establish the tunnel every DNS lookup from nzbget returned EAI_AGAIN.
Any in-queue download that had articles fetched during that window dropped
below the HealthCheck threshold (~97.9%) and was auto-cancelled — even items
that would otherwise complete (saw 97.6-97.8% health = "very nearly fine").

Override the nzbget container's entrypoint to poll DNS resolution and only
exec /init once it succeeds. That's the direct test of "tunnel is up + DNS
works", which is what nzbget needs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 11:41:51 +02:00
gilgamezh 49cfd05bee feat(nzbget): route NNTP through gluetun VPN sidecar; drop dead HTTP proxy
nzbget's usenet downloads (NNTP/563) were egressing DIRECT: the HTTP_PROXY env
pointed at a standalone gluetun that isn't even running, and NNTP ignores HTTP
proxies anyway. Adopt the qbittorrent pattern instead: run gluetun as a sidecar
in the nzbget pod so the shared netns + kill-switch force ALL traffic through
the tunnel, regardless of protocol.

- Add gluetun sidecar (own AirVPN device via gluetun-wireguard-nzbget secret,
  FIREWALL_INPUT_PORTS=6789 to keep the WebUI reachable, DOT=off + DNS_ADDRESS
  per the AirVPN-blocks-DoT gotcha).
- Remove the useless HTTP_PROXY/NO_PROXY envs from nzbget.
- Delete the standalone gluetun chart/values/application (was not running; only
  nzbget referenced it).

Trade-off: if the tunnel drops, downloads stop (no leak) rather than falling
back to direct — same behaviour as qbittorrent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 11:19:25 +02:00
gilgamezh 8da60f2ae8 perf(nzbget): bump newshosting to 50 conns + add Article/WriteBuffer cache
Bump Server1.Connections 30->50 for headroom (Newshosting cap is 100, but
TLS-on-arm overhead makes maxing it counterproductive). Add global
ArticleCache=700 (MB RAM buffer, within the 2Gi limit) and WriteBuffer=1024
(KB/conn) to reduce write thrash on the contended SSD, which is the more
likely throughput ceiling than connection count at our ~100-300 Mbit line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 11:03:57 +02:00
gilgamezh 5e1a919721 fix(nzbget): render newshosting creds via init container
nzbget does not expand OS env vars in nzbget.conf (its ${...} only
references other nzbget options), so the previous secretKeyRef-as-env
approach left the literal ${NEWSHOSTING_USER} in the config and auth
failed with 400 DENIED.

Add initContainers support to the chart and an init step that seds the
Server1 (newshosting) block into nzbget.conf on every start: non-secret
settings in git, username/password from the usenet-creds Secret. Rotating
the secret + restarting re-renders the creds; no password lands in git.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 09:22:01 +02:00
gilgamezh 6247b140bc feat(nzbget): inject newshosting creds from usenet-creds secret
Reuse the existing nzbget chart as the usenet downloader. Newshosting
username/password come from the out-of-band `usenet-creds` Opaque secret
(same pattern as gluetun-wireguard), exposed as env and referenced in
nzbget.conf via ${NEWSHOSTING_USER}/${NEWSHOSTING_PASS} so no plaintext
provider password lands in the config file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 09:16:34 +02:00
gilgamezh 98c1e7b63d feat(disk-usage-alert): Telegram alert when turing3 SSD >=90%
The SSD on turing3 (/mnt/ssd) is shared by Postgres, Plex's SQLite DB
and the media library. When it fills, Postgres crashes (cannot write
postmaster.pid) and Plex's library DB corrupts. Add a CronJob that
checks df via the plex-data mount every 15m and warns Telegram at >=90%
used (3h cooldown). Bot token/chatId live in the out-of-band
`telegram-disk-alert` Secret, not in git.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 22:50:01 +02:00
gilgamezh 79a28a674a feat(maintainerr): deploy for watched-movie cleanup (Plex -> Radarr)
Rule-based deletion of watched movies from Radarr (with files), driven by
Maintainerr. Raw manifests + directory-type Argo Application (no Helm).
Config on shared plex-data NFS PVC (subPath configs/maintainerr); Recreate
strategy since it uses SQLite on RWX NFS. ClusterIP only, no ingress —
access via kubectl/k9s port-forward.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 21:34:44 +02:00
gilgamezh d6ee993a60 docs: add CloudNativePG migration TODO for postgresql
Plan to move both Bitnami postgres instances (pgsql PG16 in default,
gitea-postgresql PG17 bundled in gitea) to CloudNativePG, since Bitnami
images are frozen (bitnamilegacy). Not executed -- planning doc only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 11:15:54 +02:00
gilgamezh 2c68a21d0b ops(metallb): upgrade 0.13.12 -> 0.16.1, pin native L2 (no FRR)
0.16.1 chart defaults frr.enabled=false but frrk8s.enabled=true, which
deploys a heavy frr-k8s daemonset. With no BGP peers (pure L2/ARP), FRR is
unnecessary and its images caused DiskPressure on the Pi nodes, evicting a
speaker and stalling the rollout. Disable both frr and frrk8s for a single
-container L2 speaker.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 11:13:59 +02:00
gilgamezh 1b3f34a432 build: upgrade qbittorrent 5.1.4-r3-ls453 -> 5.2.1_v2.0.12-ls459
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 10:59:16 +02:00
gilgamezh 261aebfd10 ops(gitea): Recreate strategy to avoid RWO upgrade deadlock
Bumped gitea helm chart 12.4.0->12.6.0 (app 1.24.6->1.26.1). The chart
default RollingUpdate (maxSurge 100%/maxUnavailable 0) surges a second pod
that can't mount the single RWO NFS PVC, deadlocking 'helm upgrade --wait'.
Recreate avoids it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 10:59:03 +02:00
gilgamezh 9b24978342 docs: add CLAUDE.md (GitOps flow + AirVPN/gluetun DNS gotcha)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 10:20:16 +02:00
gilgamezh 1a91b72464 fix(qbittorrent): use AirVPN plaintext DNS, disable gluetun DoT
AirVPN blocks outbound DNS-over-TLS (tcp/853), so gluetun's default DoT
resolver at 127.0.0.1 never gets answers. The startup healthcheck's
"lookup cloudflare.com" then times out and the VPN restarts every ~6s
in a permanent loop, leaving qbittorrent with no working DNS.

Verified inside the pod netns: tunnel egress works (ping 8.8.8.8 18ms),
AirVPN's pushed resolver 10.128.0.1 resolves fine, but tcp/853 to both
1.1.1.1 and 8.8.8.8 times out.

Set DOT=off and DNS_ADDRESS=10.128.0.1 so gluetun points resolv.conf at
AirVPN's pushed DNS, reached over the tunnel (no DNS leak, no port 853).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 10:17:11 +02:00
argocd-image-updater ac637adaf4 build: automatic update of plex
updates image linuxserver/plex tag '1.43.1.10611-1e34174b1-ls306' to '1.43.2.10687-563d026ea-ls307'
2026-05-19 19:33:41 +00:00
argocd-image-updater 6082e6fc14 build: automatic update of plex
updates image linuxserver/plex tag '1.43.1.10611-1e34174b1-ls305' to '1.43.1.10611-1e34174b1-ls306'
2026-05-18 12:53:50 +00:00
gilgamezh 7e0a38d65f build: pin gluetun to v3.41.1
Fixes VPN restart loop after :latest pulled a build with the Alpine 3.22
iptables parsing regression and the healthcheck race (#3123). v3.41.1
includes the k8s cluster-DNS auto-detection so DNS lookups in the
startup healthcheck no longer time out behind the killswitch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 09:24:50 +02:00
gilgamezh 3b480d6abf build: backup traefik HelmChartConfig from k3s master manifests
Snapshot of /var/lib/rancher/k3s/server/manifests/traefik-config.yaml
on turing1 after dropping the v2 image pin during the Traefik v3
migration. Lives only on the node otherwise — track it here so it can
be restored on a node rebuild.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 09:24:50 +02:00
gilgamezh 3ace05a695 build: migrate ingresses for Traefik v3 (k3s upgrade)
k3s update bumped Traefik chart 37 → 39, dropping v2 support. Replace
the v2-only `whitelist.sourcerange` annotation on the gitea ingress
with an `ipAllowList` Middleware (resources/gitea-middleware.yaml),
referenced via `router.middlewares`. Switch the default-ns ingresses
(kube-plex, radarr, sonarr, lidarr) from the deprecated
`kubernetes.io/ingress.class` annotation to `spec.ingressClassName`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 09:24:50 +02:00
argocd-image-updater 290ce6a103 build: automatic update of plex
updates image linuxserver/plex tag '1.43.1.10611-1e34174b1-ls304' to '1.43.1.10611-1e34174b1-ls305'
2026-05-11 12:31:16 +00:00
gilgamezh e230129119 build: use Recreate strategy for qbittorrent
qbittorrent holds an exclusive lockfile on /config; rolling updates
deadlock because the new pod can't acquire the lock until the old one
is gone.
2026-05-07 10:03:16 +02:00
gilgamezh 269ab53002 build: pin qbittorrent to 5.1.4-r3-ls453 2026-05-07 09:51:40 +02:00
gilgamezh 724568e08f build: pin qbittorrent to 5.1.4 (linuxserver has no libtorrent2 5.2.0) 2026-05-07 09:49:11 +02:00
gilgamezh 770125e7c8 build: update qbittorrent to 5.2.0 and unstick prowlarr
prowlarr was pinned to a stale digest (v2.0.5.5160) via
.argocd-source-prowlarr.yaml; remove the file so the live app's
helm.parameters (which already has the current :latest digest =
v2.3.5.5327) takes effect.

qbittorrent: bump 5.1.0 -> 5.2.0.
2026-05-07 09:46:18 +02:00
argocd-image-updater 3239a1e729 build: automatic update of plex
updates image linuxserver/plex tag '1.43.1.10611-1e34174b1-ls303' to '1.43.1.10611-1e34174b1-ls304'
2026-05-04 11:24:15 +00:00
argocd-image-updater d476af6cfd build: automatic update of plex
updates image linuxserver/plex tag '1.43.1.10611-1e34174b1-ls302' to '1.43.1.10611-1e34174b1-ls303'
2026-04-27 11:20:51 +00:00
argocd-image-updater 930ede9b74 build: automatic update of plex
updates image linuxserver/plex tag '1.43.1.10611-1e34174b1-ls301' to '1.43.1.10611-1e34174b1-ls302'
2026-04-20 11:05:41 +00:00
argocd-image-updater a31e50f02f build: automatic update of plex
updates image linuxserver/plex tag '1.43.1.10576-06378bdcd-ls300' to '1.43.1.10611-1e34174b1-ls301'
2026-04-10 14:05:19 +00:00
argocd-image-updater aa89a5f238 build: automatic update of plex
updates image linuxserver/plex tag '1.43.0.10492-121068a07-ls299' to '1.43.1.10576-06378bdcd-ls300'
2026-04-08 15:58:38 +00:00
argocd-image-updater 41e272c9f2 build: automatic update of plex
updates image linuxserver/plex tag '1.43.0.10492-121068a07-ls298' to '1.43.0.10492-121068a07-ls299'
2026-04-06 10:26:04 +00:00
argocd-image-updater 4fe6ef579c build: automatic update of plex
updates image linuxserver/plex tag '1.43.0.10492-121068a07-ls297' to '1.43.0.10492-121068a07-ls298'
2026-03-30 10:35:06 +00:00
argocd-image-updater 8ea4086a37 build: automatic update of plex
updates image linuxserver/plex tag '1.43.0.10492-121068a07-ls296' to '1.43.0.10492-121068a07-ls297'
2026-03-21 14:45:07 +00:00
argocd-image-updater 3827ad656a build: automatic update of plex
updates image linuxserver/plex tag '1.43.0.10492-121068a07-ls295' to '1.43.0.10492-121068a07-ls296'
2026-03-15 21:59:52 +00:00
argocd-image-updater 9ab1659939 build: automatic update of plex
updates image linuxserver/plex tag '1.43.0.10492-121068a07-ls294' to '1.43.0.10492-121068a07-ls295'
2026-03-02 10:19:10 +00:00
argocd-image-updater 4a66fdfabf build: automatic update of plex
updates image linuxserver/plex tag '1.43.0.10492-121068a07-ls293' to '1.43.0.10492-121068a07-ls294'
2026-02-23 10:12:30 +00:00
argocd-image-updater c98f1b93b5 build: automatic update of plex
updates image linuxserver/plex tag '1.42.2.10156-f737b826c-ls292' to '1.43.0.10492-121068a07-ls293'
2026-02-11 16:35:08 +00:00
argocd-image-updater 377fa8ec4a build: automatic update of plex
updates image linuxserver/plex tag '1.42.2.10156-f737b826c-ls291' to '1.42.2.10156-f737b826c-ls292'
2026-02-09 10:20:10 +00:00
argocd-image-updater d009a61c0e build: automatic update of plex
updates image linuxserver/plex tag '1.43.0.10467-2b1ba6e69-ls290' to '1.42.2.10156-f737b826c-ls291'
2026-01-28 21:21:09 +00:00
argocd-image-updater eeb80c2662 build: automatic update of plex
updates image linuxserver/plex tag '1.42.2.10156-f737b826c-ls289' to '1.43.0.10467-2b1ba6e69-ls290'
2026-01-27 19:54:58 +00:00
argocd-image-updater 24a2463f20 build: automatic update of plex
updates image linuxserver/plex tag '1.42.2.10156-f737b826c-ls288' to '1.42.2.10156-f737b826c-ls289'
2026-01-19 09:53:36 +00:00
gilgamezh 5e80bac19d fix torrent path 2026-01-18 14:35:48 +01:00
gilgamezh d39c8ff550 update torrent port 2026-01-18 14:26:47 +01:00
gilgamezh d79e75fa88 update mount path 2026-01-18 14:22:55 +01:00
gilgamezh c773b6da26 update mount path 2026-01-18 14:13:34 +01:00
gilgamezh ba2c36b6f2 media: set cross-seed config dir env 2026-01-18 13:49:28 +01:00
gilgamezh 988a44b609 media: run cross-seed via explicit command 2026-01-18 13:45:00 +01:00
gilgamezh 1b904fe20d media: bind cross-seed daemon to pod IP 2026-01-18 13:38:14 +01:00
gilgamezh 9e4e0d7a9a media: drop unsupported cross-seed config flag 2026-01-18 13:35:29 +01:00
gilgamezh 78ba3041f1 media: run cross-seed with config args 2026-01-18 13:31:41 +01:00
gilgamezh 5988d0df38 media: run cross-seed daemon explicitly 2026-01-18 13:25:27 +01:00
gilgamezh 9a6d7670f4 media: fix cross-seed config for qbittorrent 2026-01-18 13:21:41 +01:00
gilgamezh 1becccb339 argocd: register qbittorrent and cross-seed applications 2026-01-17 13:29:20 +01:00
gilgamezh adff03ea7c media: add cross-seed with nzbgeek config 2026-01-17 13:29:13 +01:00
gilgamezh 7979c6c917 media: add qbittorrent with gluetun sidecar 2026-01-17 13:29:06 +01:00
gilgamezh 7bb64786ac align paths 2026-01-17 12:56:26 +01:00
gilgamezh caa7495a71 chore: add gitignore for AirVPN configs 2026-01-17 12:38:56 +01:00
gilgamezh c87694e5ce media: add lowercase proxy env for nzbget 2026-01-17 12:36:30 +01:00
gilgamezh 10eebfc9d0 media: select airvpn by country for wireguard 2026-01-17 12:30:28 +01:00
gilgamezh 7abedc9bce media: add wireguard preshared key support 2026-01-17 12:21:35 +01:00
gilgamezh 2ee850da6e media: wireguard values from airvpn config 2026-01-17 12:14:59 +01:00
gilgamezh 0a3f5dfc80 docs: add airvpn wireguard key + verification notes 2026-01-17 11:16:55 +01:00
gilgamezh 34f1e08f2c argocd: register gluetun and nzbget applications 2026-01-17 11:16:48 +01:00
gilgamezh bf8252970a media: add nzbget with pvc and proxy routing 2026-01-17 11:16:43 +01:00
gilgamezh 1bf2ea313f media: add gluetun (AirVPN wireguard) with proxy service 2026-01-17 11:16:38 +01:00
gilgamezh 8dc529451c use master instead of HEAD 2026-01-11 18:37:16 +01:00
argocd-image-updater e26b8401ef build: automatic update of plex
updates image linuxserver/plex tag 'sha256:28f18c27b6822328df994154dbf7c0f511032d9f91bbd10881030b706afd8593' to '1.42.2.10156-f737b826c-ls288'
2026-01-11 17:04:12 +00:00
gilgamezh e474d0d882 fix values path 2026-01-11 17:55:58 +01:00
argocd-image-updater 585aff8319 build: automatic update of plex
updates image linuxserver/plex tag 'sha256:28f18c27b6822328df994154dbf7c0f511032d9f91bbd10881030b706afd8593' to '1.42.2.10156-f737b826c-ls288'
2026-01-11 16:55:17 +00:00
gilgamezh 3f5eeb737e fix values path 2026-01-11 17:51:35 +01:00
argocd-image-updater ee2ccd5c5e build: automatic update of plex
updates image linuxserver/plex tag 'sha256:28f18c27b6822328df994154dbf7c0f511032d9f91bbd10881030b706afd8593' to '1.42.2.10156-f737b826c-ls288'
2026-01-11 16:43:17 +00:00
gilgamezh 272b065ba7 update plex config 2026-01-11 16:50:09 +01:00
gilgamezh 825a73a5b9 update resources for lidar 2026-01-11 15:44:38 +01:00
gilgamezh 0becd2930d Update README structure 2026-01-11 13:15:00 +01:00
gilgamezh f3b7d23bb7 upgrade cert-manager and configure it to use dns instead of http 2026-01-11 12:22:18 +01:00
gilgamezh be0b6b6dd4 Move Lidarr Postgres config to config.xml 2026-01-10 18:53:54 +01:00
gilgamezh e89454c1ec Fix Lidarr Postgres DB env names 2026-01-10 18:49:12 +01:00
gilgamezh 956ddd6b73 Fix Lidarr Postgres env var names 2026-01-10 18:39:59 +01:00
gilgamezh deb2901c6f Add Lidarr ArgoCD app and custom chart 2026-01-10 18:31:58 +01:00
gilgamezh 35057c2851 image updater config 2026-01-10 18:31:26 +01:00
gilgamezh 3a467d9d5a set latest for everything 2026-01-10 17:53:36 +01:00
gilgamezh 0fcada7d14 Remove argocd-image-updater annotations 2026-01-10 17:29:53 +01:00
gilgamezh 1fdd7c1b25 Fix prowlarr Argo CD app path 2026-01-10 17:25:16 +01:00
gilgamezh 261a64de76 update argo 2026-01-10 17:23:12 +01:00
gilgamezh 32864aae67 update helm chat path 2026-01-10 17:22:46 +01:00
gilgamezh f9aba632ec more cleaning 2026-01-10 16:48:04 +01:00
gilgamezh 743c086b36 reorder and clean repo 2026-01-10 16:47:21 +01:00
gilgamezh 6460253941 remove unused files 2026-01-10 16:36:32 +01:00
gilgamezh caae3465cd upgrade actual 2026-01-10 13:20:11 +01:00
gilgamezh 19a19d8fd8 fix(lidarr): avoid turing2 and use postgres 2025-12-06 19:24:18 +01:00
gilgamezh 4979fedcc0 lidar user/pass 2025-12-06 18:50:06 +01:00
gilgamezh bd73d73cd2 add lidar 2025-12-06 18:39:51 +01:00
gilgamezh bf72a032df add lidar 2025-12-06 18:37:20 +01:00
gilgamezh d7bf0ac5ef fix(prowlarr): correct values file path 2025-11-30 14:15:03 +01:00
gilgamezh adcc180ad9 chore(argocd): enforce api write-back and pin image tags 2025-11-30 14:14:37 +01:00
argocd-image-updater 5d047ba639 build: automatic update of prowlarr
updates image linuxserver/prowlarr tag 'sha256:4f2a6d597845b2f3e19284b1d982b3e0b4bd7c22472c2979c956aa198b83f472' to 'sha256:964485823771c102427a0c1cd896cf6b576add6f21bd041498b92cb040ee7270'
2025-10-11 09:31:08 +00:00
gilgamezh ee6431e01b fix(argocd): clean values write-back paths 2025-10-11 11:30:08 +02:00
gilgamezh cc85762e1e use dark theme in gitea because auto is not working 2025-10-11 11:29:38 +02:00
argocd-image-updater 84ce934f17 build: automatic update of prowlarr
updates image linuxserver/prowlarr tag 'sha256:4f2a6d597845b2f3e19284b1d982b3e0b4bd7c22472c2979c956aa198b83f472' to 'sha256:964485823771c102427a0c1cd896cf6b576add6f21bd041498b92cb040ee7270'
2025-10-08 03:58:59 +00:00
argocd-image-updater 8c66325e12 build: automatic update of prowlarr
updates image linuxserver/prowlarr tag 'sha256:4f2a6d597845b2f3e19284b1d982b3e0b4bd7c22472c2979c956aa198b83f472' to 'sha256:fa81e471a7e46a24b121838563a10d468cf82eecd1587a464c6df4927ecc3248'
2025-10-01 04:07:50 +00:00
argocd-image-updater 84369eb4a4 build: automatic update of prowlarr
updates image linuxserver/prowlarr tag 'sha256:4f2a6d597845b2f3e19284b1d982b3e0b4bd7c22472c2979c956aa198b83f472' to 'sha256:856c93bab72a6a41a23ff60bab48554135c044a456f40909307011dea8ddeafb'
2025-09-17 03:59:13 +00:00
gilgamezh d9d216c772 delete garbage 2025-09-11 22:20:07 +02:00
gilgamezh 8015e3ff06 update claim 2025-09-11 22:11:46 +02:00
gilgamezh c1cb6f8881 update claim token 2025-09-11 22:06:07 +02:00
argocd-image-updater e7041bbddc build: automatic update of prowlarr
updates image linuxserver/prowlarr tag 'dummy' to 'sha256:4f2a6d597845b2f3e19284b1d982b3e0b4bd7c22472c2979c956aa198b83f472'
2025-08-30 17:41:01 +00:00
gilgamezh 8c502c71a7 fix(image-updater): multi-source git write-back via ref
Add write-back-target=values and values-file=/helm-values/* for all apps. Map helm image fields per alias.\n\nCommitted via Codex CLI.
2025-08-30 19:39:38 +02:00
argocd-image-updater b14adc3781 build: automatic update of prowlarr
updates image linuxserver/prowlarr tag 'dummy' to 'sha256:4f2a6d597845b2f3e19284b1d982b3e0b4bd7c22472c2979c956aa198b83f472'
2025-08-30 17:38:34 +00:00
gilgamezh 11e3fb8043 chore(image-updater): git write-back to values files for all apps
Set values-file annotations with alias helm.image-name=image.repository and helm.image-tag=image.tag.\nEnsures updates commit into /helm-values/*.\n\nCommitted via Codex CLI.
2025-08-30 19:33:22 +02:00
gilgamezh 7237535d6e fix(image-updater): use argocd write-back for multi-source apps
Git write-back fails for Helm repo sources (multi-source): updater attempts to target external chart repos. Switch to argocd for plex, radarr, sonarr, flaresolverr, transmission, ollama. Keep git for prowlarr (chart path in this repo).\n\nCommitted via Codex CLI.
2025-08-30 19:33:22 +02:00
argocd-image-updater 883b856060 build: automatic update of prowlarr
updates image linuxserver/prowlarr tag 'dummy' to 'sha256:4f2a6d597845b2f3e19284b1d982b3e0b4bd7c22472c2979c956aa198b83f472'
2025-08-30 16:37:17 +00:00
gilgamezh e18ea61992 chore(image-updater): use Git write-back for all apps
Set write-back-method=git with write-back-target to this repo, branch=master, and commit identity. Keeps image-list and strategies.\n\nCommitted via Codex CLI.
2025-08-30 18:24:13 +02:00
gilgamezh 4e9393521c feat(argocd): add prowlarr app (custom chart via Git path)
Uses multi-source Application: chart path "prowlarr/" + values from this repo at "helm-values/prowlarr_values.yml".\nPreserves existing ports, PVCs, and mounts.\n\nCommitted via Codex CLI.
2025-08-30 17:59:55 +02:00
gilgamezh a534461867 fix(transmission): use correct chart bananaspliff/transmission-openvpn
Helm reference that worked: "helm install transmission bananaspliff/transmission-openvpn --values transmission_values.yml".\nArgoCD now points to the same chart.\n\nCommitted via Codex CLI.
2025-08-30 17:37:42 +02:00
gilgamezh 9810330571 fix(transmission): point ArgoCD app to bananaspliff/transmission chart
ArgoCD reported: chart 'transmission-openvpn' not found in https://k8s-at-home.com/charts/.\nSwitch to bananaspliff/geek-charts 'transmission' which matches our values pattern (env/volumes/volumeMounts).\n\nCommitted via Codex CLI.
2025-08-30 17:33:36 +02:00
gilgamezh 75622374ff docs(agents): add Git + ArgoCD CLI checks to migration workflow
Adds "Agent Workflow Defaults" and "Commit & Push + ArgoCD CLI Checks" to AGENTS.md (via CLAUDE.md).\nStandardizes post-app-addition flow: commit/push, argocd login/sync/wait, and basic diagnostics.\n\nCommitted via Codex CLI.
2025-08-30 17:30:35 +02:00
gilgamezh 02869c07d7 move ollama and transmission to argo 2025-08-30 17:25:12 +02:00
gilgamezh 1d3dfec593 generic 2025-08-30 17:05:41 +02:00
gilgamezh a4d3d41ece file for codex 2025-08-30 17:05:09 +02:00
gilgamezh 085eac3dce remove dups for files that are in helm-values 2025-08-30 17:03:02 +02:00
gilgamezh 1fd2399e73 move values to common folder 2025-08-30 16:50:54 +02:00
gilgamezh 9821c67e72 add flaresolverr 2025-08-30 13:12:19 +02:00
gilgamezh bb5c4d754c fix: configure Gitea with proper domain and HTTPS
- Update domain from gitea.turing.lan to gitea.gilgamezh.me
- Configure Traefik ingress with TLS termination
- Set ROOT_URL to https while keeping internal protocol as http
- Enable Let's Encrypt certificate generation

Gitea is now accessible at https://gitea.gilgamezh.me with proper styling.
CSS 404 errors in dev tools are expected - assets are embedded in binary.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-16 11:35:50 +02:00
gilgamezh 503793a2ee docs: add ArgoCD image-updater multi-source troubleshooting guide
Add comprehensive documentation for resolving ArgoCD image-updater credential
errors with multi-source applications. Documents the solution of using ArgoCD
API write-back method instead of git write-back for applications that use
external Helm charts with local values repositories.

Key learnings:
- Multi-source apps need argocd write-back method not git
- External chart repos don't need write credentials with API method
- Includes step-by-step implementation and verification commands

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-16 11:00:20 +02:00
gilgamezh b611c1ffad feat: complete ArgoCD migration for Radarr and Sonarr with lessons learned
- Fix ArgoCD multi-source applications (remove conflicting source: section)
- Restore original volume mount configuration using plex-data PVC
- Update ingress class from nginx to traefik for K3s compatibility
- Remove unnecessary compatibility patches (myvolume PVC)
- Document comprehensive ArgoCD migration guidelines in CLAUDE.md
- Add chart-specific configuration patterns for Bananaspliff charts
- Include pre/post migration checklists and verification commands

Both applications now working exactly as before migration with:
- Original service ports (Radarr:7878, Sonarr:8989)
- Complete access to existing configurations and databases
- External accessibility via radarr.gilgamezh.me and sonarr.gilgamezh.me
- Automatic image updates enabled for latest tag tracking

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-15 18:15:23 +02:00
gilgamezh 9ab532c2b4 fix: restore original volume mount configuration for Radarr and Sonarr
- Use original volumes/volumeMounts structure instead of persistence
- Mount plex-data PVC to /config with correct subPath
- Restore exact working configuration from before ArgoCD migration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-15 18:09:43 +02:00
gilgamezh bf63967cfd fix: revert to correct Bananaspliff chart service port structure
- Use service.port directly as per chart schema
- Radarr: service.port: 7878
- Sonarr: service.port: 8989

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-15 17:59:12 +02:00
gilgamezh 93499d7d10 fix: correct service port configuration for Radarr and Sonarr
- Fix service port configuration to respect original working setup
- Radarr service port: 7878 (was incorrectly defaulting to 80)
- Sonarr service port: 8989 (was incorrectly defaulting to 80)
- Use proper Bananaspliff chart service structure with main.ports.http

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-15 17:58:24 +02:00
gilgamezh 7e1847a5ac feat: add myvolume PVC for Bananaspliff charts compatibility
- Create myvolume PVC required by Bananaspliff Radarr/Sonarr charts
- Uses nfs-client storage class with 450Gi capacity
- Enables Radarr and Sonarr pods to schedule and run successfully
- Maintains compatibility with existing Bananaspliff chart structure
2025-08-15 17:50:56 +02:00
gilgamezh 64b7624e74 fix: correct persistence configuration for Bananaspliff charts
- Fix Radarr and Sonarr values to use Bananaspliff chart structure
- Use persistence.config and persistence.media instead of volumes/volumeMounts
- Reference existing plex-data PVC with proper subPaths
- Fixes FailedScheduling due to missing 'myvolume' PVC
2025-08-15 17:47:08 +02:00
gilgamezh 06e284a1e0 feat: add Radarr and Sonarr GitOps applications
- Add Radarr and Sonarr ArgoCD Applications with auto-image updates
- Update image tags from pinned versions to latest for auto-updates
- Configure Bananaspliff charts with existing configurations
- Enable GitOps workflow with digest strategy for latest tag tracking
- Radarr: ghcr.io/linuxserver/radarr:5.26.2 -> latest
- Sonarr: ghcr.io/linuxserver/sonarr:4.0.15 -> latest

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-15 17:44:32 +02:00
gilgamezh 53d9fb3a6e feat: configure Plex for automatic latest image updates
- Change Plex image tag from 1.41.8 to latest for auto-updates
- Switch ArgoCD Image Updater strategy to 'digest' for latest tag tracking
- Enables automatic detection of newer Plex versions
- Tests complete GitOps workflow with image auto-updates

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-15 17:35:39 +02:00
gilgamezh cfb30912b0 fix: use multi-source ArgoCD Application for Plex
- Separate chart source (kube-plex) and values source (turingpi)
- Deploy to default namespace where existing PVCs are located
- Use complete plex_values.yml for proper GPU and persistence config
- Fixes storage class and duplicate port issues
2025-08-15 17:28:00 +02:00
gilgamezh 52366f3812 fix: use separate kube-plex repository for GPU-enabled chart
- Point ArgoCD to dedicated kube-plex repository in Gitea
- Ensures GPU support changes are available to ArgoCD
- Separates chart from main configuration repository
2025-08-15 17:26:32 +02:00
gilgamezh 2e513d155d fix: use local kube-plex repository with GPU support
- Switch from upstream kube-plex to our local version in Gitea
- Ensures GPU support changes are preserved in GitOps workflow
- Fixes ArgoCD Application source path
2025-08-15 17:21:01 +02:00
gilgamezh 97329837d4 Add Plex GitOps application for automatic image updates
- Create organized directory structure (applications/, helm-values/)
- Add ArgoCD Application manifest for Plex with image auto-updater
- Configure automatic upgrade detection from current 1.41.8 to latest
- Enable Git write-back for GitOps workflow
- Use existing kube-plex chart with LinuxServer Plex image

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-15 17:17:41 +02:00
gilgamezh 1f5d5988cb update gitea 2025-08-15 17:07:19 +02:00
gilgamezh 45dfbfcfbb Add ArgoCD and Gitea for GitOps workflow implementation
- Deploy ArgoCD with Helm for GitOps continuous delivery
  * Configure LoadBalancer and Ingress access on LAN
  * Enable ArgoCD Image Updater for automatic "latest" tag updates
  * Simplified RBAC for single-user homelab environment

- Deploy Gitea as self-hosted Git server for local repositories
  * PostgreSQL backend with NFS persistent storage
  * SSH and HTTP access via MetalLB LoadBalancer
  * Integration guides for ArgoCD GitOps workflows

- Add example ArgoCD Application with auto-image updates
- Include comprehensive migration guides from Helm to GitOps
- Maintain compatibility with existing Helm-based deployments

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-15 16:52:10 +02:00
gilgamezh 99e9371969 Update Plex to use new configurable GPU support
- Replace extraVolumeMounts/extraVolumes with new gpu configuration
- Enable GPU support with cleaner, more maintainable config
- Use same /dev/dri paths for hardware-accelerated transcoding
- Leverages new kube-plex GPU feature for better integration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-28 14:38:21 +02:00
gilgamezh ff3e6f723c Add comprehensive documentation and automated update script
- Add README.md: Complete repository overview, architecture, and usage guide
- Add update.sh: Automated K3s cluster upgrade script for all nodes
- Add CLAUDE.md: Claude Code integration documentation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-28 14:18:07 +02:00
gilgamezh 620d757f8b Add new applications: Home Assistant Voice LLMs and Ollama
- Add custom Helm chart for Home Assistant Voice LLMs integration
- Add Ollama configuration for local LLM inference
- Support AI voice assistant capabilities in homelab

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-28 14:13:23 +02:00
gilgamezh 0f61ffae27 Update node selectors and Plex version
- Update Plex: 1.41.3 → 1.41.8
- Fix deprecated node selector: beta.kubernetes.io/arch → kubernetes.io/arch
- Add ARM64 node selector for Transmission

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-28 14:12:57 +02:00
gilgamezh 3970af8ce2 Update application images to latest versions
- Actual Budget: 25.1.0 → 25.3.1
- Prowlarr: 1.23.1 → 1.37.0.5076-ls121
- Radarr: 5.10.4 → 5.26.2
- Sonarr: 4.0.9 → 4.0.15

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-28 14:12:28 +02:00
gilgamezh 0e08d89e01 move plex to AMD with GPU 2025-02-16 15:31:50 +01:00
gilgamezh 62f0e703de update actual 2025-01-19 12:25:02 +01:00
gilgamezh 447231f6b0 small fixes and always pull actual-budget image 2025-01-05 09:53:54 +01:00
gilgamezh 7e0a5ee800 deploy jellyfin 2025-01-02 14:04:04 +01:00
120 changed files with 3626 additions and 193 deletions
+22
View File
@@ -0,0 +1,22 @@
/AirVPN*
# OS files
.DS_Store
Thumbs.db
# Editor/IDE
*.swp
*.swo
*~
.idea/
.vscode/
# Logs and temp
*.log
*.bak
*.tmp
.tmp/
# Env files
.env
.env.*
+39
View File
@@ -0,0 +1,39 @@
# turingpi
Home k3s cluster on a Turing Pi (arm64 nodes), GitOps-managed by ArgoCD.
## Deploying changes
- Apps are ArgoCD `Application`s in `applications/*.yaml`, pointing at an **internal Gitea**
repo (`gitea-http.gitea.svc.cluster.local`), `targetRevision: HEAD`.
- The git remote is `gitea` (`git@192.168.222.26:admin/turingpi.git`); working branch is **`master`**.
- To deploy: **commit and push to `gitea/master`**. Apps have `syncPolicy.automated` with
`selfHeal: true`, so direct `kubectl patch`/`edit` is reverted — changes must go through git.
- Argo polls Gitea every ~3 min. Force a sync with:
`kubectl -n argocd annotate application <name> argocd.argoproj.io/refresh=hard --overwrite`
- Helm values: `helm-values/<app>_values.yaml`. Custom charts: `custom_helm_charts/<app>/`.
## Gotchas
### qbittorrent + gluetun (AirVPN) — DNS / restart loop
AirVPN blocks outbound **DNS-over-TLS (tcp/853)** to force its own resolver. Gluetun's default
`DOT=on` resolver (127.0.0.1) therefore never gets answers, **all DNS fails**, and the VPN
startup healthcheck (`lookup cloudflare.com`) times out — gluetun restarts the VPN every ~6s in a
permanent loop. The pod still shows `2/2 Running` with 0 restarts, so it looks healthy while
having no usable network.
The gluetun sidecar in `helm-values/qbittorrent_values.yaml` **must** keep:
```yaml
- name: DOT
value: "off"
- name: DNS_ADDRESS
value: "10.128.0.1" # AirVPN's pushed resolver, reached over the tunnel — no DNS leak
```
Diagnose: gluetun logs repeat `restarting VPN ... lookup ... i/o timeout`. Confirm with
`ping 8.8.8.8` (works) and `nslookup x 10.128.0.1` (works) but `curl 1.1.1.1:853` (times out).
Note: the `7e0a38d` "pin gluetun to v3.41.1" commit message falsely claimed v3.41.1 fixed this
DNS timeout. It did not — don't trust that claim.
+152
View File
@@ -0,0 +1,152 @@
# TuringPi K3s Homelab
This repository contains Kubernetes configuration files for a K3s cluster running on TuringPi hardware. It includes Helm charts, values files, and manifests for deploying various self-hosted applications in a homelab environment.
## 🏗️ Cluster Architecture
### Hardware Setup
- **turing1**: Control plane + worker (192.168.222.237)
- **turing2**: Worker node
- **turing3**: Worker node (NFS server at turing3.lan)
- **turing4**: Worker node
- **beelink**: Additional x86_64 worker node
### Infrastructure Stack
- **Kubernetes**: K3s lightweight distribution
- **Storage**: NFS-backed persistent volumes from turing3.lan:/mnt/ssd
- **Load Balancer**: MetalLB for bare metal LoadBalancer services
- **SSL**: cert-manager with Let's Encrypt certificates
- **Ingress**: Nginx with LAN-only access restrictions
## 🚀 Applications
### Media Services
- **Plex**: Via kube-plex (Kubernetes-native with dynamic transcoding)
- **Jellyfin**: Alternative media server
- **Sonarr/Radarr**: TV/Movie management
- **Prowlarr**: Indexer management
- **Transmission**: BitTorrent client with OpenVPN
- **FlareSolverr**: Captcha solver service
### Other Applications
- **Actual Budget**: Personal finance management
- **Home Assistant Voice LLMs**: AI voice integration
- **Ollama**: Local LLM inference
- **Prometheus**: Monitoring and metrics
- **PostgreSQL**: Database backend
## 📁 Repository Structure
```
├── applications/ # Argo CD Application manifests
├── resources/ # Plain Kubernetes resources (kubectl apply -f)
├── helm-values/ # Helm values for Argo-managed apps
├── non_argo_values/ # Helm values for apps managed directly with Helm
├── custom_helm_charts/ # Custom charts (actual, voice, prowlarr, lidarr)
└── cluster_setup/ # Cluster bootstrap + update scripts
```
## 🔧 Common Operations
### Application Deployment
```bash
# Deploy with Helm using values files
helm upgrade <release-name> <chart> -f <app>_values.yaml -i
# Examples:
helm upgrade actual my-actual-server -f actual_values.yaml -i
helm upgrade plex kube-plex/charts/kube-plex --values plex_values.yml
helm upgrade radarr bananaspliff/radarr -f radarr_values.yaml
```
### Infrastructure Management
```bash
# Apply Kubernetes manifests
kubectl apply -f metallb.yml
kubectl apply -f ingress.yaml
# Check cluster status
kubectl get nodes
kubectl get pods --all-namespaces
```
## 🔄 K3s Cluster Updates
### Automated Update
Run the provided script to update all nodes:
```bash
./update.sh
```
Old way:
```bash
master: curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_DOWNLOAD=false sh -s - --write-kubeconfig-mode 644 --disable servicelb --token torino --node-ip 192.168.222.237 --disable-cloud-controller --disable local-storage
nodes: curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_DOWNLOAD=false K3S_URL=https://192.168.222.237:6443 K3S_TOKEN=torino sh -
````
### Manual Update Process
#### 1. Update Master Node (turing1)
```bash
ssh root@turing1 # password: turing
curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_DOWNLOAD=false sh -s - \
--write-kubeconfig-mode 644 \
--disable servicelb \
--token torino \
--node-ip 192.168.222.237 \
--disable-cloud-controller \
--disable local-storage
```
#### 2. Update Worker Nodes (turing2, turing3, turing4)
```bash
ssh root@<node> # password: turing
curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_DOWNLOAD=false \
K3S_URL=https://192.168.222.237:6443 \
K3S_TOKEN=torino sh -
```
#### 3. Update Beelink Node
```bash
ssh gilgamezh@beelink.lan # no password (SSH keys)
sudo curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_DOWNLOAD=false \
K3S_URL=https://192.168.222.237:6443 \
K3S_TOKEN=torino sh -
```
#### 4. Verify Update
```bash
kubectl get nodes # Check all nodes show new version
kubectl get pods --all-namespaces | grep -v Running # Check for issues
```
## 🔑 Access Information
- **Cluster Token**: `torino`
- **Master Node**: `192.168.222.237:6443`
- **SSH Access**:
- TuringPi nodes: `root@<hostname>` (password: `turing`)
- Beelink: `gilgamezh@beelink.lan` (SSH keys)
## 📚 Additional Documentation
- See `CLAUDE.md` for detailed Claude Code integration guide
- Custom Helm charts include their own README files
- Check application-specific `*_values.yaml` files for configuration options
## 🛠️ Development
### Helm Chart Development
```bash
helm create <chart-name>
helm lint <chart-path>
helm template <chart> -f <values> | kubectl apply --dry-run=client -f -
```
### Storage Requirements
- NFS server must be running on turing3.lan
- Applications require ReadWriteMany access for shared media
- Persistent volumes are dynamically provisioned via nfs-subdir-external-provisioner
+174
View File
@@ -0,0 +1,174 @@
# TODO: Migrate PostgreSQL to CloudNativePG
**Status:** planned, not started
**Target:** [CloudNativePG](https://cloudnative-pg.io/) (CNPG operator)
**Created:** 2026-05-31
## Why
Both Postgres instances run **Bitnami** images, which Bitnami froze in
Aug 2025 (free images moved to the `bitnamilegacy` archive and stopped
getting updates / security patches):
| Instance | Namespace | Version | Image | How deployed | Consumers |
|---|---|---|---|---|---|
| `pgsql` | `default` | PG **16.2** | `docker.io/bitnami/postgresql:16.2.0-debian-12-r8` (non-legacy, tag effectively gone from the registry) | standalone helm release `pgsql` (chart `postgresql-15.1.2`) | lidarr, radarr, sonarr, prowlarr, alhfmf |
| `gitea-postgresql` | `gitea` | PG **17.6** | `bitnamilegacy/postgresql:17.6.0-debian-12-r4` | **bundled subchart** inside the `gitea` helm release | gitea only |
CloudNativePG is the chosen replacement: actively maintained, operator-managed
HA/backups, first-class `pg_dump`-based import for migration, and not tied to
Bitnami.
> ⚠️ `pgsql` is on the **non-legacy** `bitnami/` path whose `16.2.0` tag is no
> longer pullable — if that pod is ever rescheduled to a node without the image
> cached, it will **fail to start**. Treat instance A as the higher priority.
## Databases to migrate
- **pgsql (PG16):** `alhfmf`, `lidarr_db`, `lidarr_db_log`, `radarr_db`,
`radarr_db_log`, `sonarr_db`, `sonarr_db_log`, `prowlarr_db`, `prowlarr_db_log`
(confirm full list at cutover with `\l`).
- **gitea (PG17):** `gitea` (owner role `gitea`).
---
## 0. Prerequisites
- [ ] Install the CNPG operator (pin a recent stable version):
```bash
helm repo add cnpg https://cloudnative-pg.github.io/charts
helm repo update cnpg
helm upgrade --install cnpg cnpg/cloudnative-pg \
-n cnpg-system --create-namespace --wait
kubectl get deploy -n cnpg-system # operator Running
```
- [ ] Decide on storage: reuse `nfs-client` StorageClass, **or** prefer local
storage for the DB PVCs (Postgres on NFS is workable but not ideal;
CNPG defaults to RWO). Pick a `storageClass` + size per cluster below.
- [ ] Decide CNPG topology: homelab can run `instances: 1` (no HA) to keep it
light on the Pi nodes; bump to 23 later if desired.
- [ ] Take a manual backup of both instances first (safety net):
```bash
kubectl exec -n default pgsql-postgresql-0 -- \
bash -c 'PGPASSWORD=$POSTGRES_PASSWORD pg_dumpall -U postgres' > pgsql-all.sql
kubectl exec -n gitea gitea-postgresql-0 -- \
bash -c 'PGPASSWORD=$POSTGRES_PASSWORD pg_dumpall -U postgres' > gitea-all.sql
```
---
## Instance A — `pgsql` (media *arr + alhfmf), PG16
CNPG can pull data straight from the old server at bootstrap via
`initdb.import` (it runs `pg_dump`/`pg_restore` for you). Use **monolith** type
to copy all listed databases + roles in one shot.
- [ ] Keep the old `pgsql` release **running** during migration (read source).
- [ ] Create a secret with the old server's superuser creds for the importer:
```bash
# password: kubectl get secret pgsql-postgresql -n default -o jsonpath='{.data.postgres-password}' | base64 -d
kubectl create secret generic pg16-source-superuser -n default \
--from-literal=username=postgres --from-literal=password='<OLD_PW>'
```
- [ ] Apply a CNPG `Cluster` with import bootstrap (sketch — tune names/size):
```yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg16
namespace: default
spec:
instances: 1
imageName: ghcr.io/cloudnative-pg/postgresql:16 # match major 16
storage:
size: 10Gi
storageClass: nfs-client # or local SC — see prereqs
bootstrap:
initdb:
import:
type: monolith
databases: ["alhfmf","lidarr_db","lidarr_db_log","radarr_db","radarr_db_log","sonarr_db","sonarr_db_log","prowlarr_db","prowlarr_db_log"]
roles: ["*"]
source:
externalCluster: old-pgsql
externalClusters:
- name: old-pgsql
connectionParameters:
host: pgsql-postgresql.default.svc.cluster.local
user: postgres
dbname: postgres
password:
name: pg16-source-superuser
key: password
```
- [ ] Wait for import to finish: `kubectl get cluster pg16 -n default -w`
(phase → `Cluster in healthy state`). Check logs of the bootstrap job.
- [ ] Verify row counts / `\dt` in a couple of DBs vs. the old server.
- [ ] **Cutover:** the new service is `pg16-rw.default.svc.cluster.local:5432`.
Update each consumer's DB host:
- [ ] lidarr — `custom_helm_charts/lidarr` / `helm-values/lidarr_values.yaml`
- [ ] radarr — `helm-values/radarr_values.yaml`
- [ ] sonarr — `helm-values/sonarr_values.yaml`
- [ ] prowlarr — `custom_helm_charts/prowlarr` / `helm-values/prowlarr_values.yml`
- [ ] alhfmf — `alhfmf` release (find its DB env/secret)
(the *arr apps store DB host/creds in their `config.xml`/Postgres env —
confirm where each one is configured before flipping.)
Commit + push so Argo redeploys; verify each app reconnects.
- [ ] Soak for a few days.
- [ ] Decommission old: `helm uninstall pgsql -n default`, delete its PVC
(`data-pgsql-postgresql-0`) and `resources/pgsql_persistent_volume.yml`,
remove `non_argo_values/pgsql_values.yaml`, drop `pg16-source-superuser`.
---
## Instance B — `gitea` DB, PG17
Trickier because Postgres is a **subchart of the gitea release**, so it must be
externalized from the gitea chart.
- [ ] Stand up a CNPG `Cluster` `pg17` in the `gitea` ns, PG major **17**
(`imageName: ghcr.io/cloudnative-pg/postgresql:17`), same import approach:
`databases: ["gitea"]`, `roles: ["*"]`, source =
`gitea-postgresql.gitea.svc.cluster.local`, superuser secret from
`kubectl get secret gitea-postgresql -n gitea -o jsonpath='{.data.postgres-password}'`.
- [ ] **Quiesce gitea during final cutover** (scale gitea deploy to 0) so the
source DB is static, then run/refresh the import (or do a final `pg_dump`
of `gitea` and restore into `pg17`) to avoid losing writes.
- [ ] Edit `non_argo_values/gitea_values.yaml`:
- set `postgresql.enabled: false` (drop the bundled subchart)
- point `gitea.config.database` at the CNPG service:
```yaml
gitea:
config:
database:
DB_TYPE: postgres
HOST: pg17-rw.gitea.svc.cluster.local:5432
NAME: gitea
USER: gitea
PASSWD: <from CNPG-managed secret pg17-app>
```
(CNPG creates an app user/secret; either reuse the migrated `gitea` role or
wire gitea to the `pg17-app` secret.)
- [ ] `helm upgrade gitea ...` (Recreate strategy already set). Bring gitea back
up; verify login + that the GitOps repo (`admin/turingpi.git`) is intact —
**Argo depends on this**, so validate before moving on.
- [ ] Decommission: once `postgresql.enabled: false` is live, the old
`gitea-postgresql` StatefulSet is removed by the chart; delete leftover
PVC `data-gitea-postgresql-0` after confirming the new DB is good.
---
## Rollback
- Old instances stay intact until the explicit decommission steps — to roll
back, just point the consumer/gitea config back at the original
`*-postgresql` service. Keep the `pg_dumpall` files until both soaks pass.
## Open questions / decisions
- [ ] NFS vs local storage for DB PVCs (perf + RWO behavior on node loss).
- [ ] HA (`instances: 1` vs `3`) given Pi resource limits.
- [ ] Backups: configure CNPG scheduled backups (Barman to NFS or object store)
as part of this — replaces whatever (if any) backup the Bitnami charts did.
- [ ] Confirm exact per-app DB connection config for the 5 consumers of `pgsql`
before cutover (where each *arr stores its Postgres host/creds).
-46
View File
@@ -1,46 +0,0 @@
# Default values for my-actual-server.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: docker.io/actualbudget/actual-server
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: "latest"
service:
type: ClusterIP
port: 5006
ingress:
enabled: false
className: ""
annotations:
cert-manager.io/cluster-issuer: letsencrypt-production
kubernetes.io/ingress.class: traefik
hosts:
- host: actual.gilgamezh.me
paths:
- path: /
pathType: ImplementationSpecific
tls:
- hosts:
- actual.gilgamezh.me
secretName: actual-gilgamezh-me
volumes:
- name: "actual-data"
persistentVolumeClaim:
claimName: "actual-data" # PersistentVolumeClaim created earlier
volumeMounts:
- name: "actual-data"
mountPath: "/data"
nodeSelector: {}
tolerations: []
affinity: {}
+26
View File
@@ -0,0 +1,26 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: cross-seed
namespace: argocd
annotations:
spec:
project: default
source:
repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
path: custom_helm_charts/cross-seed
helm:
releaseName: cross-seed
valueFiles:
- ../../helm-values/cross-seed_values.yaml
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
+23
View File
@@ -0,0 +1,23 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: disk-usage-alert
namespace: argocd
spec:
project: default
source:
repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
path: manifests/disk-usage-alert
directory:
recurse: false
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
+69
View File
@@ -0,0 +1,69 @@
apiVersion: argocd-image-updater.argoproj.io/v1alpha1
kind: ImageUpdater
metadata:
name: media-image-updater
namespace: argocd
spec:
namespace: argocd
commonUpdateSettings:
updateStrategy: digest
writeBackConfig:
method: argocd
applicationRefs:
- namePattern: transmission
images:
- alias: transmission
imageName: haugene/transmission-openvpn:latest
manifestTargets:
helm:
name: image.repository
tag: image.tag
- namePattern: radarr
images:
- alias: radarr
imageName: ghcr.io/linuxserver/radarr:latest
manifestTargets:
helm:
name: image.repository
tag: image.tag
- namePattern: prowlarr
images:
- alias: prowlarr
imageName: lscr.io/linuxserver/prowlarr:latest
manifestTargets:
helm:
name: image.repository
tag: image.tag
- namePattern: ollama
images:
- alias: ollama
imageName: ollama/ollama:latest
manifestTargets:
helm:
name: image.name
tag: image.tag
- namePattern: plex
writeBackConfig:
method: git
gitConfig:
repository: "http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git"
branch: "master"
writeBackTarget: "helmvalues:/helm-values/plex_values.yml"
images:
- alias: plex
imageName: linuxserver/plex
commonUpdateSettings:
updateStrategy: newest-build
allowTags: "regexp:^(amd64-)?[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+-[0-9a-f]+-ls[0-9]+$"
manifestTargets:
helm:
name: image.repository
tag: image.tag
- namePattern: sonarr
images:
- alias: sonarr
imageName: ghcr.io/linuxserver/sonarr:latest
manifestTargets:
helm:
name: image.repository
tag: image.tag
+26
View File
@@ -0,0 +1,26 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: lidarr
namespace: argocd
annotations:
spec:
project: default
source:
repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
path: custom_helm_charts/lidarr
helm:
releaseName: lidarr
valueFiles:
- ../../helm-values/lidarr_values.yaml
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
+23
View File
@@ -0,0 +1,23 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: maintainerr
namespace: argocd
spec:
project: default
source:
repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
path: manifests/maintainerr
directory:
recurse: false
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
+26
View File
@@ -0,0 +1,26 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nzbget
namespace: argocd
annotations:
spec:
project: default
source:
repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
path: custom_helm_charts/nzbget
helm:
releaseName: nzbget
valueFiles:
- ../../helm-values/nzbget_values.yaml
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
+30
View File
@@ -0,0 +1,30 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ollama
namespace: argocd
annotations:
spec:
project: default
sources:
- repoURL: https://otwld.github.io/ollama-helm/
chart: ollama
targetRevision: "*"
ref: charts
helm:
releaseName: ollama
valueFiles:
- $values/helm-values/ollama_values.yaml
- repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
+30
View File
@@ -0,0 +1,30 @@
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: plex
namespace: argocd
spec:
project: default
sources:
- repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/kube-plex.git
path: charts/kube-plex
targetRevision: master
ref: charts
helm:
releaseName: plex
valueFiles:
- $values/helm-values/plex_values.yml
- repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: master
ref: values
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
+26
View File
@@ -0,0 +1,26 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: prowlarr
namespace: argocd
annotations:
spec:
project: default
source:
repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
path: custom_helm_charts/prowlarr
helm:
releaseName: prowlarr
valueFiles:
- ../../helm-values/prowlarr_values.yml
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
+26
View File
@@ -0,0 +1,26 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: qbittorrent
namespace: argocd
annotations:
spec:
project: default
source:
repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
path: custom_helm_charts/qbittorrent
helm:
releaseName: qbittorrent
valueFiles:
- ../../helm-values/qbittorrent_values.yaml
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
+33
View File
@@ -0,0 +1,33 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: radarr
namespace: argocd
annotations:
# Enable automatic image updates for Radarr
# Use digest strategy to track latest tag updates
# Write changes back via ArgoCD API (multi-source friendly)
spec:
project: default
sources:
- repoURL: https://bananaspliff.github.io/geek-charts
chart: radarr
targetRevision: "*"
ref: charts
helm:
releaseName: radarr
valueFiles:
- $values/helm-values/radarr_values.yaml
- repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
+33
View File
@@ -0,0 +1,33 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: sonarr
namespace: argocd
annotations:
# Enable automatic image updates for Sonarr
# Use digest strategy to track latest tag updates
# Write changes back via ArgoCD API (multi-source friendly)
spec:
project: default
sources:
- repoURL: https://bananaspliff.github.io/geek-charts
chart: sonarr
targetRevision: "*"
ref: charts
helm:
releaseName: sonarr
valueFiles:
- $values/helm-values/sonarr_values.yaml
- repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
+30
View File
@@ -0,0 +1,30 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: transmission
namespace: argocd
annotations:
spec:
project: default
sources:
- repoURL: https://bananaspliff.github.io/geek-charts
chart: transmission-openvpn
targetRevision: "*"
ref: charts
helm:
releaseName: transmission
valueFiles:
- $values/helm-values/transmission_values.yml
- repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
+37
View File
@@ -0,0 +1,37 @@
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
logs:
access:
enabled: true
format: common
# opcional: para logs de Traefik (no sólo access logs)
log:
level: INFO
format: json
# esto ya estaba, pero si querés mantenerlo:
deployment:
podAnnotations:
prometheus.io/port: "8082"
prometheus.io/scrape: "true"
providers:
kubernetesIngress:
publishedService:
enabled: true
priorityClassName: "system-cluster-critical"
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
service:
ipFamilyPolicy: "PreferDualStack"
+180
View File
@@ -0,0 +1,180 @@
#!/bin/bash
set -e
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# Configuration
MASTER_NODE="turing1"
WORKER_NODES=("turing2" "turing3" "turing4")
BEELINK_NODE="beelink.lan"
MASTER_IP="192.168.222.237"
TOKEN="torino"
SSH_PASSWORD="turing"
# Function to print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Function to check if sshpass is available
check_dependencies() {
if ! command -v sshpass &> /dev/null; then
print_warning "sshpass not found, installing..."
if command -v pacman &> /dev/null; then
sudo pacman -S sshpass --noconfirm
elif command -v apt-get &> /dev/null; then
sudo apt-get update && sudo apt-get install -y sshpass
else
print_error "Cannot install sshpass automatically. Please install it manually."
exit 1
fi
fi
}
# Function to upgrade master node
upgrade_master() {
print_status "Upgrading master node: $MASTER_NODE"
sshpass -p "$SSH_PASSWORD" ssh -o StrictHostKeyChecking=no root@$MASTER_NODE '
curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_DOWNLOAD=false sh -s - \
--write-kubeconfig-mode 644 \
--disable servicelb \
--token torino \
--node-ip 192.168.222.237 \
--disable-cloud-controller \
--disable local-storage
'
if [ $? -eq 0 ]; then
print_status "Master node $MASTER_NODE upgraded successfully"
else
print_error "Failed to upgrade master node $MASTER_NODE"
exit 1
fi
}
# Function to upgrade worker nodes
upgrade_worker() {
local node=$1
print_status "Upgrading worker node: $node"
sshpass -p "$SSH_PASSWORD" ssh -o StrictHostKeyChecking=no root@$node "
curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_DOWNLOAD=false \
K3S_URL=https://$MASTER_IP:6443 \
K3S_TOKEN=$TOKEN sh -
"
if [ $? -eq 0 ]; then
print_status "Worker node $node upgraded successfully"
else
print_error "Failed to upgrade worker node $node"
return 1
fi
}
# Function to upgrade beelink node
upgrade_beelink() {
print_status "Upgrading beelink node: $BEELINK_NODE"
ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no gilgamezh@$BEELINK_NODE "
sudo curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_DOWNLOAD=false \
K3S_URL=https://$MASTER_IP:6443 \
K3S_TOKEN=$TOKEN sh -
"
if [ $? -eq 0 ]; then
print_status "Beelink node $BEELINK_NODE upgraded successfully"
else
print_error "Failed to upgrade beelink node $BEELINK_NODE"
return 1
fi
}
# Function to verify cluster health
verify_cluster() {
print_status "Verifying cluster health..."
# Wait a moment for nodes to register
sleep 10
print_status "Cluster nodes:"
kubectl get nodes
print_status "Checking for unhealthy pods..."
unhealthy_pods=$(kubectl get pods --all-namespaces | grep -v Running | grep -v Completed | wc -l)
if [ "$unhealthy_pods" -gt 1 ]; then # Greater than 1 because header line counts
print_warning "Found unhealthy pods:"
kubectl get pods --all-namespaces | grep -v Running | grep -v Completed
else
print_status "All pods are healthy"
fi
print_status "Cluster upgrade completed successfully!"
}
# Main execution
main() {
print_status "Starting K3s cluster upgrade..."
print_status "This will upgrade all nodes in the TuringPi cluster"
# Check dependencies
check_dependencies
# Show current cluster state
print_status "Current cluster state:"
kubectl get nodes
read -p "Do you want to continue with the upgrade? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_status "Upgrade cancelled"
exit 0
fi
# Upgrade master node first
upgrade_master
# Wait a bit for master to stabilize
sleep 15
# Upgrade worker nodes
failed_workers=0
for worker in "${WORKER_NODES[@]}"; do
if ! upgrade_worker "$worker"; then
((failed_workers++))
fi
sleep 5 # Brief pause between worker upgrades
done
# Upgrade beelink node
if ! upgrade_beelink; then
print_warning "Beelink node upgrade failed, but continuing..."
fi
# Verify cluster health
verify_cluster
if [ $failed_workers -gt 0 ]; then
print_warning "Upgrade completed with $failed_workers failed worker node(s)"
exit 1
else
print_status "All nodes upgraded successfully!"
fi
}
# Run main function
main "$@"
+5
View File
@@ -0,0 +1,5 @@
apiVersion: v1
appVersion: "1.0"
description: cross-seed - Torrent seeding assistant
name: cross-seed
version: 0.1.0
+17
View File
@@ -0,0 +1,17 @@
# cross-seed
Service endpoint:
- cross-seed UI: cross-seed.default.svc.cluster.local:2468
Config notes:
- Config file is generated from values at `custom_helm_charts/cross-seed/templates/configmap.yaml`.
- Update `helm-values/cross-seed_values.yaml` for qbittorrent URL, torznab list, and paths.
Secrets:
- Secret name: `cross-seed-secrets`
- Required keys:
- `QBITTORRENT_USERNAME`
- `QBITTORRENT_PASSWORD`
- `NZBGEEK_API_KEY`
After setting qBittorrent credentials and NZBGeek API key, restart the cross-seed deployment.
@@ -0,0 +1,19 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "cross-seed.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ template "cross-seed.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "cross-seed.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "cross-seed.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:2468 to use your application"
kubectl port-forward $POD_NAME 2468:{{ .Values.service.port }}
{{- end }}
@@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "cross-seed.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "cross-seed.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "cross-seed.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
@@ -0,0 +1,30 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cross-seed-config
labels:
app: {{ template "cross-seed.name" . }}
chart: {{ template "cross-seed.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
data:
config.js: |
module.exports = {
torrentClients: [
{
type: "qbittorrent",
url: "{{ .Values.config.qbittorrentUrl }}",
username: process.env.QBITTORRENT_USERNAME,
password: process.env.QBITTORRENT_PASSWORD
}
],
host: "{{ .Values.config.host }}",
port: {{ .Values.config.port }},
torrentDir: "{{ .Values.config.torrentDir }}",
dataDirs: {{ toJson .Values.config.dataDirs }},
outputDir: {{ toJson .Values.config.outputDir }},
action: "{{ .Values.config.action }}",
torznab: [
`{{ .Values.config.torznabBaseUrl }}?t=search&apikey=${process.env.NZBGEEK_API_KEY}`
]
};
@@ -0,0 +1,61 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "cross-seed.fullname" . }}
labels:
app: {{ template "cross-seed.name" . }}
chart: {{ template "cross-seed.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "cross-seed.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "cross-seed.name" . }}
release: {{ .Release.Name }}
spec:
volumes:
{{ toYaml .Values.volumes | indent 6 }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- with .Values.command }}
command:
{{ toYaml . | indent 12 }}
{{- end }}
{{- with .Values.args }}
args:
{{ toYaml . | indent 12 }}
{{- end }}
env:
{{ toYaml .Values.env | indent 12 }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
{{ toYaml .Values.livenessProbe | indent 12 }}
readinessProbe:
{{ toYaml .Values.readinessProbe | indent 12 }}
volumeMounts:
{{ toYaml .Values.volumeMounts | indent 12 }}
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}
@@ -0,0 +1,38 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "cross-seed.fullname" . -}}
{{- $ingressPath := .Values.ingress.path -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
app: {{ template "cross-seed.name" . }}
chart: {{ template "cross-seed.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ . }}
http:
paths:
- path: {{ $ingressPath }}
backend:
serviceName: {{ $fullName }}
servicePort: http
{{- end }}
{{- end }}
@@ -0,0 +1,16 @@
{{- if .Values.secret.create -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Values.secret.name }}
labels:
app: {{ template "cross-seed.name" . }}
chart: {{ template "cross-seed.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
type: Opaque
stringData:
QBITTORRENT_USERNAME: {{ .Values.secret.qbittorrentUsername | quote }}
QBITTORRENT_PASSWORD: {{ .Values.secret.qbittorrentPassword | quote }}
NZBGEEK_API_KEY: {{ .Values.secret.nzbgeekApiKey | quote }}
{{- end }}
@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "cross-seed.fullname" . }}
labels:
app: {{ template "cross-seed.name" . }}
chart: {{ template "cross-seed.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.port }}
protocol: TCP
name: http
selector:
app: {{ template "cross-seed.name" . }}
release: {{ .Release.Name }}
+121
View File
@@ -0,0 +1,121 @@
replicaCount: 1
image:
repository: ghcr.io/cross-seed/cross-seed
tag: "6"
pullPolicy: IfNotPresent
env:
- name: TZ
value: "Europe/Amsterdam"
- name: CONFIG_DIR
value: "/config"
- name: DOCKER_ENV
value: "true"
- name: QBITTORRENT_USERNAME
valueFrom:
secretKeyRef:
name: cross-seed-secrets
key: QBITTORRENT_USERNAME
- name: QBITTORRENT_PASSWORD
valueFrom:
secretKeyRef:
name: cross-seed-secrets
key: QBITTORRENT_PASSWORD
- name: NZBGEEK_API_KEY
valueFrom:
secretKeyRef:
name: cross-seed-secrets
key: NZBGEEK_API_KEY
command:
- "cross-seed"
args:
- "daemon"
config:
qbittorrentUrl: "http://qbittorrent.default.svc.cluster.local:8080"
host: "0.0.0.0"
port: 2468
torrentDir: "/qbittorrent-config/qBittorrent/BT_backup"
dataDirs:
- "/data/torrents"
outputDir: null
action: "inject"
torznabBaseUrl: "https://api.nzbgeek.info/api"
secret:
create: true
name: cross-seed-secrets
qbittorrentUsername: "REPLACE_ME"
qbittorrentPassword: "REPLACE_ME"
nzbgeekApiKey: "REPLACE_ME"
service:
type: ClusterIP
port: 2468
ingress:
enabled: false
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
path: /
hosts:
- cross-seed.example.org
tls:
- secretName: cross-seed-example-org
hosts:
- cross-seed.example.org
volumes:
- name: plex-data
persistentVolumeClaim:
claimName: plex-data
- name: config
configMap:
name: cross-seed-config
volumeMounts:
- name: plex-data
mountPath: "/config"
subPath: "configs/cross-seed"
- name: config
mountPath: "/config/config.js"
subPath: "config.js"
- name: plex-data
mountPath: "/qbittorrent-config"
subPath: "configs/qbittorrent"
- name: plex-data
mountPath: "/data/torrents"
subPath: "torrent"
livenessProbe:
tcpSocket:
port: 2468
initialDelaySeconds: 10
periodSeconds: 20
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 2468
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
nodeSelector: {}
tolerations: []
affinity: {}
@@ -0,0 +1,24 @@
apiVersion: v2
name: home-assistant-voice-llms
description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"
@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "home-assistant-voice-llms.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "home-assistant-voice-llms.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "home-assistant-voice-llms.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "home-assistant-voice-llms.labels" -}}
helm.sh/chart: {{ include "home-assistant-voice-llms.chart" . }}
{{ include "home-assistant-voice-llms.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "home-assistant-voice-llms.selectorLabels" -}}
app.kubernetes.io/name: {{ include "home-assistant-voice-llms.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "home-assistant-voice-llms.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "home-assistant-voice-llms.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
@@ -0,0 +1,97 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-faster-whisper
spec:
replicas: {{ .Values.defaults.replicaCount }}
selector:
matchLabels:
app: faster-whisper
template:
metadata:
labels:
app: faster-whisper
spec:
containers:
- name: faster-whisper
image: {{ .Values.defaults.fasterWhisper.image.repository }}:{{ .Values.defaults.fasterWhisper.image.tag }}
imagePullPolicy: {{ .Values.defaults.fasterWhisper.image.pullPolicy }}
env:
- name: PUID
value: "{{ .Values.defaults.fasterWhisper.env.PUID }}"
- name: PGID
value: "{{ .Values.defaults.fasterWhisper.env.PGID }}"
- name: TZ
value: "{{ .Values.defaults.fasterWhisper.env.TZ }}"
- name: WHISPER_MODEL
value: "{{ .Values.defaults.fasterWhisper.env.WHISPER_MODEL }}"
- name: WHISPER_BEAM
value: "{{ .Values.defaults.fasterWhisper.env.WHISPER_BEAM }}"
- name: WHISPER_LANG
value: "{{ .Values.defaults.fasterWhisper.env.WHISPER_LANG }}"
ports:
- containerPort: 10300
volumeMounts:
- mountPath: {{ .Values.defaults.fasterWhisper.volume.mountPath }}
name: config
{{- if .Values.nodeSelector }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}
volumes:
- name: config
persistentVolumeClaim:
claimName: {{ .Values.defaults.fasterWhisper.volume.claimName }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-piper
spec:
replicas: {{ .Values.defaults.replicaCount }}
selector:
matchLabels:
app: piper
template:
metadata:
labels:
app: piper
spec:
containers:
- name: piper
image: {{ .Values.defaults.piper.image.repository }}:{{ .Values.defaults.piper.image.tag }}
imagePullPolicy: {{ .Values.defaults.piper.image.pullPolicy }}
env:
- name: PUID
value: "{{ .Values.defaults.piper.env.PUID }}"
- name: PGID
value: "{{ .Values.defaults.piper.env.PGID }}"
- name: TZ
value: "{{ .Values.defaults.piper.env.TZ }}"
- name: PIPER_VOICE
value: "{{ .Values.defaults.piper.env.PIPER_VOICE }}"
- name: PIPER_LENGTH
value: "{{ .Values.defaults.piper.env.PIPER_LENGTH }}"
- name: PIPER_NOISE
value: "{{ .Values.defaults.piper.env.PIPER_NOISE }}"
- name: PIPER_NOISEW
value: "{{ .Values.defaults.piper.env.PIPER_NOISEW }}"
- name: PIPER_SPEAKER
value: "{{ .Values.defaults.piper.env.PIPER_SPEAKER }}"
- name: PIPER_PROCS
value: "{{ .Values.defaults.piper.env.PIPER_PROCS }}"
ports:
- containerPort: 10200
volumeMounts:
- mountPath: {{ .Values.defaults.piper.volume.mountPath }}
name: config
volumes:
- name: config
persistentVolumeClaim:
claimName: {{ .Values.defaults.piper.volume.claimName }}
{{- if .Values.nodeSelector }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}
@@ -0,0 +1,25 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Values.defaults.fasterWhisper.volume.claimName }}
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .Values.defaults.fasterWhisper.volume.storage }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ .Values.defaults.piper.volume.claimName }}
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .Values.defaults.piper.volume.storage }}
@@ -0,0 +1,25 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-faster-whisper
spec:
type: {{ .Values.defaults.fasterWhisper.service.type }}
ports:
- port: {{ .Values.defaults.fasterWhisper.service.port }}
targetPort: 10300
selector:
app: faster-whisper
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-piper
spec:
type: {{ .Values.defaults.piper.service.type }}
ports:
- port: {{ .Values.defaults.piper.service.port }}
targetPort: 10200
selector:
app: piper
@@ -0,0 +1,57 @@
defaults:
replicaCount: 1
fasterWhisper:
image:
repository: lscr.io/linuxserver/faster-whisper
tag: "2.4.0"
pullPolicy: IfNotPresent
service:
type: LoadBalancer
port: 10300
resources:
limits:
cpu: "3"
memory: "5000Mi"
requests:
cpu: "3"
memory: "2000Mi"
env:
PUID: 1000
PGID: 1000
TZ: Europe/Amsterdam
WHISPER_MODEL: small-int8
WHISPER_BEAM: 1
WHISPER_LANG: en
volume:
mountPath: /config
claimName: faster-whisper-pvc
storage: 1Gi
piper:
image:
repository: lscr.io/linuxserver/piper
tag: "latest"
pullPolicy: IfNotPresent
service:
type: LoadBalancer
port: 10200
resources:
limits:
cpu: "2"
memory: "4000Mi"
requests:
cpu: "1"
memory: "2000Mi"
env:
PUID: 1000
PGID: 1000
TZ: Europe/Amsterdam
PIPER_VOICE: en_US-lessac-medium
PIPER_LENGTH: 1.0
PIPER_NOISE: 0.667
PIPER_NOISEW: 0.333
PIPER_SPEAKER: 0
PIPER_PROCS: 2
volume:
mountPath: /config
claimName: piper-pvc
storage: 1Gi
+21
View File
@@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v1
appVersion: "1.0"
description: Lidarr - Automatic music downloader and manager
name: lidarr
version: 0.1.0
icon: https://lidarr.audio/img/logo.png

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

@@ -0,0 +1,19 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "lidarr.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ template "lidarr.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "lidarr.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "lidarr.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8686 to use your application"
kubectl port-forward $POD_NAME 8686:{{ .Values.service.port }}
{{- end }}
@@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "lidarr.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "lidarr.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "lidarr.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
@@ -0,0 +1,49 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "lidarr.fullname" . }}
labels:
app: {{ template "lidarr.name" . }}
chart: {{ template "lidarr.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "lidarr.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "lidarr.name" . }}
release: {{ .Release.Name }}
spec:
volumes:
{{ toYaml .Values.volumes | indent 6 }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
{{ toYaml .Values.env | indent 12 }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
volumeMounts:
{{ toYaml .Values.volumeMounts | indent 12 }}
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}
@@ -0,0 +1,38 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "lidarr.fullname" . -}}
{{- $ingressPath := .Values.ingress.path -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
app: {{ template "lidarr.name" . }}
chart: {{ template "lidarr.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ . }}
http:
paths:
- path: {{ $ingressPath }}
backend:
serviceName: {{ $fullName }}
servicePort: http
{{- end }}
{{- end }}
@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "lidarr.fullname" . }}
labels:
app: {{ template "lidarr.name" . }}
chart: {{ template "lidarr.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.port }}
protocol: TCP
name: http
selector:
app: {{ template "lidarr.name" . }}
release: {{ .Release.Name }}
+60
View File
@@ -0,0 +1,60 @@
replicaCount: 1
image:
repository: ghcr.io/linuxserver/lidarr
tag: latest
pullPolicy: Always
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: "Europe/Amsterdam"
service:
type: ClusterIP
port: 8686
ingress:
enabled: false
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
path: /
hosts:
- lidarr.example.org
tls:
- secretName: lidarr-example-org
hosts:
- lidarr.example.org
volumes:
- name: plex-data
persistentVolumeClaim:
claimName: plex-data
volumeMounts:
- name: plex-data
mountPath: "/config"
subPath: "configs/lidarr"
- name: plex-data
mountPath: "/nfs"
resources:
requests:
memory: 500Mi
cpu: 500m
ephemeral-storage: 50Mi
limits:
memory: 800Mi
cpu: 1000m
ephemeral-storage: 1Gi
nodeSelector: {}
tolerations: []
affinity: {}
@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
+21
View File
@@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
+5
View File
@@ -0,0 +1,5 @@
apiVersion: v1
appVersion: "1.0"
description: NZBGet - Usenet downloader
name: nzbget
version: 0.1.0
@@ -0,0 +1,19 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "nzbget.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ template "nzbget.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "nzbget.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "nzbget.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:6789 to use your application"
kubectl port-forward $POD_NAME 6789:{{ .Values.service.port }}
{{- end }}
@@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "nzbget.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "nzbget.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "nzbget.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
@@ -0,0 +1,76 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "nzbget.fullname" . }}
labels:
app: {{ template "nzbget.name" . }}
chart: {{ template "nzbget.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "nzbget.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "nzbget.name" . }}
release: {{ .Release.Name }}
spec:
volumes:
{{ toYaml .Values.volumes | indent 6 }}
{{- with .Values.initContainers }}
initContainers:
{{ toYaml . | indent 8 }}
{{- end }}
containers:
- name: gluetun
image: "{{ .Values.gluetun.image.repository }}:{{ .Values.gluetun.image.tag }}"
imagePullPolicy: {{ .Values.gluetun.image.pullPolicy }}
securityContext:
{{ toYaml .Values.gluetun.securityContext | indent 12 }}
env:
{{ toYaml .Values.gluetun.env | indent 12 }}
livenessProbe:
{{ toYaml .Values.gluetun.livenessProbe | indent 12 }}
readinessProbe:
{{ toYaml .Values.gluetun.readinessProbe | indent 12 }}
volumeMounts:
{{ toYaml .Values.gluetun.volumeMounts | indent 12 }}
resources:
{{ toYaml .Values.gluetun.resources | indent 12 }}
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- with .Values.command }}
command:
{{ toYaml . | indent 12 }}
{{- end }}
env:
{{ toYaml .Values.env | indent 12 }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
{{ toYaml .Values.livenessProbe | indent 12 }}
readinessProbe:
{{ toYaml .Values.readinessProbe | indent 12 }}
volumeMounts:
{{ toYaml .Values.volumeMounts | indent 12 }}
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}
@@ -0,0 +1,38 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "nzbget.fullname" . -}}
{{- $ingressPath := .Values.ingress.path -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
app: {{ template "nzbget.name" . }}
chart: {{ template "nzbget.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ . }}
http:
paths:
- path: {{ $ingressPath }}
backend:
serviceName: {{ $fullName }}
servicePort: http
{{- end }}
{{- end }}
@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "nzbget.fullname" . }}
labels:
app: {{ template "nzbget.name" . }}
chart: {{ template "nzbget.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.port }}
protocol: TCP
name: http
selector:
app: {{ template "nzbget.name" . }}
release: {{ .Release.Name }}
+86
View File
@@ -0,0 +1,86 @@
replicaCount: 1
image:
repository: lscr.io/linuxserver/nzbget
tag: latest
pullPolicy: IfNotPresent
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: "Europe/Amsterdam"
- name: HTTP_PROXY
value: "http://gluetun.default.svc.cluster.local:8888"
- name: http_proxy
value: "http://gluetun.default.svc.cluster.local:8888"
- name: HTTPS_PROXY
value: "http://gluetun.default.svc.cluster.local:8888"
- name: https_proxy
value: "http://gluetun.default.svc.cluster.local:8888"
- name: NO_PROXY
value: "localhost,127.0.0.1,.svc,.cluster.local"
service:
type: ClusterIP
port: 6789
ingress:
enabled: false
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
path: /
hosts:
- nzbget.example.org
tls:
- secretName: nzbget-example-org
hosts:
- nzbget.example.org
volumes:
- name: plex-data
persistentVolumeClaim:
claimName: plex-data
volumeMounts:
- name: plex-data
mountPath: "/config"
subPath: "configs/nzbget"
- name: plex-data
mountPath: "/downloads"
subPath: "usenet"
livenessProbe:
tcpSocket:
port: 6789
initialDelaySeconds: 10
periodSeconds: 20
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 6789
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
memory: "500Mi"
cpu: "500m"
ephemeral-storage: "50Mi"
limits:
memory: "2Gi"
cpu: "2"
ephemeral-storage: "1Gi"
nodeSelector: {}
tolerations: []
affinity: {}
+21
View File
@@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

@@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
@@ -0,0 +1,5 @@
apiVersion: v1
appVersion: "1.0"
description: qBittorrent - BitTorrent client (VPN via Gluetun sidecar)
name: qbittorrent
version: 0.1.0
+11
View File
@@ -0,0 +1,11 @@
# qBittorrent
Service endpoint:
- qBittorrent UI: qbittorrent.default.svc.cluster.local:8080
VPN routing:
- qBittorrent runs in the same pod as a Gluetun sidecar and uses the `gluetun-wireguard` secret.
- Update `helm-values/qbittorrent_values.yaml` if you change AirVPN WireGuard addresses or server selection.
Port forwarding:
- Set `qbittorrent.env.TORRENTING_PORT` and `gluetun.env.FIREWALL_VPN_INPUT_PORTS` to your AirVPN forwarded port if you use VPN port forwarding.
@@ -0,0 +1,19 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "qbittorrent.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ template "qbittorrent.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "qbittorrent.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "qbittorrent.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:{{ .Values.service.port }}
{{- end }}
@@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "qbittorrent.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "qbittorrent.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "qbittorrent.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
@@ -0,0 +1,76 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "qbittorrent.fullname" . }}
labels:
app: {{ template "qbittorrent.name" . }}
chart: {{ template "qbittorrent.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
strategy:
type: Recreate
selector:
matchLabels:
app: {{ template "qbittorrent.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "qbittorrent.name" . }}
release: {{ .Release.Name }}
spec:
volumes:
{{ toYaml .Values.volumes | indent 6 }}
containers:
- name: gluetun
image: "{{ .Values.gluetun.image.repository }}:{{ .Values.gluetun.image.tag }}"
imagePullPolicy: {{ .Values.gluetun.image.pullPolicy }}
securityContext:
{{ toYaml .Values.gluetun.securityContext | indent 12 }}
env:
{{ toYaml .Values.gluetun.env | indent 12 }}
livenessProbe:
{{ toYaml .Values.gluetun.livenessProbe | indent 12 }}
readinessProbe:
{{ toYaml .Values.gluetun.readinessProbe | indent 12 }}
volumeMounts:
{{ toYaml .Values.gluetun.volumeMounts | indent 12 }}
resources:
{{ toYaml .Values.gluetun.resources | indent 12 }}
- name: qbittorrent
image: "{{ .Values.qbittorrent.image.repository }}:{{ .Values.qbittorrent.image.tag }}"
imagePullPolicy: {{ .Values.qbittorrent.image.pullPolicy }}
env:
{{ toYaml .Values.qbittorrent.env | indent 12 }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
- name: torrent
containerPort: {{ .Values.qbittorrent.torrentPort }}
protocol: TCP
- name: torrent-udp
containerPort: {{ .Values.qbittorrent.torrentPort }}
protocol: UDP
livenessProbe:
{{ toYaml .Values.qbittorrent.livenessProbe | indent 12 }}
readinessProbe:
{{ toYaml .Values.qbittorrent.readinessProbe | indent 12 }}
volumeMounts:
{{ toYaml .Values.qbittorrent.volumeMounts | indent 12 }}
resources:
{{ toYaml .Values.qbittorrent.resources | indent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}
@@ -0,0 +1,38 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "qbittorrent.fullname" . -}}
{{- $ingressPath := .Values.ingress.path -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
app: {{ template "qbittorrent.name" . }}
chart: {{ template "qbittorrent.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ . }}
http:
paths:
- path: {{ $ingressPath }}
backend:
serviceName: {{ $fullName }}
servicePort: http
{{- end }}
{{- end }}
@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "qbittorrent.fullname" . }}
labels:
app: {{ template "qbittorrent.name" . }}
chart: {{ template "qbittorrent.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.port }}
protocol: TCP
name: http
selector:
app: {{ template "qbittorrent.name" . }}
release: {{ .Release.Name }}
+142
View File
@@ -0,0 +1,142 @@
replicaCount: 1
qbittorrent:
image:
repository: lscr.io/linuxserver/qbittorrent
tag: "5.1.0"
pullPolicy: IfNotPresent
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: "Europe/Amsterdam"
- name: WEBUI_PORT
value: "8080"
- name: TORRENTING_PORT
value: "6881"
torrentPort: 6881
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 10
periodSeconds: 20
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
memory: "500Mi"
cpu: "500m"
ephemeral-storage: "50Mi"
limits:
memory: "2Gi"
cpu: "2"
ephemeral-storage: "1Gi"
volumeMounts:
- name: plex-data
mountPath: "/config"
subPath: "configs/qbittorrent"
- name: plex-data
mountPath: "/data/torrents"
subPath: "torrent"
gluetun:
image:
repository: qmcgaw/gluetun
tag: latest
pullPolicy: IfNotPresent
env:
- name: VPN_SERVICE_PROVIDER
value: "airvpn"
- name: VPN_TYPE
value: "wireguard"
- name: WIREGUARD_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: gluetun-wireguard
key: WIREGUARD_PRIVATE_KEY
- name: WIREGUARD_PRESHARED_KEY
valueFrom:
secretKeyRef:
name: gluetun-wireguard
key: WIREGUARD_PRESHARED_KEY
- name: WIREGUARD_ADDRESSES
value: "10.160.17.207/32,fd7d:76ee:e68f:a993:61d7:a5fe:f834:90e1/128"
- name: SERVER_COUNTRIES
value: "Netherlands"
- name: FIREWALL_INPUT_PORTS
value: "8080"
- name: FIREWALL_VPN_INPUT_PORTS
value: "6881"
- name: TZ
value: "Europe/Amsterdam"
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_ADMIN
livenessProbe:
tcpSocket:
port: 8000
initialDelaySeconds: 10
periodSeconds: 20
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
memory: 128Mi
cpu: 100m
limits:
memory: 512Mi
cpu: 500m
volumeMounts:
- name: dev-tun
mountPath: "/dev/net/tun"
service:
type: ClusterIP
port: 8080
ingress:
enabled: false
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
path: /
hosts:
- qbittorrent.example.org
tls:
- secretName: qbittorrent-example-org
hosts:
- qbittorrent.example.org
volumes:
- name: plex-data
persistentVolumeClaim:
claimName: plex-data
- name: dev-tun
hostPath:
path: /dev/net/tun
nodeSelector: {}
tolerations: []
affinity: {}
-20
View File
@@ -1,20 +0,0 @@
---
image:
repository: ghcr.io/flaresolverr/flaresolverr
pullPolicy: IfNotPresent
tag: v3.3.21
env:
TZ: UTC
service:
main:
ports:
http:
port: 8191
ingress:
# -- Enable and configure ingress settings for the chart under this key.
# @default -- See values.yaml
main:
enabled: false
-2
View File
@@ -1,2 +0,0 @@
ID: e464f256-841f-4ba6-a489-b72b0530f9ec
Key: 5a81e9238d361cce4393f56b8db8f4be2f23b0e9d571667c4a5625a7ca844a528195496d571dabf81b51a2480d07ac8ef0397b05f8cd7393984e4f74b516c6f9
+108
View File
@@ -0,0 +1,108 @@
replicaCount: 1
image:
repository: ghcr.io/cross-seed/cross-seed
tag: "6"
pullPolicy: Always
env:
- name: TZ
value: "Europe/Amsterdam"
- name: CONFIG_DIR
value: "/config"
- name: DOCKER_ENV
value: "true"
- name: QBITTORRENT_USERNAME
valueFrom:
secretKeyRef:
name: cross-seed-secrets
key: QBITTORRENT_USERNAME
- name: QBITTORRENT_PASSWORD
valueFrom:
secretKeyRef:
name: cross-seed-secrets
key: QBITTORRENT_PASSWORD
- name: NZBGEEK_API_KEY
valueFrom:
secretKeyRef:
name: cross-seed-secrets
key: NZBGEEK_API_KEY
command:
- "cross-seed"
args:
- "daemon"
config:
qbittorrentUrl: "http://qbittorrent.default.svc.cluster.local:8080"
host: "0.0.0.0"
port: 2468
torrentDir: "/qbittorrent-config/qBittorrent/BT_backup"
dataDirs:
- "/data/torrents"
outputDir: null
action: "inject"
torznabBaseUrl: "https://api.nzbgeek.info/api"
secret:
create: true
name: cross-seed-secrets
qbittorrentUsername: "REPLACE_ME"
qbittorrentPassword: "REPLACE_ME"
nzbgeekApiKey: "REPLACE_ME"
service:
type: ClusterIP
port: 2468
volumes:
- name: plex-data
persistentVolumeClaim:
claimName: plex-data
- name: config
configMap:
name: cross-seed-config
volumeMounts:
- name: plex-data
mountPath: "/config"
subPath: "configs/cross-seed"
- name: config
mountPath: "/config/config.js"
subPath: "config.js"
- name: plex-data
mountPath: "/qbittorrent-config"
subPath: "configs/qbittorrent"
- name: plex-data
mountPath: "/data/torrents"
subPath: "torrent"
livenessProbe:
tcpSocket:
port: 2468
initialDelaySeconds: 10
periodSeconds: 20
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 2468
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
nodeSelector: {}
tolerations: []
affinity: {}
+41
View File
@@ -0,0 +1,41 @@
---
replicaCount: 1
image:
repository: ghcr.io/linuxserver/lidarr
tag: "latest"
pullPolicy: Always
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: "Europe/Amsterdam"
service:
type: ClusterIP
port: 8686
volumes:
- name: plex-data
persistentVolumeClaim:
claimName: plex-data
volumeMounts:
- name: plex-data
mountPath: "/config"
subPath: "configs/lidarr"
- name: plex-data
mountPath: "/nfs"
resources:
requests:
memory: "500Mi"
cpu: "500m"
ephemeral-storage: "50Mi"
limits:
memory: "4Gi"
cpu: "4000m"
ephemeral-storage: "1Gi"
+199
View File
@@ -0,0 +1,199 @@
replicaCount: 1
image:
repository: lscr.io/linuxserver/nzbget
tag: "latest"
pullPolicy: Always
# Wait for the gluetun sidecar's tunnel to come up before starting nzbget.
# Without this, nzbget races gluetun on pod start: /etc/resolv.conf points at
# 10.128.0.1 (reachable only via the tunnel), so during the ~20s gluetun takes
# to establish WireGuard, every DNS lookup from nzbget fails with EAI_AGAIN.
# Any items in the queue at that moment hit nzbget's per-file HealthCheck
# threshold (~97.9%) and get auto-cancelled — even ones that would otherwise
# complete fine. Polling DNS resolution is the direct test: if it resolves,
# the tunnel is up and DNS works.
command:
- sh
- -c
- |
until nslookup news.newshosting.com >/dev/null 2>&1; do
echo "waiting for gluetun tunnel + DNS..."
sleep 2
done
echo "tunnel up, starting nzbget"
exec /init
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: "Europe/Amsterdam"
# gluetun runs as a sidecar in this pod (same pattern as qbittorrent): it shares
# the pod network namespace and installs the WireGuard tunnel + a kill-switch, so
# ALL of nzbget's traffic — including NNTP (port 563) to newshosting — egresses
# through the VPN. (An HTTP proxy can't cover NNTP, which is why the old
# HTTP_PROXY-to-standalone-gluetun approach left usenet downloads going direct.)
# Uses its own AirVPN device/secret (gluetun-wireguard-nzbget) to avoid sharing a
# WireGuard IP with the qbittorrent tunnel. Keep DOT=off + DNS_ADDRESS — see the
# AirVPN-blocks-DoT gotcha in CLAUDE.md.
gluetun:
image:
repository: qmcgaw/gluetun
tag: v3.41.1
pullPolicy: IfNotPresent
env:
- name: VPN_SERVICE_PROVIDER
value: "airvpn"
- name: VPN_TYPE
value: "wireguard"
- name: WIREGUARD_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: gluetun-wireguard-nzbget
key: WIREGUARD_PRIVATE_KEY
- name: WIREGUARD_PRESHARED_KEY
valueFrom:
secretKeyRef:
name: gluetun-wireguard-nzbget
key: WIREGUARD_PRESHARED_KEY
- name: WIREGUARD_ADDRESSES
value: "10.166.207.220/32,fd7d:76ee:e68f:a993:bd5e:ddfc:ad2c:d30c/128"
- name: SERVER_COUNTRIES
value: "Netherlands"
- name: DOT
value: "off"
- name: DNS_ADDRESS
value: "10.128.0.1"
- name: FIREWALL_INPUT_PORTS
value: "6789"
- name: TZ
value: "Europe/Amsterdam"
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_ADMIN
livenessProbe:
tcpSocket:
port: 8000
initialDelaySeconds: 10
periodSeconds: 20
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
memory: 128Mi
cpu: 100m
limits:
memory: 512Mi
cpu: 500m
volumeMounts:
- name: dev-tun
mountPath: "/dev/net/tun"
# nzbget cannot read server credentials from environment variables (its
# ${...} config syntax only references other nzbget options, not env). So an
# init container renders the Server1 (newshosting) block into nzbget.conf on
# every start: the non-secret settings live here in git, while the username
# and password come from the out-of-band `usenet-creds` Secret (same pattern
# as gluetun-wireguard — secret not committed). Rotating the secret + a pod
# restart re-renders the creds. No provider password is ever stored in git.
initContainers:
- name: render-newshosting
image: lscr.io/linuxserver/nzbget:latest
command:
- sh
- -c
- |
f=/config/nzbget.conf
[ -f "$f" ] || { echo "nzbget.conf absent; main container will seed defaults"; exit 0; }
sed -i \
-e "s|^Server1.Active=.*|Server1.Active=yes|" \
-e "s|^Server1.Name=.*|Server1.Name=newshosting|" \
-e "s|^Server1.Host=.*|Server1.Host=news.newshosting.com|" \
-e "s|^Server1.Port=.*|Server1.Port=563|" \
-e "s|^Server1.Encryption=.*|Server1.Encryption=yes|" \
-e "s|^Server1.Connections=.*|Server1.Connections=50|" \
-e "s|^ArticleCache=.*|ArticleCache=700|" \
-e "s|^WriteBuffer=.*|WriteBuffer=1024|" \
-e "s|^Server1.Username=.*|Server1.Username=${NEWSHOSTING_USER}|" \
-e "s|^Server1.Password=.*|Server1.Password=${NEWSHOSTING_PASS}|" \
"$f"
echo "rendered newshosting Server1 block into nzbget.conf"
env:
- name: NEWSHOSTING_USER
valueFrom:
secretKeyRef:
name: usenet-creds
key: NEWSHOSTING_USER
- name: NEWSHOSTING_PASS
valueFrom:
secretKeyRef:
name: usenet-creds
key: NEWSHOSTING_PASS
volumeMounts:
- name: plex-data
mountPath: /config
subPath: configs/nzbget
service:
type: ClusterIP
port: 6789
volumes:
- name: plex-data
persistentVolumeClaim:
claimName: "plex-data"
- name: dev-tun
hostPath:
path: /dev/net/tun
volumeMounts:
- name: plex-data
mountPath: "/config"
subPath: "configs/nzbget"
- name: plex-data
mountPath: "/nfs"
livenessProbe:
tcpSocket:
port: 6789
initialDelaySeconds: 10
periodSeconds: 20
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 6789
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
memory: "500Mi"
cpu: "500m"
ephemeral-storage: "50Mi"
limits:
memory: "2Gi"
cpu: "2"
ephemeral-storage: "1Gi"
nodeSelector: {}
tolerations: []
affinity: {}
+22
View File
@@ -0,0 +1,22 @@
replicaCount: 1
image:
repository: ollama/ollama
pullPolicy: IfNotPresent
tag: "latest"
# Ollama parameters
ollama:
models:
pull:
- TinyLlama
- llama3.1:8b
# Configure Service
service:
# -- Service type
type: LoadBalancer
# -- Service port
port: 11434
nodeSelector:
kubernetes.io/arch: amd64
+11 -16
View File
@@ -1,27 +1,18 @@
claimToken: "claim-Ku2YYmJzDB1mpyG6YD7x"
claimToken: "claim-E_NxQDtUMMVsLCBFvybK"
image:
repository: linuxserver/plex
tag: 1.41.3
tag: "1.43.2.10687-563d026ea-ls307"
pullPolicy: Always
kubePlex:
enabled: false # kubePlex (transcoder job) is disabled because not available on ARM. The transcoding will be performed by the main Plex instance instead of a separate Job.
timezone: Europe/Amsterdam
service:
type: LoadBalancer # We will use a LoadBalancer to obtain a virtual IP that can be exposed to Plex Media via our router
port: 32400 # Port to expose Plex
rbac:
create: true
nodeSelector:
beta.kubernetes.io/arch: arm64
kubernetes.io/arch: amd64
persistence:
transcode:
claimName: "plex-transcode"
@@ -29,15 +20,19 @@ persistence:
claimName: "plex-data"
config:
claimName: "plex-config"
# GPU support for hardware-accelerated transcoding
gpu:
enabled: true
hostPath: "/dev/dri"
mountPath: "/dev/dri"
resources:
requests:
memory: "2Gi"
cpu: "3"
cpu: "1"
ephemeral-storage: "50Mi"
limits:
memory: "6Gi"
cpu: "4"
memory: "10Gi"
cpu: "3"
ephemeral-storage: "1Gi"
podAnnotations: {}
proxy:
@@ -1,6 +1,6 @@
image:
repository: lscr.io/linuxserver/prowlarr
tag: 1.23.1
tag: "latest"
pullPolicy: Always
env:
@@ -37,4 +37,3 @@ nodeSelector: {}
tolerations: []
affinity: {}
+140
View File
@@ -0,0 +1,140 @@
---
replicaCount: 1
qbittorrent:
image:
repository: lscr.io/linuxserver/qbittorrent
tag: "5.2.1_v2.0.12-ls459"
pullPolicy: Always
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: "Europe/Amsterdam"
- name: WEBUI_PORT
value: "8080"
- name: TORRENTING_PORT
value: "54408"
torrentPort: 54408
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 10
periodSeconds: 20
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
memory: "500Mi"
cpu: "500m"
ephemeral-storage: "50Mi"
limits:
memory: "2Gi"
cpu: "2"
ephemeral-storage: "1Gi"
volumeMounts:
- name: plex-data
mountPath: "/config"
subPath: "configs/qbittorrent"
- name: plex-data
mountPath: "/nfs/incomplete_torrents"
subPath: "incomplete_torrents"
- name: plex-data
mountPath: "/nfs/torrent"
subPath: "torrent"
gluetun:
image:
repository: qmcgaw/gluetun
tag: v3.41.1
pullPolicy: IfNotPresent
env:
- name: VPN_SERVICE_PROVIDER
value: "airvpn"
- name: VPN_TYPE
value: "wireguard"
- name: WIREGUARD_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: gluetun-wireguard
key: WIREGUARD_PRIVATE_KEY
- name: WIREGUARD_PRESHARED_KEY
valueFrom:
secretKeyRef:
name: gluetun-wireguard
key: WIREGUARD_PRESHARED_KEY
- name: WIREGUARD_ADDRESSES
value: "10.160.17.207/32,fd7d:76ee:e68f:a993:61d7:a5fe:f834:90e1/128"
- name: SERVER_COUNTRIES
value: "Netherlands"
# AirVPN blocks outbound DNS-over-TLS (tcp/853), so gluetun's default
# DoT resolver never gets answers and the startup healthcheck loops
# forever on "lookup cloudflare.com: i/o timeout". Use AirVPN's pushed
# plaintext resolver instead (reached over the tunnel, no DNS leak).
- name: DOT
value: "off"
- name: DNS_ADDRESS
value: "10.128.0.1"
- name: FIREWALL_INPUT_PORTS
value: "8080"
- name: FIREWALL_VPN_INPUT_PORTS
value: "54408"
- name: TZ
value: "Europe/Amsterdam"
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_ADMIN
livenessProbe:
tcpSocket:
port: 8000
initialDelaySeconds: 10
periodSeconds: 20
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
memory: 128Mi
cpu: 100m
limits:
memory: 512Mi
cpu: 500m
volumeMounts:
- name: dev-tun
mountPath: "/dev/net/tun"
service:
type: ClusterIP
port: 8080
volumes:
- name: plex-data
persistentVolumeClaim:
claimName: plex-data
- name: dev-tun
hostPath:
path: /dev/net/tun
nodeSelector:
kubernetes.io/arch: arm64
tolerations: []
affinity: {}
@@ -3,7 +3,7 @@ replicaCount: 1
image:
repository: ghcr.io/linuxserver/radarr
tag: 5.10.4
tag: "latest"
pullPolicy: Always
env:
@@ -3,7 +3,7 @@ replicaCount: 1
image:
repository: ghcr.io/linuxserver/sonarr
tag: 4.0.9
tag: "latest"
pullPolicy: Always
env:
@@ -51,9 +51,10 @@ volumeMounts:
mountPath: "/dev/net/tun" # Needed for VPN
- name: "plex-data"
mountPath: "/etc/openvpn/custom/"
subPath: "airvpn" # Path /mnt/ssd/media/downloads/transmission where transmission downloads Torrents
subPath: "airvpn"
securityContext:
capabilities: # Needed for VPN
add:
- NET_ADMIN
privileged: true
nodeSelector:
kubernetes.io/arch: arm64
Submodule kube-plex deleted from 74c7ede426

Some files were not shown because too many files have changed in this diff Show More