2f24f75752
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>
200 lines
5.6 KiB
YAML
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: {}
|