apiVersion: apps/v1 kind: Deployment metadata: name: qbittorrent namespace: qbittorrent spec: # qBittorrent has one writable configuration volume and one Proton port # forwarding lease, so upgrades replace the Pod rather than overlap two. replicas: 1 strategy: type: Recreate selector: matchLabels: app: qbittorrent template: metadata: labels: app: qbittorrent spec: # The SOPS-managed Secret is read-only, but Hotio writes Proton's current # forwarded port beside wg0.conf. Copy the secret into a writable emptyDir # without ever storing the plaintext WireGuard key in Git. initContainers: - name: prepare-wireguard-config image: busybox:1.37.0 command: - sh - -ec - >- cp /vpn-secret/wg0.conf /wireguard/wg0.conf && chmod 0600 /wireguard/wg0.conf volumeMounts: - name: wireguard-secret mountPath: /vpn-secret readOnly: true - name: wireguard-runtime mountPath: /wireguard containers: - name: qbittorrent image: ghcr.io/hotio/qbittorrent:release imagePullPolicy: Always securityContext: # Hotio needs this capability to create the WireGuard interface, # routes, firewall rules, and VPN kill switch inside the Pod. capabilities: add: - NET_ADMIN env: - name: PUID value: "1000" - name: PGID value: "1000" - name: UMASK value: "002" - name: TZ value: Europe/London - name: WEBUI_PORTS value: 8080/tcp - name: VPN_ENABLED value: "true" - name: VPN_CONF value: wg0 - name: VPN_PROVIDER value: proton # Permit the physical LAN to reach the Web UI/API without allowing # ordinary internet traffic to bypass the VPN. Do not add the # Kubernetes Pod CIDR here: Hotio detects the Pod's attached network # itself and rejects an overlapping VPN_LAN_NETWORK route. - name: VPN_LAN_NETWORK value: 10.10.0.0/16 - name: VPN_LAN_LEAK_ENABLED value: "false" # Proton assigns a dynamic inbound port through NAT-PMP. Hotio # renews it and updates qBittorrent's listening port automatically. - name: VPN_AUTO_PORT_FORWARD value: "true" - name: VPN_HEALTHCHECK_ENABLED value: "true" - name: VPN_NAMESERVERS value: wg ports: - name: http containerPort: 8080 protocol: TCP startupProbe: httpGet: path: / port: http periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 36 readinessProbe: httpGet: path: / port: http periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 livenessProbe: httpGet: path: / port: http periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3 volumeMounts: - name: qbittorrent-config mountPath: /config - name: wireguard-runtime mountPath: /config/wireguard - name: torrents mountPath: /data/torrents volumes: - name: qbittorrent-config persistentVolumeClaim: claimName: qbittorrent-config - name: wireguard-secret secret: secretName: qbittorrent-wireguard - name: wireguard-runtime emptyDir: {} - name: torrents nfs: server: nas.ahmza.com path: /srv/media/torrents