Kubernetes and k3s
Deploy one Tidefetch pod with persistent storage, a secret-backed password and a ClusterIP service, using either the Helm chart or the raw manifests in packaging/kubernetes/. Both run the pod as UID 1000 without privileges and use a Recreate strategy, because one aria2 session volume must never be written by two pods.
Prerequisites
- Kubernetes 1.27 or newer, including k3s
- A default StorageClass, or explicit classes for the two claims
kubectl, and Helm 3.8 or newer for the chart
Helm chart
The chart is published as an OCI artifact:
helm install tidefetch oci://ghcr.io/thre4dripper/charts/tidefetch \
--namespace tidefetch --create-namespace \
--set auth.password='replace-this-password' \
--set persistence.downloads.size=200Gi
Key values:
| Value | Default | Meaning |
|---|---|---|
image.tag |
the chart's appVersion |
Image tag to run |
auth.enabled |
true |
Require a password; false adds -no-auth |
auth.password |
none | Password, stored in a generated Secret |
auth.existingSecret, auth.secretKey |
none, password |
Use your own Secret instead |
persistence.config.size |
2Gi |
Config claim |
persistence.downloads.size |
100Gi |
Downloads claim; or set persistence.downloads.existingClaim |
persistence.*.storageClass |
cluster default | Storage class per claim |
bittorrent.enabled |
false |
Add a LoadBalancer service for port 6881 TCP and UDP |
ingress.enabled, ingress.host, ingress.className |
false, tidefetch.example.com, nginx |
Ingress; TLS secret via ingress.tls.secretName |
resources |
25m CPU and 64Mi requested, 512Mi limit | Pod resources |
timezone |
Etc/UTC |
TZ inside the container |
Upgrade and remove:
helm upgrade tidefetch oci://ghcr.io/thre4dripper/charts/tidefetch -n tidefetch --reuse-values
helm uninstall tidefetch -n tidefetch # the claims are kept
Raw manifests
packaging/kubernetes/ holds a namespace, two claims, the Deployment and a Service, applied with Kustomize.
Storage
Defaults are a 2 GiB config claim and a 100 GiB downloads claim, both ReadWriteOnce. Edit storage.yaml before applying, for example to choose a class:
spec:
storageClassName: longhorn
On k3s the default local-path class ties the data to one node. Use Longhorn, NFS CSI or another replicated class if a node failure must not strand the queue.
Password
kubectl apply -f packaging/kubernetes/namespace.yaml
umask 077
openssl rand -base64 36 > web-password
kubectl -n tidefetch create secret generic tidefetch-web-password \
--from-file=password=./web-password
cat web-password # keep it in a password manager
rm web-password
Deploy
kubectl apply -k packaging/kubernetes
kubectl -n tidefetch rollout status deployment/tidefetch --timeout=180s
kubectl -n tidefetch get pods,pvc,service
Test without an Ingress:
kubectl -n tidefetch port-forward service/tidefetch 8210:8210
Then open http://127.0.0.1:8210.
Ingress and TLS
Copy packaging/kubernetes/ingress.example.yaml, set the hostname and issuer, and apply it. Tidefetch must be served at the root of the hostname, not under a path. Current ingress controllers pass its WebSocket through without extra annotations. See Reverse proxy and TLS.
BitTorrent peer ports
The base Service exposes only the web UI. For inbound peers, add a LoadBalancer Service, or set bittorrent.enabled=true in the chart:
apiVersion: v1
kind: Service
metadata:
name: tidefetch-bittorrent
namespace: tidefetch
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
app.kubernetes.io/name: tidefetch
ports:
- name: bittorrent-tcp
port: 6881
targetPort: bittorrent-tcp
protocol: TCP
- name: bittorrent-udp
port: 6881
targetPort: bittorrent-udp
protocol: UDP
MetalLB is the usual LoadBalancer on bare metal. Forward TCP and UDP 6881 from your router to the assigned address. Never expose aria2's RPC port 6800.
Upgrade
Pin a release tag rather than latest, then apply and watch the Recreate rollout:
image: ghcr.io/thre4dripper/tidefetch:0.1.0
kubectl apply -k packaging/kubernetes
kubectl -n tidefetch rollout status deployment/tidefetch
kubectl -n tidefetch logs deployment/tidefetch --tail=100
The brief downtime is intentional: two aria2 processes must not write the same session volume.
Rotate the password
umask 077
openssl rand -base64 36 > web-password
kubectl -n tidefetch create secret generic tidefetch-web-password \
--from-file=password=./web-password \
--dry-run=client -o yaml | kubectl apply -f -
rm web-password
kubectl -n tidefetch rollout restart deployment/tidefetch
Browser sessions end when the pod restarts.
Backups
Prefer CSI VolumeSnapshots. Otherwise scale to zero, copy the config claim, and scale back up:
kubectl -n tidefetch scale deployment/tidefetch --replicas=0
kubectl -n tidefetch wait --for=delete pod -l app.kubernetes.io/name=tidefetch --timeout=120s
# snapshot or copy the tidefetch-config claim here
kubectl -n tidefetch scale deployment/tidefetch --replicas=1
The config claim is the irreplaceable one; the downloads claim follows your normal retention policy.
Troubleshooting
kubectl -n tidefetch describe pod -l app.kubernetes.io/name=tidefetch
kubectl -n tidefetch logs deployment/tidefetch --tail=200
kubectl -n tidefetch get events --sort-by=.lastTimestamp
kubectl -n tidefetch get pvc
The usual causes are an unbound claim, image pull credentials, a missing password Secret, or a storage backend that cannot apply fsGroup 1000.
Uninstall
kubectl delete -k packaging/kubernetes
kubectl -n tidefetch delete secret tidefetch-web-password
Claims may survive depending on your storage policy. Back them up before deleting the namespace.