Import manifests reverse-engineered from live cluster
Captured 2026-07-26 from rpi-master (k3s v1.30.3) and cleaned of runtime fields. Six apps as per-app kustomizations: vaultwarden, searxng, home-assistant, nginx, pihole(+unbound), unified-streaming. Intentional divergences from live state: - pihole WEBPASSWORD and USP_LICENSE_KEY moved from inline plaintext env to secretKeyRef (secrets gitignored; templates in secret.example.yaml) - HA ingress defaultBackend fixed (pointed at nonexistent service) - unifiedstreaming-svc kept as ClusterIP (LoadBalancer could never bind port 80 behind svclb-traefik) Validated against the live cluster with kubectl apply --dry-run=server: no immutable-field conflicts; one-time kubectl replace procedure for the two env->secretKeyRef migrations documented in README. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
ec9b422f87
commit
8afac39d03
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Real secrets never go in git — only *.example.yaml templates do.
|
||||
apps/**/secret.yaml
|
||||
*.secret.yaml
|
||||
100
README.md
100
README.md
@ -1,3 +1,101 @@
|
||||
# homelab-k3s
|
||||
|
||||
k3s deployments and change management for homelab env.
|
||||
k3s deployments and change management for the homelab Raspberry Pi cluster
|
||||
(rpi-master + rpi-worker-1/2/3, k3s v1.30.3, flannel/traefik/klipper-lb
|
||||
defaults).
|
||||
|
||||
Manifests were reverse-engineered from the live cluster on 2026-07-26 and
|
||||
cleaned of runtime fields. Except where a file header says **DIVERGENCE**,
|
||||
they are faithful to what was running.
|
||||
|
||||
## Layout
|
||||
|
||||
Each app under `apps/` is a self-contained kustomization
|
||||
(namespace + PV/PVC + deployment + service + ingress):
|
||||
|
||||
| App | Namespace | Hostname(s) |
|
||||
|---|---|---|
|
||||
| vaultwarden | bitwarden | pm.wittyoneoff.com |
|
||||
| searxng | default | searxng.wittyoneoff.com, origin-searxng.wittyoneoff.com |
|
||||
| home-assistant | home-assistant | ha.wittyoneoff.com, ha-origin.wittyoneoff.com |
|
||||
| nginx | nginx | www.wittyoneoff.com, www.ramonaajj.com |
|
||||
| pihole (+unbound) | pihole | admin on :8000 via klipper-lb, DNS on :53 |
|
||||
| unified-streaming | unified-streaming | unified.wittyoneoff.com |
|
||||
|
||||
Not in this repo: socktop (own repo + pipeline), rancher/monitoring/logging
|
||||
(rancher-managed), traefik (k3s packaged component).
|
||||
|
||||
## Applying
|
||||
|
||||
```sh
|
||||
kubectl apply -k . # everything
|
||||
kubectl apply -k apps/home-assistant # one app
|
||||
```
|
||||
|
||||
### One-time secret bootstrap (per cluster)
|
||||
|
||||
Three apps read secrets that are NOT in git. Each app dir has a
|
||||
`secret.example.yaml`; copy to `secret.yaml` (gitignored), fill in, apply.
|
||||
|
||||
| Secret | Namespace | Used for |
|
||||
|---|---|---|
|
||||
| `pihole-admin` | pihole | pi-hole admin `WEBPASSWORD` |
|
||||
| `usp-license` | unified-streaming | `USP_LICENSE_KEY` |
|
||||
| `searxng-1723974683-config` | default | searxng `settings.yml` |
|
||||
|
||||
**One-time migration (pihole + unified-streaming):** the live cluster (as of
|
||||
2026-07-26) has WEBPASSWORD / USP_LICENSE_KEY as inline plaintext `value`s.
|
||||
`kubectl apply` (client- or server-side) cannot patch an env var from `value`
|
||||
to `valueFrom` — the API rejects the merged object. Validated procedure:
|
||||
|
||||
```sh
|
||||
# 1. create the two secrets (see secret.example.yaml in each app dir)
|
||||
# 2. one-time replace (restarts the pods):
|
||||
kubectl replace -f apps/pihole/deployment-pihole.yaml
|
||||
kubectl replace -f apps/unified-streaming/deployment.yaml
|
||||
```
|
||||
|
||||
After that, `kubectl apply -k .` works cleanly for everything (verified with
|
||||
`--dry-run=server` against the live cluster, 2026-07-26 — zero immutable-field
|
||||
conflicts).
|
||||
|
||||
## Cluster assumptions (state that lives outside these manifests)
|
||||
|
||||
- **Node labels** — scheduling relies on them: workers carry `cpu=arm`
|
||||
(all nodes do), the control-plane node carries `controller=true`
|
||||
(most apps use `controller NotIn (true)` to stay off the master), pis carry
|
||||
`model=raspi` (pihole/unbound prefer it). Re-label with:
|
||||
`kubectl label node <n> cpu=arm model=raspi` /
|
||||
`kubectl label node rpi-master controller=true`.
|
||||
- **GlusterFS** — every PV is `hostPath` under `/mnt/gvolume0/…`, which is the
|
||||
gluster volume mounted identically on all nodes. The mount must exist before
|
||||
pods schedule; PVs bind by explicit `volumeName` + `storageClassName: ""`.
|
||||
- **Ingress/LB** — traefik (k3s default) terminates all HTTP on 80/443 via
|
||||
svclb on every node. Because of that, no other LoadBalancer service can
|
||||
claim host port 80 (that's why unifiedstreaming-svc is ClusterIP).
|
||||
- **Home Assistant config** — lives on the PV, not in git.
|
||||
`http.trusted_proxies` must include `10.42.0.0/16` (the cluster pod CIDR)
|
||||
or HA 400s everything the moment traefik reschedules to another node.
|
||||
|
||||
## Known quirks (faithful to live state, fix at leisure)
|
||||
|
||||
- Most images are `:latest`/`:stable` — deploys are not reproducible until
|
||||
pinned. vaultwarden is pinned (1.35.4).
|
||||
- searxng keeps its helm-generated name suffix (`searxng-1723974683`) because
|
||||
deployment selectors are immutable; its helm release record still exists in
|
||||
the cluster — don't run `helm upgrade/uninstall` on it, this repo is the
|
||||
source of truth now.
|
||||
- searxng's `TZ=America/Los Angeles` (missing underscore) is invalid tzdata —
|
||||
container falls back to UTC.
|
||||
- vaultwarden and home-assistant carry helm-chart-era label/name shapes but
|
||||
their release records are gone; they are plain manifests now.
|
||||
- pihole runs `privileged: true` with `PIHOLE_UID=0`.
|
||||
|
||||
## History
|
||||
|
||||
- 2026-07-25: cluster outage (rpi-master undervoltage → CNI cache corruption).
|
||||
Recovery runbook in the Notes vault:
|
||||
`rpi-master_undervoltage_CNI_corruption_incident-2026-07-25.md`.
|
||||
- 2026-07-26: initial import of manifests from live cluster; HA ingress
|
||||
defaultBackend fixed; unifiedstreaming-svc LoadBalancer→ClusterIP;
|
||||
pihole/USP secrets moved to secretKeyRef (pending first apply).
|
||||
|
||||
74
apps/home-assistant/deployment.yaml
Normal file
74
apps/home-assistant/deployment.yaml
Normal file
@ -0,0 +1,74 @@
|
||||
# Originally installed via helm chart home-assistant-13.3.0 (release record no
|
||||
# longer in cluster); managed as a raw manifest since. Selector labels are
|
||||
# immutable — do not change them.
|
||||
# NOTE: /config/configuration.yaml (on the PV, not in git) must keep
|
||||
# http.trusted_proxies: [10.42.0.0/16] so traefik can reach HA from any node.
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: home-assistant
|
||||
namespace: home-assistant
|
||||
labels:
|
||||
app.kubernetes.io/instance: home-assistant
|
||||
app.kubernetes.io/name: home-assistant
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: home-assistant
|
||||
app.kubernetes.io/name: home-assistant
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: home-assistant
|
||||
app.kubernetes.io/name: home-assistant
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: cpu
|
||||
operator: In
|
||||
values:
|
||||
- arm
|
||||
- key: controller
|
||||
operator: NotIn
|
||||
values:
|
||||
- "true"
|
||||
containers:
|
||||
- name: home-assistant
|
||||
image: ghcr.io/home-assistant/home-assistant:stable
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: TZ
|
||||
value: UTC
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8123
|
||||
protocol: TCP
|
||||
startupProbe:
|
||||
tcpSocket:
|
||||
port: 8123
|
||||
failureThreshold: 30
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 8123
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 8123
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /config
|
||||
volumes:
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: home-assistant
|
||||
37
apps/home-assistant/ingress.yaml
Normal file
37
apps/home-assistant/ingress.yaml
Normal file
@ -0,0 +1,37 @@
|
||||
# NOTE: the live object's defaultBackend pointed at a service named
|
||||
# "home-assistant", which does not exist (the real service is
|
||||
# "home-assistant-svc"). Fixed here — this is the one intentional divergence
|
||||
# from the cluster state captured 2026-07-26.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: home-assistant
|
||||
namespace: home-assistant
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
defaultBackend:
|
||||
service:
|
||||
name: home-assistant-svc
|
||||
port:
|
||||
number: 80
|
||||
rules:
|
||||
- host: ha.wittyoneoff.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: home-assistant-svc
|
||||
port:
|
||||
number: 80
|
||||
- host: ha-origin.wittyoneoff.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: home-assistant-svc
|
||||
port:
|
||||
number: 80
|
||||
8
apps/home-assistant/kustomization.yaml
Normal file
8
apps/home-assistant/kustomization.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- storage.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ingress.yaml
|
||||
4
apps/home-assistant/namespace.yaml
Normal file
4
apps/home-assistant/namespace.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: home-assistant
|
||||
15
apps/home-assistant/service.yaml
Normal file
15
apps/home-assistant/service.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: home-assistant-svc
|
||||
namespace: home-assistant
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/instance: home-assistant
|
||||
app.kubernetes.io/name: home-assistant
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 8123
|
||||
protocol: TCP
|
||||
30
apps/home-assistant/storage.yaml
Normal file
30
apps/home-assistant/storage.yaml
Normal file
@ -0,0 +1,30 @@
|
||||
# /config lives on GlusterFS (/mnt/gvolume0 mounted on every node).
|
||||
# Known risk: HA recorder DB on gluster + non-atomic renames → occasional
|
||||
# .storage write errors. See homelab notes.
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: home-assistant-pv
|
||||
spec:
|
||||
capacity:
|
||||
storage: 10Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
volumeMode: Filesystem
|
||||
hostPath:
|
||||
path: /mnt/gvolume0/home-assistant
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: home-assistant
|
||||
namespace: home-assistant
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
storageClassName: ""
|
||||
volumeName: home-assistant-pv
|
||||
59
apps/nginx/deployment.yaml
Normal file
59
apps/nginx/deployment.yaml
Normal file
@ -0,0 +1,59 @@
|
||||
# Created via Rancher originally — the workload.user.cattle.io selector label
|
||||
# is immutable on the live object; do not change it.
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
namespace: nginx
|
||||
labels:
|
||||
workload.user.cattle.io/workloadselector: apps.deployment-nginx-nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
workload.user.cattle.io/workloadselector: apps.deployment-nginx-nginx
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
workload.user.cattle.io/workloadselector: apps.deployment-nginx-nginx
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: cpu
|
||||
operator: In
|
||||
values:
|
||||
- arm
|
||||
- key: controller
|
||||
operator: NotIn
|
||||
values:
|
||||
- "true"
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- name: vol-vnbxq
|
||||
mountPath: /usr/share/nginx/html
|
||||
subPath: www
|
||||
- name: vol-vnbxq
|
||||
mountPath: /etc/nginx/conf
|
||||
subPath: conf
|
||||
- name: vol-vnbxq
|
||||
mountPath: /etc/nginx/conf.d
|
||||
subPath: conf.d
|
||||
volumes:
|
||||
- name: vol-vnbxq
|
||||
persistentVolumeClaim:
|
||||
claimName: nginx
|
||||
28
apps/nginx/ingress.yaml
Normal file
28
apps/nginx/ingress.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx
|
||||
namespace: nginx
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- host: www.wittyoneoff.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx
|
||||
port:
|
||||
number: 80
|
||||
- host: www.ramonaajj.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx
|
||||
port:
|
||||
number: 80
|
||||
8
apps/nginx/kustomization.yaml
Normal file
8
apps/nginx/kustomization.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- storage.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ingress.yaml
|
||||
4
apps/nginx/namespace.yaml
Normal file
4
apps/nginx/namespace.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: nginx
|
||||
14
apps/nginx/service.yaml
Normal file
14
apps/nginx/service.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
namespace: nginx
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
workload.user.cattle.io/workloadselector: apps.deployment-nginx-nginx
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
35
apps/nginx/storage.yaml
Normal file
35
apps/nginx/storage.yaml
Normal file
@ -0,0 +1,35 @@
|
||||
# Static site content + nginx config live on GlusterFS:
|
||||
# /mnt/gvolume0/nginx/www → /usr/share/nginx/html
|
||||
# /mnt/gvolume0/nginx/conf → /etc/nginx/conf
|
||||
# /mnt/gvolume0/nginx/conf.d → /etc/nginx/conf.d
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: nginx-gvolume0
|
||||
spec:
|
||||
capacity:
|
||||
storage: 50Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
- ReadOnlyMany
|
||||
- ReadWriteMany
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
volumeMode: Filesystem
|
||||
hostPath:
|
||||
path: /mnt/gvolume0/nginx
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: nginx
|
||||
namespace: nginx
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
- ReadOnlyMany
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 50Gi
|
||||
storageClassName: ""
|
||||
volumeName: nginx-gvolume0
|
||||
86
apps/pihole/deployment-pihole.yaml
Normal file
86
apps/pihole/deployment-pihole.yaml
Normal file
@ -0,0 +1,86 @@
|
||||
# DIVERGENCE from live state (intentional): the live deployment has WEBPASSWORD
|
||||
# as a plaintext env value. Here it comes from the `pihole-admin` secret —
|
||||
# create it (see secret.example.yaml) BEFORE applying this, or the pod will
|
||||
# fail with CreateContainerConfigError.
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: pihole
|
||||
namespace: pihole
|
||||
labels:
|
||||
app: pihole
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: pihole
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 0
|
||||
maxUnavailable: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: pihole
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 1
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: cpu
|
||||
operator: In
|
||||
values:
|
||||
- arm
|
||||
- key: model
|
||||
operator: In
|
||||
values:
|
||||
- raspi
|
||||
containers:
|
||||
- name: pihole
|
||||
image: pihole/pihole:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: TZ
|
||||
value: America/Los_Angeles
|
||||
- name: WEBPASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: pihole-admin
|
||||
key: WEBPASSWORD
|
||||
- name: PIHOLE_UID
|
||||
value: "0"
|
||||
ports:
|
||||
- containerPort: 53
|
||||
protocol: TCP
|
||||
- containerPort: 53
|
||||
protocol: UDP
|
||||
- containerPort: 67
|
||||
protocol: UDP
|
||||
- containerPort: 80
|
||||
protocol: TCP
|
||||
- containerPort: 443
|
||||
protocol: TCP
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: 2Gi
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: gv-pihole
|
||||
mountPath: /etc/pihole
|
||||
- name: gv-pihole-dnsmasq-volume
|
||||
mountPath: /etc/dnsmasq.d
|
||||
volumes:
|
||||
- name: gv-pihole
|
||||
persistentVolumeClaim:
|
||||
claimName: pihole-gvolume0
|
||||
- name: gv-pihole-dnsmasq-volume
|
||||
persistentVolumeClaim:
|
||||
claimName: pihole-dnsmasq-volume
|
||||
54
apps/pihole/deployment-unbound.yaml
Normal file
54
apps/pihole/deployment-unbound.yaml
Normal file
@ -0,0 +1,54 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: unbound
|
||||
namespace: pihole
|
||||
labels:
|
||||
app: unbound
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: unbound
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: unbound
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 1
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: cpu
|
||||
operator: In
|
||||
values:
|
||||
- arm
|
||||
- key: model
|
||||
operator: In
|
||||
values:
|
||||
- raspi
|
||||
containers:
|
||||
- name: unbound
|
||||
image: docker.io/mvance/unbound-rpi:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: svc-53-udp
|
||||
containerPort: 53
|
||||
protocol: UDP
|
||||
- name: svc-53-tcp
|
||||
containerPort: 53
|
||||
protocol: TCP
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 50Mi
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: 1Gi
|
||||
10
apps/pihole/kustomization.yaml
Normal file
10
apps/pihole/kustomization.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
# secret.example.yaml is intentionally NOT listed — copy it to secret.yaml
|
||||
# (gitignored) and apply manually once per cluster.
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- storage.yaml
|
||||
- deployment-pihole.yaml
|
||||
- deployment-unbound.yaml
|
||||
- service.yaml
|
||||
4
apps/pihole/namespace.yaml
Normal file
4
apps/pihole/namespace.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: pihole
|
||||
13
apps/pihole/secret.example.yaml
Normal file
13
apps/pihole/secret.example.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
# Copy to secret.yaml (gitignored), set the real admin password, apply once:
|
||||
# kubectl apply -f secret.yaml
|
||||
# Or create directly without a file:
|
||||
# kubectl create secret generic pihole-admin -n pihole \
|
||||
# --from-literal=WEBPASSWORD='...'
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: pihole-admin
|
||||
namespace: pihole
|
||||
type: Opaque
|
||||
stringData:
|
||||
WEBPASSWORD: CHANGE-ME
|
||||
53
apps/pihole/service.yaml
Normal file
53
apps/pihole/service.yaml
Normal file
@ -0,0 +1,53 @@
|
||||
# pihole-svc is type LoadBalancer → k3s klipper-lb (svclb) binds host ports
|
||||
# 8000/53/67 on the nodes it schedules on. NodePorts are pinned so they
|
||||
# survive re-creation.
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: pihole-svc
|
||||
namespace: pihole
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
externalTrafficPolicy: Cluster
|
||||
selector:
|
||||
app: pihole
|
||||
ports:
|
||||
- name: http-admin
|
||||
port: 8000
|
||||
targetPort: 80
|
||||
nodePort: 31120
|
||||
protocol: TCP
|
||||
- name: tcp-53
|
||||
port: 53
|
||||
targetPort: 53
|
||||
nodePort: 32580
|
||||
protocol: TCP
|
||||
- name: udp-53
|
||||
port: 53
|
||||
targetPort: 53
|
||||
nodePort: 31281
|
||||
protocol: UDP
|
||||
- name: udp-67
|
||||
port: 67
|
||||
targetPort: 67
|
||||
nodePort: 31025
|
||||
protocol: UDP
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: unbound-service
|
||||
namespace: pihole
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: unbound
|
||||
ports:
|
||||
- name: udp-53
|
||||
port: 53
|
||||
targetPort: 53
|
||||
protocol: UDP
|
||||
- name: tcp-53
|
||||
port: 53
|
||||
targetPort: 53
|
||||
protocol: TCP
|
||||
55
apps/pihole/storage.yaml
Normal file
55
apps/pihole/storage.yaml
Normal file
@ -0,0 +1,55 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: pihole-gvolume0-pv
|
||||
spec:
|
||||
capacity:
|
||||
storage: 10Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
volumeMode: Filesystem
|
||||
hostPath:
|
||||
path: /mnt/gvolume0/pihole
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: pihole-dnsmasq-volume-pv
|
||||
spec:
|
||||
capacity:
|
||||
storage: 10Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
volumeMode: Filesystem
|
||||
hostPath:
|
||||
path: /mnt/gvolume0/pihole-dnsmasq-volume
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: pihole-gvolume0
|
||||
namespace: pihole
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
storageClassName: ""
|
||||
volumeName: pihole-gvolume0-pv
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: pihole-dnsmasq-volume
|
||||
namespace: pihole
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
storageClassName: ""
|
||||
volumeName: pihole-dnsmasq-volume-pv
|
||||
78
apps/searxng/deployment.yaml
Normal file
78
apps/searxng/deployment.yaml
Normal file
@ -0,0 +1,78 @@
|
||||
# Helm release searxng-1723974683 (chart searxng-1.0.0) in the *default*
|
||||
# namespace — the release is still recorded in the cluster, so if you ever
|
||||
# `helm upgrade` it, helm and this manifest will fight. Prefer retiring the
|
||||
# helm release (helm delete --no-hooks would delete resources; instead just
|
||||
# treat this repo as the source of truth and never run helm against it).
|
||||
# Names keep the timestamped release suffix because selectors are immutable.
|
||||
# NOTE (faithful to live state): TZ value "America/Los Angeles" is missing the
|
||||
# underscore — an invalid tzdata name, so searxng falls back to UTC. Kept as-is;
|
||||
# fix to America/Los_Angeles when convenient.
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: searxng-1723974683
|
||||
namespace: default
|
||||
labels:
|
||||
app.kubernetes.io/instance: searxng-1723974683
|
||||
app.kubernetes.io/name: searxng
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: searxng-1723974683
|
||||
app.kubernetes.io/name: searxng
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: searxng-1723974683
|
||||
app.kubernetes.io/name: searxng
|
||||
spec:
|
||||
containers:
|
||||
- name: searxng-1723974683
|
||||
image: searxng/searxng:latest
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: AUTOCOMPLETE
|
||||
value: google
|
||||
- name: BASE_URL
|
||||
value: http://searxng.wittyoneoff.com/
|
||||
- name: INSTANCE_NAME
|
||||
value: witty-one-off-searxng
|
||||
- name: TZ
|
||||
value: America/Los Angeles
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8080
|
||||
failureThreshold: 30
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8080
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8080
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
volumeMounts:
|
||||
- name: searxng-config
|
||||
mountPath: /etc/searxng/settings.yml
|
||||
subPath: settings.yml
|
||||
volumes:
|
||||
- name: searxng-config
|
||||
secret:
|
||||
secretName: searxng-1723974683-config
|
||||
33
apps/searxng/ingress.yaml
Normal file
33
apps/searxng/ingress.yaml
Normal file
@ -0,0 +1,33 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: searxng-ingress
|
||||
namespace: default
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
defaultBackend:
|
||||
service:
|
||||
name: searxng-1723974683
|
||||
port:
|
||||
number: 8080
|
||||
rules:
|
||||
- host: searxng.wittyoneoff.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: searxng-1723974683
|
||||
port:
|
||||
number: 8080
|
||||
- host: origin-searxng.wittyoneoff.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: searxng-1723974683
|
||||
port:
|
||||
number: 8080
|
||||
8
apps/searxng/kustomization.yaml
Normal file
8
apps/searxng/kustomization.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
# secret.example.yaml is intentionally NOT listed — copy it to secret.yaml
|
||||
# (gitignored) and apply manually once per cluster.
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ingress.yaml
|
||||
14
apps/searxng/secret.example.yaml
Normal file
14
apps/searxng/secret.example.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
# Copy to secret.yaml (gitignored), set a real secret_key, and apply once:
|
||||
# kubectl apply -f secret.yaml
|
||||
# The secret already exists in the cluster; this file only documents its shape.
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: searxng-1723974683-config
|
||||
namespace: default
|
||||
type: Opaque
|
||||
stringData:
|
||||
settings.yml: |
|
||||
server:
|
||||
secret_key: CHANGE-ME
|
||||
use_default_settings: true
|
||||
18
apps/searxng/service.yaml
Normal file
18
apps/searxng/service.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: searxng-1723974683
|
||||
namespace: default
|
||||
labels:
|
||||
app.kubernetes.io/instance: searxng-1723974683
|
||||
app.kubernetes.io/name: searxng
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/instance: searxng-1723974683
|
||||
app.kubernetes.io/name: searxng
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
69
apps/unified-streaming/deployment.yaml
Normal file
69
apps/unified-streaming/deployment.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
# DIVERGENCE from live state (intentional): the live deployment has
|
||||
# USP_LICENSE_KEY as a plaintext env value. Here it comes from the
|
||||
# `usp-license` secret — create it (see secret.example.yaml) BEFORE applying
|
||||
# this. The Rancher-era selector label is immutable; do not change it.
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: unifiedstreaming
|
||||
namespace: unified-streaming
|
||||
labels:
|
||||
workload.user.cattle.io/workloadselector: apps.deployment-unified-streaming-unifiedstreaming
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
workload.user.cattle.io/workloadselector: apps.deployment-unified-streaming-unifiedstreaming
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: unifiedstreaming
|
||||
workload.user.cattle.io/workloadselector: apps.deployment-unified-streaming-unifiedstreaming
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: cpu
|
||||
operator: In
|
||||
values:
|
||||
- arm
|
||||
- key: controller
|
||||
operator: NotIn
|
||||
values:
|
||||
- "true"
|
||||
containers:
|
||||
- name: unifiedstreaming
|
||||
image: unifiedstreaming/origin:latest
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: USP_LICENSE_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: usp-license
|
||||
key: USP_LICENSE_KEY
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
volumeMounts:
|
||||
- name: vol-apn2a
|
||||
mountPath: /var/www/unified-origin
|
||||
volumes:
|
||||
- name: vol-apn2a
|
||||
persistentVolumeClaim:
|
||||
claimName: unified-streaming-pvc
|
||||
18
apps/unified-streaming/ingress.yaml
Normal file
18
apps/unified-streaming/ingress.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: unified-streaming-ing
|
||||
namespace: unified-streaming
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- host: unified.wittyoneoff.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: unifiedstreaming-svc
|
||||
port:
|
||||
number: 80
|
||||
10
apps/unified-streaming/kustomization.yaml
Normal file
10
apps/unified-streaming/kustomization.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
# secret.example.yaml is intentionally NOT listed — copy it to secret.yaml
|
||||
# (gitignored) and apply manually once per cluster.
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- storage.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ingress.yaml
|
||||
4
apps/unified-streaming/namespace.yaml
Normal file
4
apps/unified-streaming/namespace.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: unified-streaming
|
||||
16
apps/unified-streaming/secret.example.yaml
Normal file
16
apps/unified-streaming/secret.example.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
# Copy to secret.yaml (gitignored), paste the real USP license key, apply once:
|
||||
# kubectl apply -f secret.yaml
|
||||
# Or create directly:
|
||||
# kubectl create secret generic usp-license -n unified-streaming \
|
||||
# --from-literal=USP_LICENSE_KEY='<base64-looking license blob>'
|
||||
# The current key can be read from the live deployment:
|
||||
# kubectl get deploy -n unified-streaming unifiedstreaming \
|
||||
# -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="USP_LICENSE_KEY")].value}'
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: usp-license
|
||||
namespace: unified-streaming
|
||||
type: Opaque
|
||||
stringData:
|
||||
USP_LICENSE_KEY: CHANGE-ME
|
||||
18
apps/unified-streaming/service.yaml
Normal file
18
apps/unified-streaming/service.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
# ClusterIP on purpose: this was type LoadBalancer for ~2 years, but klipper-lb
|
||||
# can never bind host port 80 (svclb-traefik owns it on every node), so the
|
||||
# svclb pods sat Pending forever. Traffic flows through the ingress. Changed
|
||||
# to ClusterIP 2026-07-26.
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: unifiedstreaming-svc
|
||||
namespace: unified-streaming
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: unifiedstreaming
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
32
apps/unified-streaming/storage.yaml
Normal file
32
apps/unified-streaming/storage.yaml
Normal file
@ -0,0 +1,32 @@
|
||||
# Origin content (ism/ismv etc.) on GlusterFS, served by all replicas.
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: unified-streaming-pv
|
||||
spec:
|
||||
capacity:
|
||||
storage: 100Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
- ReadOnlyMany
|
||||
- ReadWriteMany
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
volumeMode: Filesystem
|
||||
hostPath:
|
||||
path: /mnt/gvolume0/unified-streaming
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: unified-streaming-pvc
|
||||
namespace: unified-streaming
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
- ReadOnlyMany
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Gi
|
||||
storageClassName: ""
|
||||
volumeName: unified-streaming-pv
|
||||
75
apps/vaultwarden/deployment.yaml
Normal file
75
apps/vaultwarden/deployment.yaml
Normal file
@ -0,0 +1,75 @@
|
||||
# Originally installed via helm chart vaultwarden-5.1.0 (release record no longer
|
||||
# in cluster); managed as a raw manifest since. Selector labels are immutable —
|
||||
# do not change them.
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: bitwarden-vaultwarden
|
||||
namespace: bitwarden
|
||||
labels:
|
||||
app.kubernetes.io/instance: bitwarden
|
||||
app.kubernetes.io/name: vaultwarden
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: bitwarden
|
||||
app.kubernetes.io/name: vaultwarden
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: bitwarden
|
||||
app.kubernetes.io/name: vaultwarden
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: cpu
|
||||
operator: In
|
||||
values:
|
||||
- arm
|
||||
- key: controller
|
||||
operator: NotIn
|
||||
values:
|
||||
- "true"
|
||||
containers:
|
||||
- name: bitwarden-vaultwarden
|
||||
image: vaultwarden/server:1.35.4
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: DATA_FOLDER
|
||||
value: config
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
- name: websocket
|
||||
containerPort: 3012
|
||||
protocol: TCP
|
||||
startupProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
failureThreshold: 30
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /config
|
||||
volumes:
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: bitwarden-gvolume0
|
||||
23
apps/vaultwarden/ingress.yaml
Normal file
23
apps/vaultwarden/ingress.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: bitwarden
|
||||
namespace: bitwarden
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
defaultBackend:
|
||||
service:
|
||||
name: bitwarden-vaultwarden-svc
|
||||
port:
|
||||
number: 8080
|
||||
rules:
|
||||
- host: pm.wittyoneoff.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: bitwarden-vaultwarden-svc
|
||||
port:
|
||||
number: 8080
|
||||
8
apps/vaultwarden/kustomization.yaml
Normal file
8
apps/vaultwarden/kustomization.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- storage.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ingress.yaml
|
||||
4
apps/vaultwarden/namespace.yaml
Normal file
4
apps/vaultwarden/namespace.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: bitwarden
|
||||
19
apps/vaultwarden/service.yaml
Normal file
19
apps/vaultwarden/service.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: bitwarden-vaultwarden-svc
|
||||
namespace: bitwarden
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/instance: bitwarden
|
||||
app.kubernetes.io/name: vaultwarden
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
- name: websocket
|
||||
port: 3012
|
||||
targetPort: websocket
|
||||
protocol: TCP
|
||||
30
apps/vaultwarden/storage.yaml
Normal file
30
apps/vaultwarden/storage.yaml
Normal file
@ -0,0 +1,30 @@
|
||||
# GlusterFS-backed volume: /mnt/gvolume0 is the gluster mount present on every node.
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: bitwarden-pv
|
||||
spec:
|
||||
capacity:
|
||||
storage: 10Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
- ReadWriteMany
|
||||
- ReadOnlyMany
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
volumeMode: Filesystem
|
||||
hostPath:
|
||||
path: /mnt/gvolume0/bitwarden
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: bitwarden-gvolume0
|
||||
namespace: bitwarden
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
storageClassName: ""
|
||||
volumeName: bitwarden-pv
|
||||
9
kustomization.yaml
Normal file
9
kustomization.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- apps/vaultwarden
|
||||
- apps/searxng
|
||||
- apps/home-assistant
|
||||
- apps/nginx
|
||||
- apps/pihole
|
||||
- apps/unified-streaming
|
||||
Loading…
Reference in New Issue
Block a user