Files
turingpi/helm-values/nzbget_values.yaml
T
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

200 lines
5.6 KiB
YAML

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: {}