Compare commits

...

86 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
129 changed files with 2476 additions and 1113 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.*
-1
View File
@@ -1 +0,0 @@
CLAUDE.md
+27 -368
View File
@@ -1,380 +1,39 @@
# CLAUDE.md
# turingpi
This file provides guidance to LLM agents like Claude, codex or cursor-cli when working with code in this repository.
Home k3s cluster on a Turing Pi (arm64 nodes), GitOps-managed by ArgoCD.
## Repository Overview
## Deploying changes
This repository contains Kubernetes configuration files for a K3s homelab cluster running on TuringPi hardware. It includes Helm charts, values files, and manifests for deploying various self-hosted applications.
- 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>/`.
## Cluster Architecture
## Gotchas
### Hardware Setup
- **turing1**: Control plane + worker (master node, IP: 192.168.222.237)
- **turing2**: Worker node (currently SchedulingDisabled)
- **turing3**: Worker node (also serves as NFS server at turing3.lan)
- **turing4**: Worker node
- **beelink**: Additional worker node
### qbittorrent + gluetun (AirVPN) — DNS / restart loop
### Core Infrastructure
- **K3s version**: v1.31.6+k3s1
- **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 (staging/production cluster issuers)
- **Ingress**: Traefik (K3s default) with LAN-only restrictions
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.
## Application Stack
### Media Services
- **Plex**: kube-plex (Kubernetes-native with dynamic transcoding pods)
- **Jellyfin**: Alternative media server
- **Sonarr/Radarr**: TV/Movie management (Bananaspliff charts)
- **Prowlarr**: Indexer management (custom chart)
- **Transmission**: BitTorrent client with OpenVPN
- **FlareSolverr**: Captcha solver service
### Other Applications
- **Actual Budget**: Personal finance (custom chart: my-actual-server/)
- **Home Assistant Voice LLMs**: AI voice integration (custom chart)
- **Ollama**: Local LLM inference
- **Prometheus**: Monitoring stack
- **PostgreSQL**: Database backend
## Common Helm Operations
### Repository Management
```bash
# Key repositories used
helm repo add metallb https://metallb.github.io/metallb
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner
helm repo add jetstack https://charts.jetstack.io
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add bananaspliff https://bananaspliff.github.io/geek-charts
helm repo add k8s-at-home https://k8s-at-home.com/charts/
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add jellyfin https://jellyfin.github.io/jellyfin-helm
helm repo add ollama-helm https://otwld.github.io/ollama-helm/
helm repo update
```
### Application Deployment Pattern
```bash
# Standard deployment with values file
helm upgrade <release-name> <chart> -f <app>_values.yaml -i
# Examples from history:
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
helm upgrade sonarr bananaspliff/sonarr -f sonarr_values.yaml
helm upgrade prowlarr prowlarr -f prowlarr_values.yml
```
### Development Workflow
```bash
# Chart development
helm create <chart-name>
helm lint <chart-path>
helm template <chart> -f <values> | vim -
# Values inspection
helm show values <chart> > <app>_values.yaml
helm get values <release-name>
helm get manifest <release-name>
```
## File Structure Patterns
- `<app>_values.yaml` - Helm values overrides for each application
- Custom charts in subdirectories (my-actual-server/, home-assistant-voice-llms/, prowlarr/)
- `*_persistent_volume.yml` - PV definitions for applications requiring storage
- Infrastructure manifests: metallb.yml, ingress.yaml, cluster-issuer-*.yaml
## Storage Configuration
- **NFS Server**: turing3.lan serving /mnt/ssd
- **StorageClass**: nfs-client (via nfs-subdir-external-provisioner)
- **Access Mode**: ReadWriteMany for shared media access
- **PVC Pattern**: Applications create their own PVCs or reference pre-existing ones
## Network Setup
- **Pod Network**: Cluster subnet requires allowlisting in Plex for transcoding
- **Ingress**: LAN-only access enforced via limit_ingress_to_lan.yaml
- **Load Balancer**: MetalLB provides external IPs for services
- **DNS**: .lan domain for internal services
## Kube-Plex Specifics
The kube-plex/ directory contains a Go application that replaces the standard Plex transcoder:
- Creates Kubernetes pods for each transcode job
- Requires AMD64 nodes (configured via nodeSelector)
- Mounts shared NFS volumes for media access
- Environment variables: DATA_PVC, CONFIG_PVC, TRANSCODE_PVC, PMS_IMAGE, PMS_INTERNAL_ADDRESS
## ArgoCD Migration Guidelines
### Agent Workflow Defaults
When migrating or adding an ArgoCD Application, always:
- Create/update the Application manifest and any `helm-values/` files.
- Git add, commit, and push the changes to the tracked branch.
- Use the ArgoCD CLI to sync the app and wait for healthy status.
### Critical Configuration Preservation Rules
When migrating applications from direct Helm deployment to ArgoCD GitOps:
1. **NEVER change working configurations** - If an application was working with specific values files and service configurations, maintain those exact settings
2. **NO PATCHES OR WORKAROUNDS** - Do not create additional PVCs, port forwards, or other patches to "fix" configuration mismatches
3. **Root Cause Analysis Required** - Always identify why a configuration that worked before is now failing after ArgoCD migration
4. **Preserve Original Service Ports** - Applications should maintain their original service port configurations (e.g., Radarr:7878, Sonarr:8989)
5. **Respect Existing PVC Structure** - Use the existing PVC and storage configuration patterns, don't create compatibility layers
### Troubleshooting Process
1. Compare working Helm values with ArgoCD application configuration
2. Verify that Helm chart sources and versions match original deployments
3. Ensure service definitions in ArgoCD match original working services
4. Check that ingress configurations align with actual service ports
5. Fix the root configuration issue, don't patch around it
### Common Migration Pitfalls
- Creating unnecessary "myvolume" PVCs for chart compatibility - instead fix the chart values
- Changing service ports to match ingress instead of fixing ingress to match services
- Using generic chart defaults instead of preserving working custom configurations
## ArgoCD GitOps Implementation Lessons
### Successfully Migrated Applications
- **Plex**: Multi-source setup (kube-plex chart + turingpi values)
- **Radarr**: Bananaspliff chart with custom volume configuration
- **Sonarr**: Bananaspliff chart with custom volume configuration
### Critical ArgoCD Configuration Patterns
#### Multi-Source Application Structure
For applications requiring custom values from Git repository:
```yaml
spec:
project: default
sources:
- repoURL: https://chart-repository.com/charts
chart: app-name
targetRevision: "*"
ref: charts
helm:
releaseName: app-name
valueFiles:
- $values/helm-values/app_values.yaml
- repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
ref: values
```
**CRITICAL**: Never use both `source:` and `sources:` sections - this creates conflicts where `source:` overrides `sources:` configuration.
#### Image Auto-Update Configuration
For applications using "latest" tags with automatic updates:
```yaml
metadata:
annotations:
argocd-image-updater.argoproj.io/image-list: app=registry/image:latest
argocd-image-updater.argoproj.io/app.update-strategy: digest
argocd-image-updater.argoproj.io/write-back-method: git
argocd-image-updater.argoproj.io/git-branch: master
argocd-image-updater.argoproj.io/git-commit-user: argocd-image-updater
argocd-image-updater.argoproj.io/git-commit-email: argocd@turing.lan
```
### Chart-Specific Configuration Requirements
#### Bananaspliff Charts (Radarr/Sonarr)
These charts require specific volume configuration syntax:
```yaml
# CORRECT: Direct volumes/volumeMounts (working configuration)
volumes:
- name: "plex-data"
persistentVolumeClaim:
claimName: "plex-data"
volumeMounts:
- name: "plex-data"
mountPath: "/config"
subPath: "configs/app-name"
- name: "plex-data"
mountPath: "/nfs"
# INCORRECT: persistence syntax (doesn't work with these charts)
persistence:
config:
enabled: true
existingClaim: "plex-data"
subPath: "configs/app-name"
```
#### Service Port Configuration
Bananaspliff charts use simple service structure:
```yaml
service:
type: ClusterIP
port: 7878 # Direct port specification
```
### Ingress Controller Configuration
#### K3s Default Setup
- **Default Controller**: Traefik (not Nginx)
- **Ingress Class**: Use `kubernetes.io/ingress.class: traefik`
- **Common Mistake**: Using `nginx` class when Traefik is the active controller
#### Verification Commands
```bash
# Check active ingress controller
kubectl get pods -A | grep traefik
kubectl get pods -A | grep ingress
# Verify ingress class in configurations
kubectl get ingress -o yaml | grep "ingress.class"
```
### Pre-Migration Checklist
Before migrating any application to ArgoCD:
1. **Capture Current Configuration**:
```bash
helm get values <release-name> > original_values.yaml
helm get manifest <release-name> > original_manifest.yaml
```
2. **Document Volume Mounts**:
```bash
kubectl describe deployment <app-name> | grep -A 10 "Mounts:"
```
3. **Verify Service Configuration**:
```bash
kubectl get svc <app-name> -o yaml
```
4. **Test Chart Template Locally**:
```bash
helm template <app-name> <chart> --values <values-file> | grep -A 20 "kind: Service"
```
### Commit & Push + ArgoCD CLI Checks
After adding a new ArgoCD Application and values files:
1. Commit and push the changes:
```bash
git add applications/<app>.yaml helm-values/<app>_values.*
git commit -m "feat(argocd): add <app> application"
git push # or: git push gitea master
```
2. ArgoCD CLI login (if not already authenticated):
```bash
# Example options; adjust for your environment
# Get admin password if needed
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d; echo
# Login (use your server/credentials)
argocd login argocd-server.argocd.svc.cluster.local --username admin --password <password> --insecure
```
3. Sync and wait for health:
```bash
argocd app sync <app>
argocd app wait <app> --sync --health --timeout 300
argocd app get <app>
argocd app list | grep <app>
```
4. If sync fails, inspect details and events:
```bash
argocd app history <app> --refresh
argocd app get <app> --refresh
kubectl -n default describe deploy/<app> || true
kubectl -n default get events --sort-by=.lastTimestamp | tail -n 50
```
### Post-Migration Verification
1. **Check Volume Mounts Match Original**:
```bash
kubectl describe pod -l app=<app-name> | grep -A 10 "Mounts:"
```
2. **Verify Service Ports**:
```bash
kubectl get svc <app-name>
```
3. **Test Application Access**:
```bash
curl -I http://<app-domain>
```
4. **Confirm Configuration Persistence**:
```bash
kubectl exec -it deployment/<app-name> -- ls -la /config
```
### ArgoCD Image-Updater Multi-Source Application Issues
#### Problem: Credential Errors with Multi-Source Applications
When using ArgoCD image-updater with multi-source applications (chart from external repo + values from Git), the image-updater may fail with credential errors like:
```
Could not update application spec: could not get creds for repo 'https://chart-repository.com': credentials for 'https://chart-repository.com' are not configured in Argo CD settings
```
#### Root Cause Analysis
1. **Multi-Source Confusion**: Image-updater tries to write back changes to the chart repository instead of the values repository
2. **Git Write-Back Limitations**: The `git` write-back method doesn't handle multi-source applications properly
3. **Repository Credentials**: External chart repositories (like Bananaspliff) don't have write credentials configured
#### Solution: Use ArgoCD API Write-Back Method
Instead of using `git` write-back method, use the `argocd` API method for multi-source applications:
The gluetun sidecar in `helm-values/qbittorrent_values.yaml` **must** keep:
```yaml
metadata:
annotations:
argocd-image-updater.argoproj.io/image-list: app=registry/image:latest
argocd-image-updater.argoproj.io/app.update-strategy: digest
argocd-image-updater.argoproj.io/write-back-method: argocd # Use ArgoCD API instead of git
argocd-image-updater.argoproj.io/write-back-target: http://git-repo.local/values.git # Optional: specify target repo
- name: DOT
value: "off"
- name: DNS_ADDRESS
value: "10.128.0.1" # AirVPN's pushed resolver, reached over the tunnel — no DNS leak
```
#### Implementation Steps
1. **Update Image-Updater Configuration**:
```bash
kubectl patch configmap argocd-image-updater-config -n argocd --patch '{"data":{"git.user":"argocd-image-updater","git.email":"argocd@turing.lan"}}'
```
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).
2. **Change Application Write-Back Method**:
```bash
kubectl patch application <app-name> -n argocd --type='merge' --patch='{"metadata":{"annotations":{"argocd-image-updater.argoproj.io/write-back-method":"argocd"}}}'
```
3. **Restart Image-Updater**:
```bash
kubectl rollout restart deployment argocd-image-updater -n argocd
```
#### Verification Commands
```bash
# Check image-updater logs for success
kubectl logs -n argocd deployment/argocd-image-updater --tail=20
# Look for these success indicators:
# - "Successfully updated the live application spec"
# - "Processing results: applications=X images_considered=X images_skipped=0 images_updated=X errors=0"
# Verify applications remain healthy
argocd app list
# Check that pods are updated with new images
kubectl get pods -l app=<app-name>
```
#### Key Learnings
- **ArgoCD API Method**: Works better than Git write-back for multi-source applications
- **No Repository Credentials Needed**: ArgoCD API method doesn't require external repository write credentials
- **Application Spec Updates**: Changes are applied directly to ArgoCD application specs, not Git files
- **Multi-Source Compatibility**: This approach handles complex application configurations properly
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.
+16 -8
View File
@@ -38,13 +38,12 @@ This repository contains Kubernetes configuration files for a K3s cluster runnin
## 📁 Repository Structure
```
├── *_values.yaml # Helm values overrides for applications
├── my-actual-server/ # Custom Helm chart for Actual Budget
├── home-assistant-voice-llms/ # Custom Helm chart for Voice AI
├── prowlarr/ # Custom Helm chart for Prowlarr
├── kube-plex/ # Kubernetes-native Plex implementation
── *.yml # Infrastructure manifests (MetalLB, ingress, etc.)
└── persistent_volume*.yml # Storage definitions
├── 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
@@ -79,6 +78,15 @@ Run the provided script to update all nodes:
./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)
@@ -141,4 +149,4 @@ 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
- 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).
+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
-35
View File
@@ -1,35 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: flaresolverr
namespace: argocd
annotations:
argocd-image-updater.argoproj.io/image-list: flaresolverr=ghcr.io/flaresolverr/flaresolverr:latest
argocd-image-updater.argoproj.io/flaresolverr.update-strategy: digest
argocd-image-updater.argoproj.io/write-back-method: argocd
argocd-image-updater.argoproj.io/flaresolverr.helm.image-name: image.repository
argocd-image-updater.argoproj.io/flaresolverr.helm.image-tag: image.tag
spec:
project: default
sources:
- repoURL: https://k8s-at-home.com/charts/
chart: flaresolverr
targetRevision: "*"
ref: charts
helm:
releaseName: flaresolverr
valueFiles:
- $values/helm-values/flaresolverr_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
+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
+8 -20
View File
@@ -4,28 +4,16 @@ metadata:
name: lidarr
namespace: argocd
annotations:
# Enable automatic image updates for Lidarr
argocd-image-updater.argoproj.io/image-list: lidarr=ghcr.io/linuxserver/lidarr:latest
# Use digest strategy to track latest tag updates
argocd-image-updater.argoproj.io/lidarr.update-strategy: digest
# Write changes back via ArgoCD API (multi-source friendly)
argocd-image-updater.argoproj.io/write-back-method: argocd
argocd-image-updater.argoproj.io/lidarr.helm.image-name: image.repository
argocd-image-updater.argoproj.io/lidarr.helm.image-tag: image.tag
spec:
project: default
sources:
- repoURL: https://k8s-at-home.com/charts/
chart: lidarr
targetRevision: "*"
ref: charts
helm:
releaseName: lidarr
valueFiles:
- $values/helm-values/lidarr_values.yaml
- repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
ref: values
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
+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
-5
View File
@@ -4,11 +4,6 @@ metadata:
name: ollama
namespace: argocd
annotations:
argocd-image-updater.argoproj.io/image-list: ollama=ollama/ollama:latest
argocd-image-updater.argoproj.io/ollama.update-strategy: digest
argocd-image-updater.argoproj.io/write-back-method: argocd
argocd-image-updater.argoproj.io/ollama.helm.image-name: image.repository
argocd-image-updater.argoproj.io/ollama.helm.image-tag: image.tag
spec:
project: default
sources:
+5 -13
View File
@@ -1,30 +1,22 @@
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: plex
namespace: argocd
annotations:
# Enable automatic image updates for Plex
argocd-image-updater.argoproj.io/image-list: plex=linuxserver/plex:latest
# Use digest strategy to track latest tag updates
argocd-image-updater.argoproj.io/plex.update-strategy: digest
# Write changes back via ArgoCD API (multi-source friendly)
argocd-image-updater.argoproj.io/write-back-method: argocd
argocd-image-updater.argoproj.io/plex.helm.image-name: image.repository
argocd-image-updater.argoproj.io/plex.helm.image-tag: image.tag
spec:
project: default
sources:
- repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/kube-plex.git
path: charts/kube-plex
targetRevision: HEAD
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: HEAD
targetRevision: master
ref: values
destination:
server: https://kubernetes.default.svc
@@ -34,5 +26,5 @@ spec:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
- CreateNamespace=true
- ServerSideApply=true
+2 -7
View File
@@ -4,21 +4,16 @@ metadata:
name: prowlarr
namespace: argocd
annotations:
argocd-image-updater.argoproj.io/image-list: prowlarr=lscr.io/linuxserver/prowlarr:latest
argocd-image-updater.argoproj.io/prowlarr.update-strategy: digest
argocd-image-updater.argoproj.io/write-back-method: argocd
argocd-image-updater.argoproj.io/prowlarr.helm.image-name: image.repository
argocd-image-updater.argoproj.io/prowlarr.helm.image-tag: image.tag
spec:
project: default
source:
repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git
targetRevision: HEAD
path: prowlarr
path: custom_helm_charts/prowlarr
helm:
releaseName: prowlarr
valueFiles:
- ../helm-values/prowlarr_values.yml
- ../../helm-values/prowlarr_values.yml
destination:
server: https://kubernetes.default.svc
namespace: default
+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
-5
View File
@@ -5,13 +5,8 @@ metadata:
namespace: argocd
annotations:
# Enable automatic image updates for Radarr
argocd-image-updater.argoproj.io/image-list: radarr=ghcr.io/linuxserver/radarr:latest
# Use digest strategy to track latest tag updates
argocd-image-updater.argoproj.io/radarr.update-strategy: digest
# Write changes back via ArgoCD API (multi-source friendly)
argocd-image-updater.argoproj.io/write-back-method: argocd
argocd-image-updater.argoproj.io/radarr.helm.image-name: image.repository
argocd-image-updater.argoproj.io/radarr.helm.image-tag: image.tag
spec:
project: default
sources:
-5
View File
@@ -5,13 +5,8 @@ metadata:
namespace: argocd
annotations:
# Enable automatic image updates for Sonarr
argocd-image-updater.argoproj.io/image-list: sonarr=ghcr.io/linuxserver/sonarr:latest
# Use digest strategy to track latest tag updates
argocd-image-updater.argoproj.io/sonarr.update-strategy: digest
# Write changes back via ArgoCD API (multi-source friendly)
argocd-image-updater.argoproj.io/write-back-method: argocd
argocd-image-updater.argoproj.io/sonarr.helm.image-name: image.repository
argocd-image-updater.argoproj.io/sonarr.helm.image-tag: image.tag
spec:
project: default
sources:
-5
View File
@@ -4,11 +4,6 @@ metadata:
name: transmission
namespace: argocd
annotations:
argocd-image-updater.argoproj.io/image-list: transmission=haugene/transmission-openvpn:latest
argocd-image-updater.argoproj.io/transmission.update-strategy: digest
argocd-image-updater.argoproj.io/write-back-method: argocd
argocd-image-updater.argoproj.io/transmission.helm.image-name: image.repository
argocd-image-updater.argoproj.io/transmission.helm.image-tag: image.tag
spec:
project: default
sources:
-40
View File
@@ -1,40 +0,0 @@
# Example ArgoCD Application with Image Auto-Update
# This demonstrates how to set up your existing Helm applications in ArgoCD
# with automatic "latest" tag updates
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: plex-example
namespace: argocd
annotations:
# Enable automatic image updates for Plex
argocd-image-updater.argoproj.io/image-list: plex=ghcr.io/k8s-at-home/plex:latest
# Use 'newest-build' strategy for latest images
argocd-image-updater.argoproj.io/plex.update-strategy: newest-build
# Write back to ArgoCD (for testing - production should use git method)
argocd-image-updater.argoproj.io/write-back-method: argocd
spec:
project: default
source:
# Point to your repository (replace with your actual Git repo)
repoURL: https://github.com/munnerz/kube-plex
path: charts/kube-plex
targetRevision: HEAD
helm:
valueFiles:
# This would reference your existing plex_values.yml
# For now, this is just an example structure
- values.yaml
parameters:
- name: image.tag
value: latest
destination:
server: https://kubernetes.default.svc
namespace: plex
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
-17
View File
@@ -1,17 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-image-updater-config
namespace: argocd
labels:
app.kubernetes.io/name: argocd-image-updater-config
app.kubernetes.io/part-of: argocd-image-updater
data:
# Force image-updater to use the ArgoCD API for write-back to avoid multi-source git issues.
write-back-method: argocd
# Keep git identity in case git mode is explicitly used for single-source apps later.
git.user: argocd-image-updater
git.email: argocd@turing.lan
argocd.server_addr: argocd-server.argocd.svc.cluster.local
argocd.plaintext: "true"
argocd.insecure: "true"
-114
View File
@@ -1,114 +0,0 @@
# ArgoCD Migration Guide for TuringPi Cluster
## ArgoCD Access Information
**Web UI Access:**
- URL: http://192.168.222.25 (LoadBalancer IP)
- Alternative: http://argocd.turing.lan (if you add to your hosts file)
- Username: `admin`
- Password: `fJ3diddVd2yson3W`
## Migration Strategy
Your existing Helm-based applications can be migrated to ArgoCD gradually. Here's how:
### Option 1: Keep Existing Helm + Add GitOps Overlay
1. Keep your current `*_values.yaml` files
2. Create ArgoCD Applications that reference the same charts
3. ArgoCD manages the lifecycle, you keep the familiar structure
### Option 2: Git-First Approach (Recommended for Production)
1. Commit your values files to a Git repository
2. Use ArgoCD's Git source with `argocd-image-updater` writing back to Git
3. Full GitOps workflow with audit trail
## Adding Image Auto-Updates to Your Applications
For any application, add these annotations to the ArgoCD Application manifest:
```yaml
metadata:
annotations:
# Define which images to track
argocd-image-updater.argoproj.io/image-list: myapp=myregistry/myapp:latest
# Use newest-build strategy for "latest" tags
argocd-image-updater.argoproj.io/myapp.update-strategy: newest-build
# Write method: 'argocd' for testing, 'git' for production
argocd-image-updater.argoproj.io/write-back-method: argocd
```
## Example: Converting Your Plex Deployment
Your current command:
```bash
helm upgrade plex kube-plex/charts/kube-plex --values plex_values.yml
```
Becomes this ArgoCD Application:
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: plex
namespace: argocd
annotations:
argocd-image-updater.argoproj.io/image-list: plex=ghcr.io/k8s-at-home/plex:latest
argocd-image-updater.argoproj.io/plex.update-strategy: newest-build
argocd-image-updater.argoproj.io/write-back-method: argocd
spec:
project: default
source:
repoURL: https://github.com/munnerz/kube-plex # or your fork
path: charts/kube-plex
targetRevision: HEAD
helm:
valueFiles:
- ../../plex_values.yml # Reference your existing values
destination:
server: https://kubernetes.default.svc
namespace: plex
syncPolicy:
automated:
prune: true
selfHeal: true
```
## Quick Start Commands
1. **Access ArgoCD UI**: Visit http://192.168.222.25 with admin/fJ3diddVd2yson3W
2. **Create your first application via CLI**:
```bash
# Install ArgoCD CLI (optional)
curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
# Login (using the LoadBalancer IP)
argocd login 192.168.222.25 --insecure --username admin --password fJ3diddVd2yson3W
```
3. **Apply the example application**:
```bash
kubectl apply -f argocd-example-app.yaml
```
## Benefits You Get Immediately
**Keep using Helm** - ArgoCD manages Helm releases
**Auto image updates** - Latest tags update automatically
**Visual UI** - See deployment status, sync state, rollback easily
**GitOps ready** - When you want to commit values to Git
**Rollback capability** - Easy rollback to previous versions
**Multi-environment** - Can manage dev/staging/prod from one place
## Next Steps
1. Access the ArgoCD UI and familiarize yourself with it
2. Create ArgoCD Applications for 1-2 of your existing services
3. Test the image auto-update functionality
4. Once comfortable, migrate more applications
5. Consider setting up a Git repository for full GitOps workflow
Your existing Helm workflow continues to work while you gain GitOps benefits!
+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"
+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: {}
+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: {}
+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: {}
-78
View File
@@ -1,78 +0,0 @@
# TuringPi GitOps Deployment Summary
## ✅ Successfully Deployed
### ArgoCD GitOps Platform
- **URL**: http://192.168.222.25
- **Username**: admin
- **Password**: fJ3diddVd2yson3W
- **Features**: GitOps CD, Image auto-updates, Helm support
### Gitea Self-Hosted Git Server
- **URL**: http://192.168.222.27:3000
- **Username**: admin
- **Password**: gitea-admin-pass
- **SSH**: git@192.168.222.26
- **Features**: PostgreSQL backend, NFS storage, SSH access
## 🚀 Repository Status
**Local Repository**: `/home/gilgamezh/code/turingpi`
**Gitea Repository**: http://192.168.222.27:3000/admin/turingpi
**SSH Clone URL**: `git@192.168.222.26:admin/turingpi.git`
### Latest Commit
```
45dfbfc Add ArgoCD and Gitea for GitOps workflow implementation
```
**Includes:**
- ArgoCD configuration (`argocd_values.yaml`)
- Gitea configuration (`gitea_values.yaml`)
- Example ArgoCD Application with auto-updates
- Migration guides and documentation
- All existing Helm configurations
## 🔧 What's Working
**ArgoCD Web UI** - Access at http://192.168.222.25
**Gitea Web UI** - Access at http://192.168.222.27:3000
**Repository Push** - Code successfully pushed to Gitea
**Image Auto-Updates** - ArgoCD Image Updater configured for "latest" tags
**LAN Security** - IP whitelisting enforced on both services
**NFS Storage** - Persistent data on your existing NFS setup
**LoadBalancer** - MetalLB providing external IPs
## 📋 Next Steps
1. **Access ArgoCD UI** and explore the interface
2. **Create first ArgoCD Application** pointing to your Gitea repo
3. **Test GitOps workflow**:
```bash
# Make a change to values file
git add changed-file.yaml
git commit -m "Update application config"
git push gitea master
# Watch ArgoCD auto-sync the changes
```
4. **Migrate existing applications** from manual Helm to GitOps
5. **Set up SSH key properly** for passwordless Git operations
## 🔐 SSH Setup Note
Your SSH key has been added to Gitea, but there may be a key mismatch. To fix:
1. Check which SSH key is being used: `ssh-add -l`
2. Test connection: `ssh -T git@192.168.222.26`
3. If issues persist, regenerate SSH key or use HTTPS for now
## 🎯 GitOps Benefits Achieved
- **Version Control**: All configs in Git with full history
- **Automated Deployments**: ArgoCD syncs Git changes automatically
- **Image Updates**: Latest container images pulled automatically
- **Rollback Capability**: Easy revert to any previous state
- **Self-Hosted**: No external dependencies, full control
- **Enterprise Features**: On your homelab hardware
Your TuringPi cluster now has production-grade GitOps capabilities! 🎉
-159
View File
@@ -1,159 +0,0 @@
# Gitea + ArgoCD Setup Guide
## Gitea Access Information
**Web UI Access:**
- **LoadBalancer URL**: http://192.168.222.27:3000
- **Ingress URL**: http://gitea.turing.lan (add to your hosts file: `192.168.222.27 gitea.turing.lan`)
- **SSH Clone URL**: `git@192.168.222.26:username/repo.git`
**Admin Credentials:**
- **Username**: `admin`
- **Password**: `gitea-admin-pass`
- **Email**: `admin@turing.lan`
## Initial Gitea Setup
1. **Access Gitea**: Visit http://192.168.222.27:3000
2. **Login**: Use admin credentials above
3. **Create Organization**: Create an org for your homelab projects (e.g., "turingpi")
4. **Create Repository**: Create your first repo for ArgoCD manifests
## Setting Up Your First Repository
### Create a Repository for ArgoCD Applications
1. **Create new repo**: `turingpi-argocd-apps`
2. **Clone locally**:
```bash
git clone http://192.168.222.27:3000/admin/turingpi-argocd-apps.git
cd turingpi-argocd-apps
```
3. **Copy your existing values files**:
```bash
# Copy your existing values files to the repo
cp /home/gilgamezh/code/turingpi/*_values.yaml ./helm-values/
mkdir -p apps/
```
4. **Create directory structure**:
```
turingpi-argocd-apps/
├── apps/ # ArgoCD Application manifests
├── helm-values/ # Your existing *_values.yaml files
├── manifests/ # Raw Kubernetes manifests
└── README.md
```
## Migrating Plex to GitOps
### Step 1: Create ArgoCD Application
Create `apps/plex.yaml`:
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: plex
namespace: argocd
annotations:
# Enable automatic image updates
argocd-image-updater.argoproj.io/image-list: plex=ghcr.io/k8s-at-home/plex:latest
argocd-image-updater.argoproj.io/plex.update-strategy: newest-build
argocd-image-updater.argoproj.io/write-back-method: git
spec:
project: default
source:
repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi-argocd-apps.git
path: helm-values
targetRevision: HEAD
helm:
valueFiles:
- plex_values.yml
destination:
server: https://kubernetes.default.svc
namespace: plex
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
```
### Step 2: Configure ArgoCD to Access Gitea
Add Gitea as a repository in ArgoCD:
1. **Via ArgoCD UI**:
- Go to Settings → Repositories → Connect Repo
- URL: `http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi-argocd-apps.git`
- Username: `admin`
- Password: `gitea-admin-pass`
2. **Via CLI**:
```bash
argocd repo add http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi-argocd-apps.git \
--username admin --password gitea-admin-pass
```
## Benefits of This Setup
**Version Control**: All your configurations are in Git
**Automatic Updates**: Images update when "latest" tags change
**Audit Trail**: See what changed and when
**Easy Rollbacks**: Git history = deployment history
**Local Control**: No external dependencies
**Team Collaboration**: Others can contribute via Git
## Migration Strategy
1. **Start Small**: Migrate 1-2 applications first
2. **Test Process**: Verify auto-updates work as expected
3. **Bulk Migration**: Move remaining applications
4. **Cleanup**: Remove manual Helm commands once confident
## Git Workflow Examples
### Adding a New Application
```bash
# Create new app manifest
cat > apps/new-app.yaml << EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: new-app
namespace: argocd
spec:
# ... configuration
EOF
# Commit and push
git add apps/new-app.yaml
git commit -m "Add new application: new-app"
git push origin main
```
### Updating Values
```bash
# Edit your values file
vim helm-values/plex_values.yml
# Commit changes
git add helm-values/plex_values.yml
git commit -m "Update Plex CPU limits"
git push origin main
# ArgoCD will auto-sync the changes
```
Your homelab now has enterprise-grade GitOps capabilities while staying completely self-hosted! 🏠✨
## Next Steps
1. **Access Gitea** and create your first repository
2. **Copy your values files** to the new repo
3. **Create your first ArgoCD application** pointing to Gitea
4. **Test the workflow** with a simple change
5. **Migrate more applications** gradually
-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: {}
-20
View File
@@ -1,20 +0,0 @@
---
image:
repository: ghcr.io/flaresolverr/flaresolverr
pullPolicy: IfNotPresent
tag: "latest@sha256:f104ee51e5124d83cf3be9b37480649355d223f7d8f9e453d0d5ef06c6e3b31b"
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
+21 -35
View File
@@ -7,32 +7,28 @@ image:
pullPolicy: Always
env:
TZ: "Europe/Amsterdam"
PUID: "1000"
PGID: "1000"
LIDARR__POSTGRES_HOST: pgsql-postgresql
LIDARR__POSTGRES_PORT: "5432"
LIDARR__POSTGRES_USER: arr_user
LIDARR__POSTGRES_PASSWORD: "clavedatabase"
LIDARR__POSTGRES_MAIN_DB: lidarr_db
LIDARR__POSTGRES_LOG_DB: lidarr_db_log
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: "Europe/Amsterdam"
service:
main:
type: ClusterIP
ports:
http:
port: 8686
type: ClusterIP
port: 8686
persistence:
config:
enabled: true
existingClaim: plex-data
subPath: configs/lidarr
media:
enabled: true
existingClaim: plex-data
mountPath: /nfs
volumes:
- name: plex-data
persistentVolumeClaim:
claimName: plex-data
volumeMounts:
- name: plex-data
mountPath: "/config"
subPath: "configs/lidarr"
- name: plex-data
mountPath: "/nfs"
resources:
requests:
@@ -40,16 +36,6 @@ resources:
cpu: "500m"
ephemeral-storage: "50Mi"
limits:
memory: "800Mi"
cpu: "1000m"
memory: "4Gi"
cpu: "4000m"
ephemeral-storage: "1Gi"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- turing2
+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: {}
+1 -1
View File
@@ -2,7 +2,7 @@ replicaCount: 1
image:
repository: ollama/ollama
pullPolicy: IfNotPresent
tag: "latest@sha256:d4188c1dfa870386a14e299976aed96daeb83876b69e1a852c9d09ea76463b9f"
tag: "latest"
# Ollama parameters
+1 -11
View File
@@ -1,26 +1,18 @@
claimToken: "claim-E_NxQDtUMMVsLCBFvybK"
image:
repository: linuxserver/plex
tag: "latest@sha256:28f18c27b6822328df994154dbf7c0f511032d9f91bbd10881030b706afd8593"
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:
kubernetes.io/arch: amd64
persistence:
transcode:
claimName: "plex-transcode"
@@ -28,13 +20,11 @@ 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"

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