Compare commits
144 Commits
e86b9d5e49
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f24f75752 | |||
| 49cfd05bee | |||
| 8da60f2ae8 | |||
| 5e1a919721 | |||
| 6247b140bc | |||
| 98c1e7b63d | |||
| 79a28a674a | |||
| d6ee993a60 | |||
| 2c68a21d0b | |||
| 1b3f34a432 | |||
| 261aebfd10 | |||
| 9b24978342 | |||
| 1a91b72464 | |||
| ac637adaf4 | |||
| 6082e6fc14 | |||
| 7e0a38d65f | |||
| 3b480d6abf | |||
| 3ace05a695 | |||
| 290ce6a103 | |||
| e230129119 | |||
| 269ab53002 | |||
| 724568e08f | |||
| 770125e7c8 | |||
| 3239a1e729 | |||
| d476af6cfd | |||
| 930ede9b74 | |||
| a31e50f02f | |||
| aa89a5f238 | |||
| 41e272c9f2 | |||
| 4fe6ef579c | |||
| 8ea4086a37 | |||
| 3827ad656a | |||
| 9ab1659939 | |||
| 4a66fdfabf | |||
| c98f1b93b5 | |||
| 377fa8ec4a | |||
| d009a61c0e | |||
| eeb80c2662 | |||
| 24a2463f20 | |||
| 5e80bac19d | |||
| d39c8ff550 | |||
| d79e75fa88 | |||
| c773b6da26 | |||
| ba2c36b6f2 | |||
| 988a44b609 | |||
| 1b904fe20d | |||
| 9e4e0d7a9a | |||
| 78ba3041f1 | |||
| 5988d0df38 | |||
| 9a6d7670f4 | |||
| 1becccb339 | |||
| adff03ea7c | |||
| 7979c6c917 | |||
| 7bb64786ac | |||
| caa7495a71 | |||
| c87694e5ce | |||
| 10eebfc9d0 | |||
| 7abedc9bce | |||
| 2ee850da6e | |||
| 0a3f5dfc80 | |||
| 34f1e08f2c | |||
| bf8252970a | |||
| 1bf2ea313f | |||
| 8dc529451c | |||
| e26b8401ef | |||
| e474d0d882 | |||
| 585aff8319 | |||
| 3f5eeb737e | |||
| ee2ccd5c5e | |||
| 272b065ba7 | |||
| 825a73a5b9 | |||
| 0becd2930d | |||
| f3b7d23bb7 | |||
| be0b6b6dd4 | |||
| e89454c1ec | |||
| 956ddd6b73 | |||
| deb2901c6f | |||
| 35057c2851 | |||
| 3a467d9d5a | |||
| 0fcada7d14 | |||
| 1fdd7c1b25 | |||
| 261a64de76 | |||
| 32864aae67 | |||
| f9aba632ec | |||
| 743c086b36 | |||
| 6460253941 | |||
| caae3465cd | |||
| 19a19d8fd8 | |||
| 4979fedcc0 | |||
| bd73d73cd2 | |||
| bf72a032df | |||
| d7bf0ac5ef | |||
| adcc180ad9 | |||
| 5d047ba639 | |||
| ee6431e01b | |||
| cc85762e1e | |||
| 84ce934f17 | |||
| 8c66325e12 | |||
| 84369eb4a4 | |||
| d9d216c772 | |||
| 8015e3ff06 | |||
| c1cb6f8881 | |||
| e7041bbddc | |||
| 8c502c71a7 | |||
| b14adc3781 | |||
| 11e3fb8043 | |||
| 7237535d6e | |||
| 883b856060 | |||
| e18ea61992 | |||
| 4e9393521c | |||
| a534461867 | |||
| 9810330571 | |||
| 75622374ff | |||
| 02869c07d7 | |||
| 1d3dfec593 | |||
| a4d3d41ece | |||
| 085eac3dce | |||
| 1fd2399e73 | |||
| 9821c67e72 | |||
| bb5c4d754c | |||
| 503793a2ee | |||
| b611c1ffad | |||
| 9ab532c2b4 | |||
| bf63967cfd | |||
| 93499d7d10 | |||
| 7e1847a5ac | |||
| 64b7624e74 | |||
| 06e284a1e0 | |||
| 53d9fb3a6e | |||
| cfb30912b0 | |||
| 52366f3812 | |||
| 2e513d155d | |||
| 97329837d4 | |||
| 1f5d5988cb | |||
| 45dfbfcfbb | |||
| 99e9371969 | |||
| ff3e6f723c | |||
| 620d757f8b | |||
| 0f61ffae27 | |||
| 3970af8ce2 | |||
| 0e08d89e01 | |||
| 62f0e703de | |||
| 447231f6b0 | |||
| 7e0a5ee800 |
+22
@@ -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.*
|
||||
@@ -0,0 +1,39 @@
|
||||
# turingpi
|
||||
|
||||
Home k3s cluster on a Turing Pi (arm64 nodes), GitOps-managed by ArgoCD.
|
||||
|
||||
## Deploying changes
|
||||
|
||||
- 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>/`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
### qbittorrent + gluetun (AirVPN) — DNS / restart loop
|
||||
|
||||
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.
|
||||
|
||||
The gluetun sidecar in `helm-values/qbittorrent_values.yaml` **must** keep:
|
||||
|
||||
```yaml
|
||||
- name: DOT
|
||||
value: "off"
|
||||
- name: DNS_ADDRESS
|
||||
value: "10.128.0.1" # AirVPN's pushed resolver, reached over the tunnel — no DNS leak
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,152 @@
|
||||
# TuringPi K3s Homelab
|
||||
|
||||
This repository contains Kubernetes configuration files for a K3s cluster running on TuringPi hardware. It includes Helm charts, values files, and manifests for deploying various self-hosted applications in a homelab environment.
|
||||
|
||||
## 🏗️ Cluster Architecture
|
||||
|
||||
### Hardware Setup
|
||||
- **turing1**: Control plane + worker (192.168.222.237)
|
||||
- **turing2**: Worker node
|
||||
- **turing3**: Worker node (NFS server at turing3.lan)
|
||||
- **turing4**: Worker node
|
||||
- **beelink**: Additional x86_64 worker node
|
||||
|
||||
### Infrastructure Stack
|
||||
- **Kubernetes**: K3s lightweight distribution
|
||||
- **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 certificates
|
||||
- **Ingress**: Nginx with LAN-only access restrictions
|
||||
|
||||
## 🚀 Applications
|
||||
|
||||
### Media Services
|
||||
- **Plex**: Via kube-plex (Kubernetes-native with dynamic transcoding)
|
||||
- **Jellyfin**: Alternative media server
|
||||
- **Sonarr/Radarr**: TV/Movie management
|
||||
- **Prowlarr**: Indexer management
|
||||
- **Transmission**: BitTorrent client with OpenVPN
|
||||
- **FlareSolverr**: Captcha solver service
|
||||
|
||||
### Other Applications
|
||||
- **Actual Budget**: Personal finance management
|
||||
- **Home Assistant Voice LLMs**: AI voice integration
|
||||
- **Ollama**: Local LLM inference
|
||||
- **Prometheus**: Monitoring and metrics
|
||||
- **PostgreSQL**: Database backend
|
||||
|
||||
## 📁 Repository Structure
|
||||
|
||||
```
|
||||
├── 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
|
||||
|
||||
### Application Deployment
|
||||
```bash
|
||||
# Deploy with Helm using values files
|
||||
helm upgrade <release-name> <chart> -f <app>_values.yaml -i
|
||||
|
||||
# Examples:
|
||||
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
|
||||
```
|
||||
|
||||
### Infrastructure Management
|
||||
```bash
|
||||
# Apply Kubernetes manifests
|
||||
kubectl apply -f metallb.yml
|
||||
kubectl apply -f ingress.yaml
|
||||
|
||||
# Check cluster status
|
||||
kubectl get nodes
|
||||
kubectl get pods --all-namespaces
|
||||
```
|
||||
|
||||
## 🔄 K3s Cluster Updates
|
||||
|
||||
### Automated Update
|
||||
Run the provided script to update all nodes:
|
||||
```bash
|
||||
./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)
|
||||
```bash
|
||||
ssh root@turing1 # password: turing
|
||||
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
|
||||
```
|
||||
|
||||
#### 2. Update Worker Nodes (turing2, turing3, turing4)
|
||||
```bash
|
||||
ssh root@<node> # password: turing
|
||||
curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_DOWNLOAD=false \
|
||||
K3S_URL=https://192.168.222.237:6443 \
|
||||
K3S_TOKEN=torino sh -
|
||||
```
|
||||
|
||||
#### 3. Update Beelink Node
|
||||
```bash
|
||||
ssh gilgamezh@beelink.lan # no password (SSH keys)
|
||||
sudo curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_DOWNLOAD=false \
|
||||
K3S_URL=https://192.168.222.237:6443 \
|
||||
K3S_TOKEN=torino sh -
|
||||
```
|
||||
|
||||
#### 4. Verify Update
|
||||
```bash
|
||||
kubectl get nodes # Check all nodes show new version
|
||||
kubectl get pods --all-namespaces | grep -v Running # Check for issues
|
||||
```
|
||||
|
||||
## 🔑 Access Information
|
||||
|
||||
- **Cluster Token**: `torino`
|
||||
- **Master Node**: `192.168.222.237:6443`
|
||||
- **SSH Access**:
|
||||
- TuringPi nodes: `root@<hostname>` (password: `turing`)
|
||||
- Beelink: `gilgamezh@beelink.lan` (SSH keys)
|
||||
|
||||
## 📚 Additional Documentation
|
||||
|
||||
- See `CLAUDE.md` for detailed Claude Code integration guide
|
||||
- Custom Helm charts include their own README files
|
||||
- Check application-specific `*_values.yaml` files for configuration options
|
||||
|
||||
## 🛠️ Development
|
||||
|
||||
### Helm Chart Development
|
||||
```bash
|
||||
helm create <chart-name>
|
||||
helm lint <chart-path>
|
||||
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
|
||||
@@ -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 2–3 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).
|
||||
@@ -1,46 +0,0 @@
|
||||
# Default values for my-actual-server.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: docker.io/actualbudget/actual-server
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: "latest"
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 5006
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
className: ""
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-production
|
||||
kubernetes.io/ingress.class: traefik
|
||||
hosts:
|
||||
- host: actual.gilgamezh.me
|
||||
paths:
|
||||
- path: /
|
||||
pathType: ImplementationSpecific
|
||||
tls:
|
||||
- hosts:
|
||||
- actual.gilgamezh.me
|
||||
secretName: actual-gilgamezh-me
|
||||
|
||||
volumes:
|
||||
- name: "actual-data"
|
||||
persistentVolumeClaim:
|
||||
claimName: "actual-data" # PersistentVolumeClaim created earlier
|
||||
|
||||
volumeMounts:
|
||||
- name: "actual-data"
|
||||
mountPath: "/data"
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,26 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: lidarr
|
||||
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/lidarr
|
||||
helm:
|
||||
releaseName: lidarr
|
||||
valueFiles:
|
||||
- ../../helm-values/lidarr_values.yaml
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: default
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,30 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: ollama
|
||||
namespace: argocd
|
||||
annotations:
|
||||
spec:
|
||||
project: default
|
||||
sources:
|
||||
- repoURL: https://otwld.github.io/ollama-helm/
|
||||
chart: ollama
|
||||
targetRevision: "*"
|
||||
ref: charts
|
||||
helm:
|
||||
releaseName: ollama
|
||||
valueFiles:
|
||||
- $values/helm-values/ollama_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
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: plex
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
sources:
|
||||
- repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/kube-plex.git
|
||||
path: charts/kube-plex
|
||||
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: master
|
||||
ref: values
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: default
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
@@ -0,0 +1,26 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: prowlarr
|
||||
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/prowlarr
|
||||
helm:
|
||||
releaseName: prowlarr
|
||||
valueFiles:
|
||||
- ../../helm-values/prowlarr_values.yml
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: default
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
@@ -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
|
||||
@@ -0,0 +1,33 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: radarr
|
||||
namespace: argocd
|
||||
annotations:
|
||||
# Enable automatic image updates for Radarr
|
||||
# Use digest strategy to track latest tag updates
|
||||
# Write changes back via ArgoCD API (multi-source friendly)
|
||||
spec:
|
||||
project: default
|
||||
sources:
|
||||
- repoURL: https://bananaspliff.github.io/geek-charts
|
||||
chart: radarr
|
||||
targetRevision: "*"
|
||||
ref: charts
|
||||
helm:
|
||||
releaseName: radarr
|
||||
valueFiles:
|
||||
- $values/helm-values/radarr_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
|
||||
@@ -0,0 +1,33 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: sonarr
|
||||
namespace: argocd
|
||||
annotations:
|
||||
# Enable automatic image updates for Sonarr
|
||||
# Use digest strategy to track latest tag updates
|
||||
# Write changes back via ArgoCD API (multi-source friendly)
|
||||
spec:
|
||||
project: default
|
||||
sources:
|
||||
- repoURL: https://bananaspliff.github.io/geek-charts
|
||||
chart: sonarr
|
||||
targetRevision: "*"
|
||||
ref: charts
|
||||
helm:
|
||||
releaseName: sonarr
|
||||
valueFiles:
|
||||
- $values/helm-values/sonarr_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
|
||||
@@ -0,0 +1,30 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: transmission
|
||||
namespace: argocd
|
||||
annotations:
|
||||
spec:
|
||||
project: default
|
||||
sources:
|
||||
- repoURL: https://bananaspliff.github.io/geek-charts
|
||||
chart: transmission-openvpn
|
||||
targetRevision: "*"
|
||||
ref: charts
|
||||
helm:
|
||||
releaseName: transmission
|
||||
valueFiles:
|
||||
- $values/helm-values/transmission_values.yml
|
||||
- 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
|
||||
@@ -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"
|
||||
|
||||
Executable
+180
@@ -0,0 +1,180 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
MASTER_NODE="turing1"
|
||||
WORKER_NODES=("turing2" "turing3" "turing4")
|
||||
BEELINK_NODE="beelink.lan"
|
||||
MASTER_IP="192.168.222.237"
|
||||
TOKEN="torino"
|
||||
SSH_PASSWORD="turing"
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Function to check if sshpass is available
|
||||
check_dependencies() {
|
||||
if ! command -v sshpass &> /dev/null; then
|
||||
print_warning "sshpass not found, installing..."
|
||||
if command -v pacman &> /dev/null; then
|
||||
sudo pacman -S sshpass --noconfirm
|
||||
elif command -v apt-get &> /dev/null; then
|
||||
sudo apt-get update && sudo apt-get install -y sshpass
|
||||
else
|
||||
print_error "Cannot install sshpass automatically. Please install it manually."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to upgrade master node
|
||||
upgrade_master() {
|
||||
print_status "Upgrading master node: $MASTER_NODE"
|
||||
|
||||
sshpass -p "$SSH_PASSWORD" ssh -o StrictHostKeyChecking=no root@$MASTER_NODE '
|
||||
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
|
||||
'
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_status "Master node $MASTER_NODE upgraded successfully"
|
||||
else
|
||||
print_error "Failed to upgrade master node $MASTER_NODE"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to upgrade worker nodes
|
||||
upgrade_worker() {
|
||||
local node=$1
|
||||
print_status "Upgrading worker node: $node"
|
||||
|
||||
sshpass -p "$SSH_PASSWORD" ssh -o StrictHostKeyChecking=no root@$node "
|
||||
curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_DOWNLOAD=false \
|
||||
K3S_URL=https://$MASTER_IP:6443 \
|
||||
K3S_TOKEN=$TOKEN sh -
|
||||
"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_status "Worker node $node upgraded successfully"
|
||||
else
|
||||
print_error "Failed to upgrade worker node $node"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to upgrade beelink node
|
||||
upgrade_beelink() {
|
||||
print_status "Upgrading beelink node: $BEELINK_NODE"
|
||||
|
||||
ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no gilgamezh@$BEELINK_NODE "
|
||||
sudo curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_DOWNLOAD=false \
|
||||
K3S_URL=https://$MASTER_IP:6443 \
|
||||
K3S_TOKEN=$TOKEN sh -
|
||||
"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_status "Beelink node $BEELINK_NODE upgraded successfully"
|
||||
else
|
||||
print_error "Failed to upgrade beelink node $BEELINK_NODE"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to verify cluster health
|
||||
verify_cluster() {
|
||||
print_status "Verifying cluster health..."
|
||||
|
||||
# Wait a moment for nodes to register
|
||||
sleep 10
|
||||
|
||||
print_status "Cluster nodes:"
|
||||
kubectl get nodes
|
||||
|
||||
print_status "Checking for unhealthy pods..."
|
||||
unhealthy_pods=$(kubectl get pods --all-namespaces | grep -v Running | grep -v Completed | wc -l)
|
||||
|
||||
if [ "$unhealthy_pods" -gt 1 ]; then # Greater than 1 because header line counts
|
||||
print_warning "Found unhealthy pods:"
|
||||
kubectl get pods --all-namespaces | grep -v Running | grep -v Completed
|
||||
else
|
||||
print_status "All pods are healthy"
|
||||
fi
|
||||
|
||||
print_status "Cluster upgrade completed successfully!"
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
print_status "Starting K3s cluster upgrade..."
|
||||
print_status "This will upgrade all nodes in the TuringPi cluster"
|
||||
|
||||
# Check dependencies
|
||||
check_dependencies
|
||||
|
||||
# Show current cluster state
|
||||
print_status "Current cluster state:"
|
||||
kubectl get nodes
|
||||
|
||||
read -p "Do you want to continue with the upgrade? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
print_status "Upgrade cancelled"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Upgrade master node first
|
||||
upgrade_master
|
||||
|
||||
# Wait a bit for master to stabilize
|
||||
sleep 15
|
||||
|
||||
# Upgrade worker nodes
|
||||
failed_workers=0
|
||||
for worker in "${WORKER_NODES[@]}"; do
|
||||
if ! upgrade_worker "$worker"; then
|
||||
((failed_workers++))
|
||||
fi
|
||||
sleep 5 # Brief pause between worker upgrades
|
||||
done
|
||||
|
||||
# Upgrade beelink node
|
||||
if ! upgrade_beelink; then
|
||||
print_warning "Beelink node upgrade failed, but continuing..."
|
||||
fi
|
||||
|
||||
# Verify cluster health
|
||||
verify_cluster
|
||||
|
||||
if [ $failed_workers -gt 0 ]; then
|
||||
print_warning "Upgrade completed with $failed_workers failed worker node(s)"
|
||||
exit 1
|
||||
else
|
||||
print_status "All nodes upgraded successfully!"
|
||||
fi
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
@@ -0,0 +1,5 @@
|
||||
apiVersion: v1
|
||||
appVersion: "1.0"
|
||||
description: cross-seed - Torrent seeding assistant
|
||||
name: cross-seed
|
||||
version: 0.1.0
|
||||
@@ -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 }}
|
||||
@@ -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: {}
|
||||
@@ -0,0 +1,24 @@
|
||||
apiVersion: v2
|
||||
name: home-assistant-voice-llms
|
||||
description: A Helm chart for Kubernetes
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "1.16.0"
|
||||
@@ -0,0 +1,62 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "home-assistant-voice-llms.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 "home-assistant-voice-llms.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 "home-assistant-voice-llms.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "home-assistant-voice-llms.labels" -}}
|
||||
helm.sh/chart: {{ include "home-assistant-voice-llms.chart" . }}
|
||||
{{ include "home-assistant-voice-llms.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "home-assistant-voice-llms.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "home-assistant-voice-llms.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "home-assistant-voice-llms.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "home-assistant-voice-llms.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,97 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-faster-whisper
|
||||
spec:
|
||||
replicas: {{ .Values.defaults.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: faster-whisper
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: faster-whisper
|
||||
spec:
|
||||
containers:
|
||||
- name: faster-whisper
|
||||
image: {{ .Values.defaults.fasterWhisper.image.repository }}:{{ .Values.defaults.fasterWhisper.image.tag }}
|
||||
imagePullPolicy: {{ .Values.defaults.fasterWhisper.image.pullPolicy }}
|
||||
env:
|
||||
- name: PUID
|
||||
value: "{{ .Values.defaults.fasterWhisper.env.PUID }}"
|
||||
- name: PGID
|
||||
value: "{{ .Values.defaults.fasterWhisper.env.PGID }}"
|
||||
- name: TZ
|
||||
value: "{{ .Values.defaults.fasterWhisper.env.TZ }}"
|
||||
- name: WHISPER_MODEL
|
||||
value: "{{ .Values.defaults.fasterWhisper.env.WHISPER_MODEL }}"
|
||||
- name: WHISPER_BEAM
|
||||
value: "{{ .Values.defaults.fasterWhisper.env.WHISPER_BEAM }}"
|
||||
- name: WHISPER_LANG
|
||||
value: "{{ .Values.defaults.fasterWhisper.env.WHISPER_LANG }}"
|
||||
ports:
|
||||
- containerPort: 10300
|
||||
volumeMounts:
|
||||
- mountPath: {{ .Values.defaults.fasterWhisper.volume.mountPath }}
|
||||
name: config
|
||||
{{- if .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{ toYaml .Values.nodeSelector | indent 8 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ .Values.defaults.fasterWhisper.volume.claimName }}
|
||||
|
||||
---
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-piper
|
||||
spec:
|
||||
replicas: {{ .Values.defaults.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: piper
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: piper
|
||||
spec:
|
||||
containers:
|
||||
- name: piper
|
||||
image: {{ .Values.defaults.piper.image.repository }}:{{ .Values.defaults.piper.image.tag }}
|
||||
imagePullPolicy: {{ .Values.defaults.piper.image.pullPolicy }}
|
||||
env:
|
||||
- name: PUID
|
||||
value: "{{ .Values.defaults.piper.env.PUID }}"
|
||||
- name: PGID
|
||||
value: "{{ .Values.defaults.piper.env.PGID }}"
|
||||
- name: TZ
|
||||
value: "{{ .Values.defaults.piper.env.TZ }}"
|
||||
- name: PIPER_VOICE
|
||||
value: "{{ .Values.defaults.piper.env.PIPER_VOICE }}"
|
||||
- name: PIPER_LENGTH
|
||||
value: "{{ .Values.defaults.piper.env.PIPER_LENGTH }}"
|
||||
- name: PIPER_NOISE
|
||||
value: "{{ .Values.defaults.piper.env.PIPER_NOISE }}"
|
||||
- name: PIPER_NOISEW
|
||||
value: "{{ .Values.defaults.piper.env.PIPER_NOISEW }}"
|
||||
- name: PIPER_SPEAKER
|
||||
value: "{{ .Values.defaults.piper.env.PIPER_SPEAKER }}"
|
||||
- name: PIPER_PROCS
|
||||
value: "{{ .Values.defaults.piper.env.PIPER_PROCS }}"
|
||||
ports:
|
||||
- containerPort: 10200
|
||||
volumeMounts:
|
||||
- mountPath: {{ .Values.defaults.piper.volume.mountPath }}
|
||||
name: config
|
||||
volumes:
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ .Values.defaults.piper.volume.claimName }}
|
||||
{{- if .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{ toYaml .Values.nodeSelector | indent 8 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,25 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ .Values.defaults.fasterWhisper.volume.claimName }}
|
||||
spec:
|
||||
storageClassName: nfs-client
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.defaults.fasterWhisper.volume.storage }}
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ .Values.defaults.piper.volume.claimName }}
|
||||
spec:
|
||||
storageClassName: nfs-client
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.defaults.piper.volume.storage }}
|
||||
@@ -0,0 +1,25 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-faster-whisper
|
||||
spec:
|
||||
type: {{ .Values.defaults.fasterWhisper.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.defaults.fasterWhisper.service.port }}
|
||||
targetPort: 10300
|
||||
selector:
|
||||
app: faster-whisper
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-piper
|
||||
spec:
|
||||
type: {{ .Values.defaults.piper.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.defaults.piper.service.port }}
|
||||
targetPort: 10200
|
||||
selector:
|
||||
app: piper
|
||||
@@ -0,0 +1,57 @@
|
||||
defaults:
|
||||
replicaCount: 1
|
||||
fasterWhisper:
|
||||
image:
|
||||
repository: lscr.io/linuxserver/faster-whisper
|
||||
tag: "2.4.0"
|
||||
pullPolicy: IfNotPresent
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 10300
|
||||
resources:
|
||||
limits:
|
||||
cpu: "3"
|
||||
memory: "5000Mi"
|
||||
requests:
|
||||
cpu: "3"
|
||||
memory: "2000Mi"
|
||||
env:
|
||||
PUID: 1000
|
||||
PGID: 1000
|
||||
TZ: Europe/Amsterdam
|
||||
WHISPER_MODEL: small-int8
|
||||
WHISPER_BEAM: 1
|
||||
WHISPER_LANG: en
|
||||
volume:
|
||||
mountPath: /config
|
||||
claimName: faster-whisper-pvc
|
||||
storage: 1Gi
|
||||
piper:
|
||||
image:
|
||||
repository: lscr.io/linuxserver/piper
|
||||
tag: "latest"
|
||||
pullPolicy: IfNotPresent
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 10200
|
||||
resources:
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: "4000Mi"
|
||||
requests:
|
||||
cpu: "1"
|
||||
memory: "2000Mi"
|
||||
env:
|
||||
PUID: 1000
|
||||
PGID: 1000
|
||||
TZ: Europe/Amsterdam
|
||||
PIPER_VOICE: en_US-lessac-medium
|
||||
PIPER_LENGTH: 1.0
|
||||
PIPER_NOISE: 0.667
|
||||
PIPER_NOISEW: 0.333
|
||||
PIPER_SPEAKER: 0
|
||||
PIPER_PROCS: 2
|
||||
volume:
|
||||
mountPath: /config
|
||||
claimName: piper-pvc
|
||||
storage: 1Gi
|
||||
@@ -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,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 }}
|
||||
@@ -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: {}
|
||||
@@ -0,0 +1,23 @@
|
||||
# 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
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
@@ -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: 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 }}
|
||||
@@ -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: {}
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
@@ -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: {}
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
image:
|
||||
repository: ghcr.io/flaresolverr/flaresolverr
|
||||
pullPolicy: IfNotPresent
|
||||
tag: v3.3.21
|
||||
|
||||
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
|
||||
@@ -1,2 +0,0 @@
|
||||
ID: e464f256-841f-4ba6-a489-b72b0530f9ec
|
||||
Key: 5a81e9238d361cce4393f56b8db8f4be2f23b0e9d571667c4a5625a7ca844a528195496d571dabf81b51a2480d07ac8ef0397b05f8cd7393984e4f74b516c6f9
|
||||
@@ -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: {}
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
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
|
||||
|
||||
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: "4Gi"
|
||||
cpu: "4000m"
|
||||
ephemeral-storage: "1Gi"
|
||||
@@ -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: {}
|
||||
@@ -0,0 +1,22 @@
|
||||
replicaCount: 1
|
||||
image:
|
||||
repository: ollama/ollama
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "latest"
|
||||
|
||||
|
||||
# Ollama parameters
|
||||
ollama:
|
||||
models:
|
||||
pull:
|
||||
- TinyLlama
|
||||
- llama3.1:8b
|
||||
# Configure Service
|
||||
service:
|
||||
# -- Service type
|
||||
type: LoadBalancer
|
||||
# -- Service port
|
||||
port: 11434
|
||||
|
||||
nodeSelector:
|
||||
kubernetes.io/arch: amd64
|
||||
@@ -1,27 +1,18 @@
|
||||
claimToken: "claim-Ku2YYmJzDB1mpyG6YD7x"
|
||||
|
||||
claimToken: "claim-E_NxQDtUMMVsLCBFvybK"
|
||||
image:
|
||||
repository: linuxserver/plex
|
||||
tag: 1.41.3
|
||||
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:
|
||||
beta.kubernetes.io/arch: arm64
|
||||
|
||||
|
||||
kubernetes.io/arch: amd64
|
||||
persistence:
|
||||
transcode:
|
||||
claimName: "plex-transcode"
|
||||
@@ -29,15 +20,19 @@ 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"
|
||||
cpu: "3"
|
||||
cpu: "1"
|
||||
ephemeral-storage: "50Mi"
|
||||
limits:
|
||||
memory: "6Gi"
|
||||
cpu: "4"
|
||||
memory: "10Gi"
|
||||
cpu: "3"
|
||||
ephemeral-storage: "1Gi"
|
||||
podAnnotations: {}
|
||||
proxy:
|
||||
@@ -1,6 +1,6 @@
|
||||
image:
|
||||
repository: lscr.io/linuxserver/prowlarr
|
||||
tag: 1.23.1
|
||||
tag: "latest"
|
||||
pullPolicy: Always
|
||||
|
||||
env:
|
||||
@@ -37,4 +37,3 @@ nodeSelector: {}
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
---
|
||||
replicaCount: 1
|
||||
|
||||
qbittorrent:
|
||||
image:
|
||||
repository: lscr.io/linuxserver/qbittorrent
|
||||
tag: "5.2.1_v2.0.12-ls459"
|
||||
pullPolicy: Always
|
||||
env:
|
||||
- name: PUID
|
||||
value: "1000"
|
||||
- name: PGID
|
||||
value: "1000"
|
||||
- name: TZ
|
||||
value: "Europe/Amsterdam"
|
||||
- name: WEBUI_PORT
|
||||
value: "8080"
|
||||
- name: TORRENTING_PORT
|
||||
value: "54408"
|
||||
torrentPort: 54408
|
||||
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: "/nfs/incomplete_torrents"
|
||||
subPath: "incomplete_torrents"
|
||||
- name: plex-data
|
||||
mountPath: "/nfs/torrent"
|
||||
subPath: "torrent"
|
||||
|
||||
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
|
||||
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"
|
||||
# AirVPN blocks outbound DNS-over-TLS (tcp/853), so gluetun's default
|
||||
# DoT resolver never gets answers and the startup healthcheck loops
|
||||
# forever on "lookup cloudflare.com: i/o timeout". Use AirVPN's pushed
|
||||
# plaintext resolver instead (reached over the tunnel, no DNS leak).
|
||||
- name: DOT
|
||||
value: "off"
|
||||
- name: DNS_ADDRESS
|
||||
value: "10.128.0.1"
|
||||
- name: FIREWALL_INPUT_PORTS
|
||||
value: "8080"
|
||||
- name: FIREWALL_VPN_INPUT_PORTS
|
||||
value: "54408"
|
||||
- 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
|
||||
|
||||
volumes:
|
||||
- name: plex-data
|
||||
persistentVolumeClaim:
|
||||
claimName: plex-data
|
||||
- name: dev-tun
|
||||
hostPath:
|
||||
path: /dev/net/tun
|
||||
|
||||
nodeSelector:
|
||||
kubernetes.io/arch: arm64
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
@@ -3,7 +3,7 @@ replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: ghcr.io/linuxserver/radarr
|
||||
tag: 5.10.4
|
||||
tag: "latest"
|
||||
pullPolicy: Always
|
||||
|
||||
env:
|
||||
@@ -3,7 +3,7 @@ replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: ghcr.io/linuxserver/sonarr
|
||||
tag: 4.0.9
|
||||
tag: "latest"
|
||||
pullPolicy: Always
|
||||
|
||||
env:
|
||||
@@ -51,9 +51,10 @@ volumeMounts:
|
||||
mountPath: "/dev/net/tun" # Needed for VPN
|
||||
- name: "plex-data"
|
||||
mountPath: "/etc/openvpn/custom/"
|
||||
subPath: "airvpn" # Path /mnt/ssd/media/downloads/transmission where transmission downloads Torrents
|
||||
subPath: "airvpn"
|
||||
|
||||
securityContext:
|
||||
capabilities: # Needed for VPN
|
||||
add:
|
||||
- NET_ADMIN
|
||||
privileged: true
|
||||
|
||||
nodeSelector:
|
||||
kubernetes.io/arch: arm64
|
||||
-1
Submodule kube-plex deleted from 74c7ede426
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user