mirror of
https://github.com/rubenhensen/k8scd.git
synced 2026-11-15 14:29:09 +01:00
Compare commits
52
Commits
requests
...
netpol-envoy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8ccbef274 | ||
|
|
0dc69d34f0 | ||
|
|
30540ea579 | ||
|
|
53d74768ad | ||
|
|
b6462fb3a9 | ||
|
|
bed68c70a8 | ||
|
|
3991a8d726 | ||
|
|
648f99c505 | ||
|
|
20bd3a4bca | ||
|
|
6cba1a9363 | ||
|
|
acfce6645e | ||
|
|
0de86dd65c | ||
|
|
d7ffdc05f6 | ||
|
|
c0f8034e14 | ||
|
|
a15a4f7c75 | ||
|
|
bdafcaf1e0 | ||
|
|
03c3196242 | ||
|
|
b072099cc1 | ||
|
|
e10ca337f1 | ||
|
|
c72b08867f | ||
|
|
d85362ce3b | ||
|
|
018f528fb3 | ||
|
|
714712b862 | ||
|
|
7dc13466cb | ||
|
|
9b77596bcb | ||
|
|
fbf2cb2d92 | ||
|
|
122873ca5c | ||
|
|
71449dd185 | ||
|
|
297d2dffa5 | ||
|
|
46117ee658 | ||
|
|
2a5bbc7f44 | ||
|
|
d7b0372f1a | ||
|
|
0ad6ca816c | ||
|
|
3e252799a4 | ||
|
|
bfd5619e9e | ||
|
|
0b38d8dec8 | ||
|
|
083353fb9d | ||
|
|
23f9c5c60a | ||
|
|
a8bf3d5b64 | ||
|
|
618954ae2c | ||
|
|
44dc85d306 | ||
|
|
b60735f6b0 | ||
|
|
3a57e5be48 | ||
|
|
1611f5d1ef | ||
|
|
68b692a112 | ||
|
|
6b2f5855d7 | ||
|
|
c25b8883da | ||
|
|
18f9e2efe6 | ||
|
|
5f5f5b01f0 | ||
|
|
6206ab5a74 | ||
|
|
075b3686c3 | ||
|
|
45d1ea0d93 |
No files matched your search
@@ -12,7 +12,7 @@ spec:
|
|||||||
project: default
|
project: default
|
||||||
source:
|
source:
|
||||||
repoURL: https://charts.jetstack.io
|
repoURL: https://charts.jetstack.io
|
||||||
targetRevision: v1.21.2
|
targetRevision: v1.21.3
|
||||||
chart: cert-manager
|
chart: cert-manager
|
||||||
helm:
|
helm:
|
||||||
parameters:
|
parameters:
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ spec:
|
|||||||
sources:
|
sources:
|
||||||
- chart: external-secrets
|
- chart: external-secrets
|
||||||
repoURL: https://charts.external-secrets.io
|
repoURL: https://charts.external-secrets.io
|
||||||
targetRevision: 2.13.0
|
targetRevision: 2.14.0
|
||||||
helm:
|
helm:
|
||||||
values: |
|
values: |
|
||||||
resources:
|
resources:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ spec:
|
|||||||
project: default
|
project: default
|
||||||
source:
|
source:
|
||||||
repoURL: https://groundhog2k.github.io/helm-charts/
|
repoURL: https://groundhog2k.github.io/helm-charts/
|
||||||
targetRevision: 0.15.0
|
targetRevision: 0.16.7
|
||||||
chart: wordpress
|
chart: wordpress
|
||||||
helm:
|
helm:
|
||||||
values: |
|
values: |
|
||||||
@@ -101,6 +101,23 @@ spec:
|
|||||||
timeoutSeconds: 5
|
timeoutSeconds: 5
|
||||||
failureThreshold: 30
|
failureThreshold: 30
|
||||||
|
|
||||||
|
# apache2.conf includes ports.conf after mods-enabled/*.conf, so the
|
||||||
|
# prefork settings here replace mpm_prefork.conf's MaxRequestWorkers
|
||||||
|
# 150. A worker takes ~45-60 MB (PSS), so 150 never fit in 1Gi: a
|
||||||
|
# crawler asking for 88 pages at once got the container OOMKilled on
|
||||||
|
# 2026-10-25. With 12 workers a burst waits in the listen backlog
|
||||||
|
# instead. Listen is the chart's default (containerPort).
|
||||||
|
apachePortsConfig: |
|
||||||
|
Listen 8000
|
||||||
|
<IfModule mpm_prefork_module>
|
||||||
|
StartServers 4
|
||||||
|
MinSpareServers 2
|
||||||
|
MaxSpareServers 6
|
||||||
|
ServerLimit 12
|
||||||
|
MaxRequestWorkers 12
|
||||||
|
MaxConnectionsPerChild 1000
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
# Custom PHP configuration
|
# Custom PHP configuration
|
||||||
customPhpConfig: |
|
customPhpConfig: |
|
||||||
upload_max_filesize = 64M
|
upload_max_filesize = 64M
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ spec:
|
|||||||
project: default
|
project: default
|
||||||
source:
|
source:
|
||||||
repoURL: https://github.com/kubernetes-csi/external-snapshotter.git
|
repoURL: https://github.com/kubernetes-csi/external-snapshotter.git
|
||||||
targetRevision: v8.2.1
|
targetRevision: v8.7.0
|
||||||
path: deploy/kubernetes/snapshot-controller
|
path: deploy/kubernetes/snapshot-controller
|
||||||
kustomize:
|
kustomize:
|
||||||
patches:
|
patches:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# k3s in Docker, to prove the backups can be restored. Nothing on the NAS
|
# k3s in Docker, to prove the backups can be restored. Nothing on the NAS
|
||||||
# changes:
|
# changes:
|
||||||
# - one backup (backups/<name>/) and the kopia repositories of the chosen
|
# - one backup (backups/<name>/) and the kopia repositories of the chosen
|
||||||
# namespaces (kopia/<namespace>/) are copied read-only from MinIO with
|
# namespaces (kopia/<namespace>/) are copied read-only from the NAS with
|
||||||
# rclone into a Docker volume. A second Velero on the real bucket could
|
# rclone into a Docker volume. A second Velero on the real bucket could
|
||||||
# run kopia maintenance on the same repositories as the cluster's own
|
# run kopia maintenance on the same repositories as the cluster's own
|
||||||
# - `rclone serve s3` serves that copy to a Velero 1.18.2 in k3s, with a
|
# - `rclone serve s3` serves that copy to a Velero 1.18.2 in k3s, with a
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
# Defaults: the newest Completed daily-backup, and ntfy freshrss
|
# Defaults: the newest Completed daily-backup, and ntfy freshrss
|
||||||
# actual-budget. Not gatus: a restored Gatus would start sending alerts.
|
# actual-budget. Not gatus: a restored Gatus would start sending alerts.
|
||||||
#
|
#
|
||||||
# It reads Velero's MinIO key from the cluster (velero/cloud-credentials)
|
# It reads Velero's S3 key from the cluster (velero/velero-s3-credentials)
|
||||||
# into a temporary directory, and the copied backup holds every Secret of
|
# into a temporary directory, and the copied backup holds every Secret of
|
||||||
# the cluster; all of it is deleted at the end. KEEP=1 keeps the test
|
# the cluster; all of it is deleted at the end. KEEP=1 keeps the test
|
||||||
# cluster (kubeconfig in the printed directory) until you run it with
|
# cluster (kubeconfig in the printed directory) until you run it with
|
||||||
@@ -33,8 +33,6 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
PROD_KUBECONFIG=${PROD_KUBECONFIG:-$HOME/.kube/homelab-config}
|
PROD_KUBECONFIG=${PROD_KUBECONFIG:-$HOME/.kube/homelab-config}
|
||||||
MINIO=${MINIO:-http://192.168.1.99:9000}
|
|
||||||
BUCKET=velero-backups
|
|
||||||
PORT=${PORT:-16443}
|
PORT=${PORT:-16443}
|
||||||
K3S=rancher/k3s:v1.34.9-k3s1
|
K3S=rancher/k3s:v1.34.9-k3s1
|
||||||
RCLONE=rclone/rclone:1.75.1
|
RCLONE=rclone/rclone:1.75.1
|
||||||
@@ -73,6 +71,18 @@ wait_for() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Endpoint, region and bucket as Velero itself uses them. The NAS rejects a
|
||||||
|
# request signed for the wrong region (rclone defaults to us-east-1); MinIO
|
||||||
|
# did so with a bare 400 on HEAD.
|
||||||
|
if [[ -n ${FROM_VOLUME:-} ]]; then
|
||||||
|
BUCKET=${BUCKET:-velero-backups}
|
||||||
|
else
|
||||||
|
bsl=$(pk -n velero get backupstoragelocations default -o json)
|
||||||
|
S3URL=${S3URL:-$(jq -r '.spec.config.s3Url' <<<"$bsl")}
|
||||||
|
REGION=${REGION:-$(jq -r '.spec.config.region // "us-east-1"' <<<"$bsl")}
|
||||||
|
BUCKET=${BUCKET:-$(jq -r '.spec.objectStorage.bucket' <<<"$bsl")}
|
||||||
|
fi
|
||||||
|
|
||||||
backup=${1:-}
|
backup=${1:-}
|
||||||
shift || true
|
shift || true
|
||||||
namespaces=("$@")
|
namespaces=("$@")
|
||||||
@@ -91,10 +101,10 @@ if [[ -n ${FROM_VOLUME:-} ]]; then
|
|||||||
docker run --rm -v "$FROM_VOLUME:/from:ro" -v "$VOL:/data" alpine:3.22 cp -a /from/. /data/
|
docker run --rm -v "$FROM_VOLUME:/from:ro" -v "$VOL:/data" alpine:3.22 cp -a /from/. /data/
|
||||||
else
|
else
|
||||||
echo "== copy from the NAS (read-only)"
|
echo "== copy from the NAS (read-only)"
|
||||||
(umask 077; pk -n velero get secret cloud-credentials -o jsonpath='{.data.cloud}' | base64 -d |
|
(umask 077; pk -n velero get secret velero-s3-credentials -o jsonpath='{.data.cloud}' | base64 -d |
|
||||||
awk -F' *= *' '/aws_access_key_id/ {print "RCLONE_CONFIG_NAS_ACCESS_KEY_ID="$2}
|
awk -F' *= *' '/aws_access_key_id/ {print "RCLONE_CONFIG_NAS_ACCESS_KEY_ID="$2}
|
||||||
/aws_secret_access_key/ {print "RCLONE_CONFIG_NAS_SECRET_ACCESS_KEY="$2}' > "$WORK/nas.env"
|
/aws_secret_access_key/ {print "RCLONE_CONFIG_NAS_SECRET_ACCESS_KEY="$2}' > "$WORK/nas.env"
|
||||||
printf 'RCLONE_CONFIG_NAS_TYPE=s3\nRCLONE_CONFIG_NAS_PROVIDER=Minio\nRCLONE_CONFIG_NAS_ENDPOINT=%s\n' "$MINIO" >> "$WORK/nas.env")
|
printf 'RCLONE_CONFIG_NAS_TYPE=s3\nRCLONE_CONFIG_NAS_PROVIDER=Other\nRCLONE_CONFIG_NAS_ENDPOINT=%s\nRCLONE_CONFIG_NAS_REGION=%s\n' "$S3URL" "$REGION" >> "$WORK/nas.env")
|
||||||
copy() { docker run --rm --env-file "$WORK/nas.env" -v "$VOL:/data" "$RCLONE" copy --stats-one-line "nas:$BUCKET/$1" "/data/$BUCKET/$1"; }
|
copy() { docker run --rm --env-file "$WORK/nas.env" -v "$VOL:/data" "$RCLONE" copy --stats-one-line "nas:$BUCKET/$1" "/data/$BUCKET/$1"; }
|
||||||
copy "backups/$backup"
|
copy "backups/$backup"
|
||||||
for ns in "${namespaces[@]}"; do copy "kopia/$ns"; done
|
for ns in "${namespaces[@]}"; do copy "kopia/$ns"; done
|
||||||
|
|||||||
@@ -123,15 +123,13 @@ D. auth.scoutingmbg.nl (2026-10-25)
|
|||||||
|
|
||||||
## Open points
|
## Open points
|
||||||
|
|
||||||
- DNS (2026-10-24): the group's DNS host (ns1/ns2.webhosted.nl) changed
|
- DNS (2026-10-24/25): the group's DNS host (ns1/ns2.webhosted.nl) pointed
|
||||||
foto.scoutingmbg.nl from a CNAME to `mbg.hensen.io` (the home IP) to a
|
scoutingmbg.nl, www, foto and auth at node001 (rubenhensen.nl); those are
|
||||||
CNAME to `rubenhensen.nl`, which is node001 on Hetzner. Only ns2 has it
|
all the records it moved, and node001 tunnels all four home. ns1 lagged
|
||||||
(serial 2026102471); ns1 is stuck on serial 2026060640 from June and
|
behind ns2 (a zone serial from June) until 2026-10-24 16:50 UTC. Nothing
|
||||||
still answers `mbg.hensen.io`. node001 now tunnels foto.scoutingmbg.nl to
|
uses mbg.hensen.io anymore, so hensen.io can expire. The SPF record's
|
||||||
the home cluster like its other hosts, so both answers work. Keep
|
`a` now means node001, which is fine: the WordPress site sends no mail
|
||||||
hensen.io until ns1 has caught up; ask the DNS host to fix ns1, and
|
and the group's mail goes through Google (`mx`)
|
||||||
decide whether foto should stay behind node001 or point straight home
|
|
||||||
(CNAME `scoutingmbg.nl`)
|
|
||||||
- Public shared links become public for real once sol-auth is gone.
|
- Public shared links become public for real once sol-auth is gone.
|
||||||
- Photos leiding back up from their phones stay private until they add
|
- Photos leiding back up from their phones stay private until they add
|
||||||
them to a shared album.
|
them to a shared album.
|
||||||
@@ -38,14 +38,15 @@ between them live.
|
|||||||
|
|
||||||
## Before: shrink what has to move
|
## Before: shrink what has to move
|
||||||
|
|
||||||
- [ ] Delete stale snapshots. 188 snapshots hold 334 GiB, 214 GiB of it in
|
- [x] Delete stale snapshots. 188 snapshots hold 334 GiB, 214 GiB of it in
|
||||||
snapshots from before October 2026, nearly all on immich-claim
|
snapshots from before October 2026, nearly all on immich-claim
|
||||||
(pvc-75d269dd): a 72.5 GiB one from 2024-12-20, 71.3 GiB from
|
(pvc-75d269dd): a 72.5 GiB one from 2024-12-20, 71.3 GiB from
|
||||||
2025-03-02, daily ones from 2025-03-13 to 03-20 (a recurring job that no
|
2025-03-02, daily ones from 2025-03-13 to 03-20 (a recurring job that no
|
||||||
longer exists), 56.6 GiB from 2026-01-20. The Longhorn backups on the NAS
|
longer exists), 56.6 GiB from 2026-01-20. The Longhorn backups on the NAS
|
||||||
don't depend on them. Deleting a snapshot merges it into the next one,
|
don't depend on them. Deleting a snapshot merges it into the next one,
|
||||||
which writes a lot, so do this in a quiet hour, one volume at a time.
|
which writes a lot, so do this in a quiet hour, one volume at a time.
|
||||||
Happened on 2026-10-24 (who started it isn't in the logs): immich-claim's
|
Happened on 2026-10-24, from a Claude Code session at Ruben's request
|
||||||
|
(the 45 snapshots from before October 2026, small volumes first): immich-claim's
|
||||||
old snapshots were removed at 14:43 UTC and Longhorn merged them one
|
old snapshots were removed at 14:43 UTC and Longhorn merged them one
|
||||||
after another, about 45 minutes each, until 23:15. For those 8.5 hours
|
after another, about 45 minutes each, until 23:15. For those 8.5 hours
|
||||||
sda ran 250-440 writes/s and 200-400 reads/s at 6-20 ms per write on
|
sda ran 250-440 writes/s and 200-400 reads/s at 6-20 ms per write on
|
||||||
|
|||||||
+139
-64
@@ -10,10 +10,11 @@ One node: opti1, an OptiPlex 5060 with an i5-8600T, 32 GB and one 1 TB
|
|||||||
SATA SSD that is 72% full. microk8s v1.36.5 from 1.36/edge, snap held.
|
SATA SSD that is 72% full. microk8s v1.36.5 from 1.36/edge, snap held.
|
||||||
35 Argo CD apps (with metrics-server), all Synced/Healthy; every Longhorn
|
35 Argo CD apps (with metrics-server), all Synced/Healthy; every Longhorn
|
||||||
volume has one replica. authentik 2026.8.5, Vault 2.2.1. Backups go to the
|
volume has one replica. authentik 2026.8.5, Vault 2.2.1. Backups go to the
|
||||||
NAS (192.168.1.99): Longhorn to NFS at 00:00 (7 kept), Velero to MinIO at
|
NAS (192.168.1.99): Longhorn to NFS at 00:00 (7 kept), Velero to Garage
|
||||||
01:00 (daily kept 30 days, weekly 90 days). Gatus on s1 (node001, Hetzner)
|
(MinIO until 2026-10-26) at 01:00 (daily kept 30 days, weekly 90 days).
|
||||||
and s2 (home) watch each other and alert through ntfy; s2 also gets an
|
Gatus on s1 (node001, Hetzner) and s2 (home) watch each other and alert
|
||||||
hourly report on the backups and Vault's seal from inside the cluster.
|
through ntfy; s2 also gets an hourly report from inside the cluster on the
|
||||||
|
backups, Vault's seal, the Argo CD apps and the CNPG clusters.
|
||||||
Every app namespace has NetworkPolicies, and CI renders and validates all
|
Every app namespace has NetworkPolicies, and CI renders and validates all
|
||||||
apps on every PR. node001 runs mail and tunnels the public hosts home.
|
apps on every PR. node001 runs mail and tunnels the public hosts home.
|
||||||
|
|
||||||
@@ -33,8 +34,9 @@ docs/longhorn-second-ssd.md:
|
|||||||
- [ ] The upgrade's dqlite and backend copies in `/root/` on opti1
|
- [ ] The upgrade's dqlite and backend copies in `/root/` on opti1
|
||||||
(`backend-*.tar`, `dqlite-pre-*`)
|
(`backend-*.tar`, `dqlite-pre-*`)
|
||||||
- [ ] Log in to immich as a jeugdlid (quota 0)
|
- [ ] Log in to immich as a jeugdlid (quota 0)
|
||||||
- [ ] Back to 1.36/stable once it is at v1.36.5 or later. The snap is held,
|
- [ ] Back to 1.36/stable once it is at v1.36.5 or later (still v1.36.2 on
|
||||||
so microk8s gets no updates at all until someone refreshes it
|
2026-10-25). The snap is held, so microk8s gets no updates at all until
|
||||||
|
someone refreshes it
|
||||||
- [ ] Second SSD, docs/longhorn-second-ssd.md
|
- [ ] Second SSD, docs/longhorn-second-ssd.md
|
||||||
- [ ] Optional: `MACHINE_LEARNING_MODEL_REVISION=v2` for immich ML
|
- [ ] Optional: `MACHINE_LEARNING_MODEL_REVISION=v2` for immich ML
|
||||||
|
|
||||||
@@ -48,8 +50,9 @@ group's ns2: ns1 is stuck on an old serial and still answers
|
|||||||
mbg.hensen.io (docs/immich-scouts-online.md).
|
mbg.hensen.io (docs/immich-scouts-online.md).
|
||||||
|
|
||||||
- [x] Expiry date: 2027-01-09 (whois, 2026-10-25; registrar Key-Systems)
|
- [x] Expiry date: 2027-01-09 (whois, 2026-10-25; registrar Key-Systems)
|
||||||
- [ ] Have the group's DNS host fix ns1; keep hensen.io until it answers
|
- [x] ns1 caught up on 2026-10-24 16:50 UTC; both ns1 and ns2 answer
|
||||||
rubenhensen.nl too
|
`rubenhensen.nl` for foto (checked 2026-10-25), and nothing in the
|
||||||
|
cluster uses hensen.io anymore. hensen.io can expire
|
||||||
|
|
||||||
### 2. Off-site backups
|
### 2. Off-site backups
|
||||||
|
|
||||||
@@ -60,24 +63,25 @@ belong to the group.
|
|||||||
There is no off-site copy (2026-10-25). The plan: swap S3 buckets with a
|
There is no off-site copy (2026-10-25). The plan: swap S3 buckets with a
|
||||||
friend.
|
friend.
|
||||||
|
|
||||||
- [ ] Ours to his bucket: Hyper Backup on the Synology, destination "S3
|
- [ ] Ours to his bucket: `rclone sync` from Garage's `velero-backups`
|
||||||
Storage" with his endpoint as a custom server. Client-side encryption
|
(~190 GB) to his bucket through an rclone `crypt` remote, in Docker on
|
||||||
on: the kopia repositories use Velero's default password
|
the NAS like the copy from MinIO (see 5). Encrypted on our side: the
|
||||||
(`static-passw0rd`), so without it he could read everything, the
|
kopia repositories use Velero's default password (`static-passw0rd`),
|
||||||
group's photos included. Keep the encryption key next to the Vault
|
so without it he could read everything, the group's photos included.
|
||||||
unseal key. Source: the shared folder with MinIO's `velero-backups`
|
Keep the crypt passwords next to the Vault unseal key. Daily after the
|
||||||
bucket (~150-200 GB). Daily after the Velero backup, Smart Recycle,
|
Velero backup, ntfy on failure. Hyper Backup, the earlier plan, backs up
|
||||||
mail on failure
|
shared folders; Garage keeps its objects as blocks plus a metadata
|
||||||
|
database, and a copy of that folder taken while Garage runs is not
|
||||||
|
consistent
|
||||||
- [ ] Needed from him: endpoint, which S3 server he runs, space (~200 GB
|
- [ ] Needed from him: endpoint, which S3 server he runs, space (~200 GB
|
||||||
now, 500 GB to grow), and versioning or object lock on the bucket, so
|
now, 500 GB to grow), and versioning or object lock on the bucket, so
|
||||||
whoever takes over the NAS can't delete the off-site copy as well
|
whoever takes over the NAS can't delete the off-site copy as well
|
||||||
- [ ] His backups with us: Garage (not MinIO, see 5) in Container Manager
|
- [ ] His backups with us: a bucket and key for him, with a quota, in the
|
||||||
on the Synology, in its own shared folder, with a bucket and key for him
|
Garage on the NAS (`nas/garage`, see 5). Only the S3 API public, as
|
||||||
and a quota. Only the S3 API public, as `s3.rubenhensen.nl` through
|
`s3.rubenhensen.nl` through node001 and Envoy with a cert-manager
|
||||||
node001 and Envoy with a cert-manager certificate; the admin API stays
|
certificate; the admin API stays on the LAN. The cluster side (DNS,
|
||||||
on the LAN. The cluster side (DNS, node001 tunnel, Certificate,
|
node001 tunnel, Certificate, HTTPRoute) goes in git. Needed: how much
|
||||||
HTTPRoute) goes in git. Needed: how much space he wants, free space on
|
space he wants, free space on the NAS
|
||||||
the NAS
|
|
||||||
- [ ] Decide which copy of the immich library goes off-site (see 5)
|
- [ ] Decide which copy of the immich library goes off-site (see 5)
|
||||||
- [ ] A restore test that runs on a schedule. `backup-testing/` predates
|
- [ ] A restore test that runs on a schedule. `backup-testing/` predates
|
||||||
Envoy Gateway and still rewrites `*.hensen.io` and bypasses sol-auth.
|
Envoy Gateway and still rewrites `*.hensen.io` and bypasses sol-auth.
|
||||||
@@ -97,8 +101,20 @@ friend.
|
|||||||
ReadOnly location: data restored byte for byte (same sha256), deployment
|
ReadOnly location: data restored byte for byte (same sha256), deployment
|
||||||
available, everything removed afterwards. Two pitfalls found on the way
|
available, everything removed afterwards. Two pitfalls found on the way
|
||||||
are in the script's header. Not yet run against the real backups:
|
are in the script's header. Not yet run against the real backups:
|
||||||
- [ ] Run `backup-testing/restore-test.sh` (it reads the MinIO key from
|
- [x] Ran `backup-testing/restore-test.sh` on 2026-10-25 against
|
||||||
the cluster), then schedule it
|
daily-backup-20261025010027 (ntfy, freshrss, actual-budget): every pod
|
||||||
|
volume came back with its backed-up byte count (actual-budget 8.4 MB,
|
||||||
|
freshrss 293 MB, ntfy 180 KB) and all deployments became available. The
|
||||||
|
first run failed on a 400 from MinIO: rclone signed for us-east-1 while
|
||||||
|
MinIO runs region `minio`; the script now reads endpoint, region and
|
||||||
|
bucket from Velero's location. Copying freshrss's kopia repository took
|
||||||
|
14 of the ~17 minutes
|
||||||
|
- [x] Ran it again on 2026-10-26 against the copy in Garage
|
||||||
|
(daily-backup-20261026010028, same namespaces), before Velero switched
|
||||||
|
over (see 5): passed, in 5 minutes instead of 17
|
||||||
|
- [ ] Run it on a schedule. It needs Docker and an S3 key, so not in the
|
||||||
|
cluster itself: the Mac (launchd) or node001, ideally with a read-only
|
||||||
|
Garage key (`garage key create`, `garage bucket allow --read`)
|
||||||
|
|
||||||
### 3. authentik 2026.2.1 → 2026.8.5
|
### 3. authentik 2026.2.1 → 2026.8.5
|
||||||
|
|
||||||
@@ -153,8 +169,8 @@ unseals it.
|
|||||||
- The OIDC job called `vault auth tune -listing_visibility`, a flag that
|
- The OIDC job called `vault auth tune -listing_visibility`, a flag that
|
||||||
doesn't exist, so the OIDC mount's listing visibility was never set.
|
doesn't exist, so the OIDC mount's listing visibility was never set.
|
||||||
Fixed; the job now uses the 2.2.1 CLI instead of 1.15
|
Fixed; the job now uses the 2.2.1 CLI instead of 1.15
|
||||||
- [ ] There is one unseal key (1 share, threshold 1). Keep it in two
|
- [x] There is one unseal key (1 share, threshold 1), kept safe outside
|
||||||
places outside this cluster; without it the data in Vault is lost
|
this cluster (2026-10-25); without it the data in Vault is lost
|
||||||
- [x] Unsealing is in the SSD install steps
|
- [x] Unsealing is in the SSD install steps
|
||||||
- [ ] After a week without problems: delete the three snapshots
|
- [ ] After a week without problems: delete the three snapshots
|
||||||
|
|
||||||
@@ -181,7 +197,7 @@ Findings from 2026-10-25:
|
|||||||
- Velero copies the CNPG and MariaDB data directories file by file while
|
- Velero copies the CNPG and MariaDB data directories file by file while
|
||||||
the databases run, which is not a consistent copy. The Longhorn backups
|
the databases run, which is not a consistent copy. The Longhorn backups
|
||||||
start from a snapshot and are crash-consistent. CNPG can back up to
|
start from a snapshot and are crash-consistent. CNPG can back up to
|
||||||
MinIO itself (barman cloud plugin), with point-in-time recovery.
|
Garage itself (barman cloud plugin), with point-in-time recovery.
|
||||||
- The PartiallyFailed daily backup of 10-23 (emptyDirs of Velero's own
|
- The PartiallyFailed daily backup of 10-23 (emptyDirs of Velero's own
|
||||||
maintenance job pods) was fixed that afternoon by
|
maintenance job pods) was fixed that afternoon by
|
||||||
`velero/skip-emptydir-policy.yaml`.
|
`velero/skip-emptydir-policy.yaml`.
|
||||||
@@ -189,10 +205,31 @@ Findings from 2026-10-25:
|
|||||||
- [ ] Whether the immich library stays in Velero. It stays for now
|
- [ ] Whether the immich library stays in Velero. It stays for now
|
||||||
(2026-10-25); decide again with the off-site copy (2)
|
(2026-10-25); decide again with the off-site copy (2)
|
||||||
- [ ] CNPG backups through the barman cloud plugin
|
- [ ] CNPG backups through the barman cloud plugin
|
||||||
- [ ] Velero's target, MinIO on the NAS, is no longer maintained: no
|
- [x] Velero's target, MinIO on the NAS, is no longer maintained: no
|
||||||
community images since October 2025, maintenance mode since December
|
community images since October 2025, maintenance mode since December
|
||||||
2025, and the repository is archived. Move Velero to the Garage from 2
|
2025, and the repository is archived. Since 2026-10-26 Velero backs up
|
||||||
and switch MinIO off
|
to Garage on the same NAS (`nas/garage`, PR #35). The bucket was copied
|
||||||
|
with rclone (185 GiB in about 6 hours, then a catch-up sync just before
|
||||||
|
the switch), so the existing backups and kopia repositories carried on:
|
||||||
|
after the switch all 55 backups were still listed, and a manual backup
|
||||||
|
of ntfy found the existing repository and used the night's snapshot as
|
||||||
|
its parent. Two things made the NAS slow during the copy: Garage on
|
||||||
|
LMDB with `metadata_fsync`, ~270 disk writes/s while idle (now SQLite,
|
||||||
|
see `nas/garage/garage.toml`), and MinIO's scanner, which kept one disk
|
||||||
|
at 97% busy. The scanner is now at `slowest` (`mc admin config set local
|
||||||
|
scanner speed=slowest` in the MinIO container); it may have slowed
|
||||||
|
Velero's backups to MinIO as well
|
||||||
|
- [ ] Switch MinIO off once Velero has run on Garage for a week (from
|
||||||
|
2026-11-02): stop the container, then delete `/volume1/minio` (343 GiB)
|
||||||
|
and the hand-made Secret `velero/cloud-credentials` (MinIO's key). Only
|
||||||
|
MinIO still has the kopia repositories of namespaces that are gone
|
||||||
|
(~156 GB: nextcloud, ttrss, whisper-priv, whisper-webui, blog, ghost,
|
||||||
|
mbg-wordpress, sogo, sogo-mail, outrun-game, owntracks); decide first
|
||||||
|
whether any of them is worth keeping
|
||||||
|
- [ ] Garage's SQLite WAL (`meta/db.sqlite-wal`) was 1.7 GB on
|
||||||
|
2026-10-26 next to a 270 MB database, and going by the folders in
|
||||||
|
`meta/snapshots`, each 6-hourly metadata snapshot takes about 70
|
||||||
|
minutes. Not growing; check again after a week of normal use
|
||||||
|
|
||||||
### 6. Monitoring
|
### 6. Monitoring
|
||||||
|
|
||||||
@@ -265,8 +302,42 @@ Counted 2026-10-25 over the 73 running containers:
|
|||||||
external-secrets, envoy-gateway-system, velero, mariadb-system, dns,
|
external-secrets, envoy-gateway-system, velero, mariadb-system, dns,
|
||||||
default) still have none; `default` can reach the LAN and everything
|
default) still have none; `default` can reach the LAN and everything
|
||||||
without a policy
|
without a policy
|
||||||
- [ ] Requests and memory limits, the big ones first (immich, authentik,
|
- [x] Requests: PR #33, merged 2026-10-25 11:23 UTC, for everything that
|
||||||
Argo CD's repo-server)
|
had none except the Vault server, from `kubectl top` every 5 minutes
|
||||||
|
from 03:13 to 08:40 UTC (highest memory plus 10%, CPU at p95, 10m
|
||||||
|
floor). Rolled out in about eight minutes; both CNPG instances
|
||||||
|
restarted and came back healthy, the OIDC endpoints, the logins and
|
||||||
|
immich answered, no OOM kills. The node now has 3770m CPU (62%, of which
|
||||||
|
1440m is Longhorn's two instance managers) and 9158Mi memory (28%)
|
||||||
|
requested. Still without requests: Longhorn (22 containers, its chart),
|
||||||
|
the Vault servers, the LDAP outpost, immich's PG14 cluster and three in
|
||||||
|
kube-system
|
||||||
|
- [ ] Memory limits, once there are daytime numbers: the night ones miss
|
||||||
|
immich's machine learning at work (it idles at 234Mi)
|
||||||
|
- [x] WordPress (scoutingmbg.nl) was OOMKilled at 08:27:59 UTC on
|
||||||
|
2026-10-25, its third restart: a crawler asked for 88 pages and assets
|
||||||
|
within a few seconds (normally ~6 a minute), Apache started a PHP worker
|
||||||
|
for each (~45-60 MB each, `MaxRequestWorkers` was the default 150), and
|
||||||
|
the container went past its 1Gi limit. Unrelated to the
|
||||||
|
NetworkPolicies. Now capped at 12 workers through the chart's
|
||||||
|
`apachePortsConfig` (ports.conf comes after mods-enabled/*.conf), checked
|
||||||
|
with `apache2ctl -t` in the same image first. Replaying the burst (90
|
||||||
|
page requests, 40 at a time): all 200, at most 13 Apache processes and
|
||||||
|
572Mi, no restart; the price is waiting (4.3s median, up to ~22s at the
|
||||||
|
peak) instead of a crash
|
||||||
|
- [ ] Optional, no plugins wanted on WordPress: a small nginx cache in the
|
||||||
|
cluster between Envoy and WordPress that keeps anonymous GETs for a few
|
||||||
|
minutes and passes anything with a WordPress cookie through. A page
|
||||||
|
renders in 0.4-0.7s when the site is quiet and OPcache is on, so the
|
||||||
|
waiting in a burst is queueing; the cap alone already prevents the crash
|
||||||
|
- [ ] Fewer plugins on the site (17 now; up to whoever runs it):
|
||||||
|
`litespeed-cache` only caches pages on a LiteSpeed server, so it does
|
||||||
|
little here, `wordpress-importer` is usually for a one-off import, and
|
||||||
|
`code-snippets` runs PHP from the dashboard
|
||||||
|
- [ ] Rate limiting per visitor in Envoy needs the client's address, which
|
||||||
|
node001 loses: it passes TLS through, so every request arrives from
|
||||||
|
46.224.26.65. That needs the PROXY protocol from node001's nginx to Envoy
|
||||||
|
(ClientTrafficPolicy `enableProxyProtocol`)
|
||||||
- [x] Probes for gitea (`/api/healthz`), freshrss (TCP, since
|
- [x] Probes for gitea (`/api/healthz`), freshrss (TCP, since
|
||||||
mod_auth_openidc answers 401 to non-browsers) and serpbear (`/login`)
|
mod_auth_openidc answers 401 to non-browsers) and serpbear (`/login`)
|
||||||
(2026-10-25). Liveness only after 5 failures 30s apart, to ride out the
|
(2026-10-25). Liveness only after 5 failures 30s apart, to ride out the
|
||||||
@@ -277,8 +348,10 @@ Counted 2026-10-25 over the 73 running containers:
|
|||||||
|
|
||||||
### 8. Renovate and CI
|
### 8. Renovate and CI
|
||||||
|
|
||||||
10 Renovate PRs are open, the oldest from February, and nothing checks a
|
On 2026-10-25 10 Renovate PRs were open, the oldest from February, and
|
||||||
PR before Argo CD applies it.
|
nothing checked a PR before Argo CD applied it. All of them have since been
|
||||||
|
merged or closed (below), none are open (checked 2026-10-25), and CI checks
|
||||||
|
every PR.
|
||||||
|
|
||||||
- [x] `.github/workflows/validate.yaml` runs `tools/ci/validate.py` on
|
- [x] `.github/workflows/validate.yaml` runs `tools/ci/validate.py` on
|
||||||
every PR and push (2026-10-25): it renders all 36 apps the way Argo CD
|
every PR and push (2026-10-25): it renders all 36 apps the way Argo CD
|
||||||
@@ -314,34 +387,30 @@ PR before Argo CD applies it.
|
|||||||
cover every app folder, a regex manager reads `imageName` in
|
cover every app folder, a regex manager reads `imageName` in
|
||||||
`*/postgresql-cluster*.yaml`, and major PostgreSQL updates are off
|
`*/postgresql-cluster*.yaml`, and major PostgreSQL updates are off
|
||||||
(those need pg_upgrade or a dump). Expect a batch of new PRs
|
(those need pg_upgrade or a dump). Expect a batch of new PRs
|
||||||
(2026-10-25; `renovate-config-validator` passes)
|
(2026-10-25). They only come once fewer than 10 Renovate PRs are open
|
||||||
- [ ] The open PRs, all validated on 2026-10-25 against main as it is now
|
(`config:recommended`'s `prConcurrentLimit`); until then they wait
|
||||||
(rebases requested, so the checks show on the PRs once Renovate has
|
under "Rate-Limited" on the Dependency Dashboard (#2)
|
||||||
pushed). Merging is yours; merging deploys:
|
- The other folders were left out with a `(?!…)` lookahead. Renovate
|
||||||
- #28 cert-manager v1.21.3: security release, "All users should
|
compiles these patterns with RE2, which has no lookahead, so it
|
||||||
upgrade" (drops the compromised eMudhra root, fixes an ARI renewal
|
rejected the config and stopped opening PRs (#32). Each manager now
|
||||||
loop and a private key copy path). Merge first
|
takes every folder and skips the others through its own
|
||||||
- #24 external-snapshotter v8.2.1 → v8.7.0: 8.6 moved VolumeGroupSnapshot
|
`ignorePaths`. Renovate 44.181.6 with RE2 loaded fails the old config
|
||||||
to v1 and 8.7 turns its feature gate on; the cluster already serves the
|
with #32's error and passes the new one, and its matcher picks the
|
||||||
v1 CRDs. Merge; close #15
|
same 166 files as the old pattern did
|
||||||
- #25 WordPress chart 0.15.0 → 0.16.7: the image is pinned
|
- [x] Merged 2026-10-25 and checked after the sync: #28 cert-manager
|
||||||
(7.1.3-php8.3-apache), so the render only changes labels and
|
v1.21.3 (security release; all three pods on v1.21.3), #24
|
||||||
checksums (a pod restart). Merge; #19 (0.15.1) is superseded
|
external-snapshotter v8.7.0 (its manifests ship the v8.6.0 image, as
|
||||||
- #26 Gitea 1.27.3 → 28.2.0 (Gitea renumbered after 1.27): security
|
v8.2.1 shipped v8.2.0; both replicas up, no errors), #25 WordPress chart
|
||||||
fixes; the breaking changes are to egress host lists
|
0.16.7 (image unchanged, site 200), #31 python 3.15.1 (the cluster check
|
||||||
(`ALLOWED_DOMAINS` and friends), none of which are set here. Database
|
ran clean on it). Closed: #19, #29, and #15 by Renovate. Later the same
|
||||||
migrations can't be undone, so merge after a fresh backup
|
day: #23 ntfy v2.34.0, #27 go-dqlite v3.0.4, #33 (see 7) and #26 Gitea
|
||||||
- #23 ntfy v2.28.0 → v2.34.0: six releases in 17 days; 2.31.0 had a
|
28.2.0 (migrations up to 357 ran clean, `/api/healthz` 200; no backup of
|
||||||
database load bug that ntfy.sh rolled back (fixed in 2.31.1). No
|
its own beforehand, the nightly ones are the way back)
|
||||||
breaking migrations. Fine, or wait a few days for 2.34 to settle
|
- [x] #17 external-secrets 2.14.0, merged 2026-10-25 11:50 UTC after a
|
||||||
- #17 external-secrets 2.13.0 → 2.14.0: no breaking changes in the
|
manual rebase (#33 had added values on the next line, which git counts
|
||||||
notes; every secret goes through it, so merge when there's time to
|
as a conflict). All three pods on v2.14.0, the store Ready, and 24 of 25
|
||||||
watch a force-sync
|
ExternalSecrets refreshed on a force-sync; the 25th is the generated
|
||||||
- #27 go-dqlite v3.0.4 for `tools/dqlite-vacuum`: builds; harmless
|
`gatus/backup-check-token`, which never refreshes by design
|
||||||
- #29 yaml.v3 for the same tool: builds, but the code imports yaml.v2,
|
|
||||||
so it only adds an unused requirement. Close
|
|
||||||
- #31 python 3.15.1 for `gatus/backup-check.yaml`: the check's tests
|
|
||||||
(made-up data for every failure) pass on that image. Merge
|
|
||||||
|
|
||||||
### 9. One node
|
### 9. One node
|
||||||
|
|
||||||
@@ -357,6 +426,12 @@ step after that, if downtime starts to matter.
|
|||||||
`backup-testing/test-velero-backup.sh.bak` (an older copy); the 15
|
`backup-testing/test-velero-backup.sh.bak` (an older copy); the 15
|
||||||
completed kopia maintenance jobs of repositories that no longer exist
|
completed kopia maintenance jobs of repositories that no longer exist
|
||||||
(nextcloud, owntracks, sogo, sogo-mail, ttrss)
|
(nextcloud, owntracks, sogo, sogo-mail, ttrss)
|
||||||
|
- [ ] freshrss mounts `freshrss-claim2` and `freshrss-claim3` as whole
|
||||||
|
volumes over the file paths `data/config.custom.php` and
|
||||||
|
`data/config-user.custom.php`, so FreshRSS sees two empty directories
|
||||||
|
there and the volumes hold nothing (0 bytes in every backup). Drop both
|
||||||
|
mounts and the two PVCs, or make them ConfigMap files if custom config
|
||||||
|
is wanted
|
||||||
- [ ] `kubectl delete namespace secrets`, empty since its app went
|
- [ ] `kubectl delete namespace secrets`, empty since its app went
|
||||||
- [ ] 41 Failed Velero backups: the dailies from 2026-09-26 to 10-21 and
|
- [ ] 41 Failed Velero backups: the dailies from 2026-09-26 to 10-21 and
|
||||||
the weeklies since 08-02. Velero doesn't restore from a Failed backup;
|
the weeklies since 08-02. Velero doesn't restore from a Failed backup;
|
||||||
@@ -398,7 +473,7 @@ critical / high:
|
|||||||
| tensorchord/cloudnative-vectorchord:17.11-1.1.1 | 17 | 136 | immich's database; look for a newer tag |
|
| tensorchord/cloudnative-vectorchord:17.11-1.1.1 | 17 | 136 | immich's database; look for a newer tag |
|
||||||
| longhornio/longhorn-instance-manager:v1.7.3 | 11 | 159 | still serves 16 volumes, see below |
|
| longhornio/longhorn-instance-manager:v1.7.3 | 11 | 159 | still serves 16 volumes, see below |
|
||||||
| actualbudget/actual-server:26.6.0 | 8 | 83 | chart 1.8.9; Renovate now covers it |
|
| actualbudget/actual-server:26.6.0 | 8 | 83 | chart 1.8.9; Renovate now covers it |
|
||||||
| gitea/gitea:1.27.3 | 8 | 43 | PR #26 (28.2.0) |
|
| gitea/gitea:1.27.3 | 8 | 43 | now 28.2.0 (PR #26, 2026-10-25); not scanned again |
|
||||||
| immich-server / machine-learning v3.3.5 | 7, 7 | 113, 78 | a newer immich release |
|
| immich-server / machine-learning v3.3.5 | 7, 7 | 113, 78 | a newer immich release |
|
||||||
| towfiqi/serpbear:3.1.0 | 6 | 77 | a newer release |
|
| towfiqi/serpbear:3.1.0 | 6 | 77 | a newer release |
|
||||||
| rubenhensen/bolletjestrui:main-10268f1 | 5 | 76 | rebuild on a current base image |
|
| rubenhensen/bolletjestrui:main-10268f1 | 5 | 76 | rebuild on a current base image |
|
||||||
|
|||||||
@@ -282,6 +282,13 @@ whatever time of day the hourly schedule had drifted to.
|
|||||||
migrations): 19-33 ms per write, single 2s windows near 2 s per write,
|
migrations): 19-33 ms per write, single 2s windows near 2 s per write,
|
||||||
pressure 78%. The CSI sidecars and Longhorn's global manager lost
|
pressure 78%. The CSI sidecars and Longhorn's global manager lost
|
||||||
their leases at 01:31
|
their leases at 01:31
|
||||||
|
Per minute against dqlite's own errors (2026-10-24 12:35 to 2026-10-25
|
||||||
|
11:30, leaving out the CNPG range cancels): all 67 minutes with errors
|
||||||
|
had sda ~100% busy, single 2s windows at 200 ms to 2.2 s per write and
|
||||||
|
"full" pressure at 45-78%, against a median minute peak of 24 ms. The
|
||||||
|
worst minute after the merges was the rollout of eight merged PRs at
|
||||||
|
2026-10-25 11:25-11:27 (77 errors in one minute). The snapshot-controller,
|
||||||
|
CNPG and Envoy Gateway held their 60s leases through all of it
|
||||||
Ubuntu's sysstat already keeps per-device history every 10 minutes
|
Ubuntu's sysstat already keeps per-device history every 10 minutes
|
||||||
without a script: `sar -d -f /var/log/sysstat/sa24` showed sda at
|
without a script: `sar -d -f /var/log/sysstat/sa24` showed sda at
|
||||||
~26 MB/s read and ~27 MB/s written, 9 ms await, during the merging
|
~26 MB/s read and ~27 MB/s written, 9 ms await, during the merging
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# Envoy Gateway: the controller and the proxy it runs (envoyproxy.yaml).
|
||||||
|
# Both reach each other (the proxy gets its config from the controller on
|
||||||
|
# 18000) and DNS, and take connections from each other and the node: the
|
||||||
|
# API server calls the controller's topology webhook (9443) from there and
|
||||||
|
# the kubelet probes come from there too (10.1.7.64 is the node's Calico
|
||||||
|
# VXLAN address, projectcalico.org/IPv4VXLANTunnelAddr). Nothing scrapes
|
||||||
|
# the metrics on 19001.
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: default
|
||||||
|
namespace: envoy-gateway-system
|
||||||
|
spec:
|
||||||
|
podSelector: {}
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
- Egress
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- podSelector: {}
|
||||||
|
- from:
|
||||||
|
- ipBlock:
|
||||||
|
cidr: 192.168.1.204/32
|
||||||
|
- ipBlock:
|
||||||
|
cidr: 10.1.7.64/32
|
||||||
|
egress:
|
||||||
|
- to:
|
||||||
|
- podSelector: {}
|
||||||
|
- to:
|
||||||
|
- namespaceSelector:
|
||||||
|
matchLabels:
|
||||||
|
kubernetes.io/metadata.name: kube-system
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
k8s-app: kube-dns
|
||||||
|
ports:
|
||||||
|
- protocol: UDP
|
||||||
|
port: 53
|
||||||
|
- protocol: TCP
|
||||||
|
port: 53
|
||||||
|
---
|
||||||
|
# The proxy holds the node's ports 80 and 443 (10080 and 10443 inside) and
|
||||||
|
# takes connections there from anywhere: the internet through node001 and
|
||||||
|
# the router, the LAN, and pods that call a public hostname. It reaches
|
||||||
|
# every app it routes to (pods in any namespace; their own policies decide
|
||||||
|
# which port), Home Assistant on 192.168.1.202:8123 (outside the cluster,
|
||||||
|
# home-assistant/), and the internet on 443 for Longhorn's OIDC login
|
||||||
|
# (authentik's token endpoint, at its public address).
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: proxy
|
||||||
|
namespace: envoy-gateway-system
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: envoy
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
- Egress
|
||||||
|
ingress:
|
||||||
|
- ports:
|
||||||
|
- port: 10080
|
||||||
|
- port: 10443
|
||||||
|
egress:
|
||||||
|
- to:
|
||||||
|
- namespaceSelector: {}
|
||||||
|
- to:
|
||||||
|
- ipBlock:
|
||||||
|
cidr: 192.168.1.202/32
|
||||||
|
ports:
|
||||||
|
- port: 8123
|
||||||
|
- to:
|
||||||
|
- ipBlock:
|
||||||
|
cidr: 0.0.0.0/0
|
||||||
|
except:
|
||||||
|
- 10.0.0.0/8
|
||||||
|
- 100.64.0.0/10
|
||||||
|
- 169.254.0.0/16
|
||||||
|
- 172.16.0.0/12
|
||||||
|
- 192.168.0.0/16
|
||||||
|
ports:
|
||||||
|
- port: 443
|
||||||
|
---
|
||||||
|
# Everything but the proxy (the controller, and the chart's certgen job on
|
||||||
|
# upgrades) reaches the API server (the node, port 16443). The controller
|
||||||
|
# also fetches the OIDC discovery document of Longhorn's SecurityPolicy
|
||||||
|
# from authentik's public address.
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: control-plane
|
||||||
|
namespace: envoy-gateway-system
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchExpressions:
|
||||||
|
- key: app.kubernetes.io/name
|
||||||
|
operator: NotIn
|
||||||
|
values:
|
||||||
|
- envoy
|
||||||
|
policyTypes:
|
||||||
|
- Egress
|
||||||
|
egress:
|
||||||
|
- to:
|
||||||
|
- ipBlock:
|
||||||
|
cidr: 192.168.1.204/32
|
||||||
|
ports:
|
||||||
|
- port: 16443
|
||||||
|
- to:
|
||||||
|
- ipBlock:
|
||||||
|
cidr: 0.0.0.0/0
|
||||||
|
except:
|
||||||
|
- 10.0.0.0/8
|
||||||
|
- 100.64.0.0/10
|
||||||
|
- 169.254.0.0/16
|
||||||
|
- 172.16.0.0/12
|
||||||
|
- 192.168.0.0/16
|
||||||
|
ports:
|
||||||
|
- port: 443
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# node001 passes TLS for the tunneled hosts through without opening it, so
|
||||||
|
# without this every request arrives from 46.224.26.65. Wordfence locks that
|
||||||
|
# address out after 20 failed logins (2026-10-26), and with it everyone who
|
||||||
|
# logs in to scoutingmbg.nl. node001 puts a PROXY header with the visitor's
|
||||||
|
# address in front of each connection; Envoy takes the address from there
|
||||||
|
# and adds it to X-Forwarded-For.
|
||||||
|
#
|
||||||
|
# Optional, because s2 and ntfy2 point straight at home and their clients
|
||||||
|
# send no header, and neither does node001's proxy on port 80. The price:
|
||||||
|
# whoever connects to the home address directly can send a header of their
|
||||||
|
# own and claim any address.
|
||||||
|
apiVersion: gateway.envoyproxy.io/v1alpha1
|
||||||
|
kind: ClientTrafficPolicy
|
||||||
|
metadata:
|
||||||
|
name: proxy-protocol
|
||||||
|
namespace: envoy-gateway-system
|
||||||
|
spec:
|
||||||
|
targetRefs:
|
||||||
|
- group: gateway.networking.k8s.io
|
||||||
|
kind: Gateway
|
||||||
|
name: homelab
|
||||||
|
proxyProtocol:
|
||||||
|
optional: true
|
||||||
@@ -256,7 +256,7 @@ spec:
|
|||||||
type: RuntimeDefault
|
type: RuntimeDefault
|
||||||
containers:
|
containers:
|
||||||
- name: check
|
- name: check
|
||||||
image: python:3.13.16-alpine3.24
|
image: python:3.15.1-alpine3.24
|
||||||
command: ["python", "/script/check.py"]
|
command: ["python", "/script/check.py"]
|
||||||
env:
|
env:
|
||||||
- name: PYTHONDONTWRITEBYTECODE
|
- name: PYTHONDONTWRITEBYTECODE
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ spec:
|
|||||||
fsGroup: 1000
|
fsGroup: 1000
|
||||||
containers:
|
containers:
|
||||||
- name: gitea
|
- name: gitea
|
||||||
image: gitea/gitea:1.27.3
|
image: gitea/gitea:28.2.0
|
||||||
env:
|
env:
|
||||||
- name: USER_UID
|
- name: USER_UID
|
||||||
value: "1000"
|
value: "1000"
|
||||||
|
|||||||
+14
-3
@@ -127,6 +127,9 @@ spec:
|
|||||||
TRANSFORMERS_CACHE: /cache
|
TRANSFORMERS_CACHE: /cache
|
||||||
HF_XET_CACHE: /cache/huggingface-xet
|
HF_XET_CACHE: /cache/huggingface-xet
|
||||||
MPLCONFIGDIR: /cache/matplotlib-config
|
MPLCONFIGDIR: /cache/matplotlib-config
|
||||||
|
# The optimized models of v3.3.0: faster, less memory,
|
||||||
|
# and the same results, so no jobs to run again.
|
||||||
|
MACHINE_LEARNING_MODEL_REVISION: v2
|
||||||
# Trust store without the compromised eMudhra
|
# Trust store without the compromised eMudhra
|
||||||
# intermediates; requests would otherwise use certifi.
|
# intermediates; requests would otherwise use certifi.
|
||||||
SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt
|
SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt
|
||||||
@@ -139,11 +142,19 @@ spec:
|
|||||||
globalMounts:
|
globalMounts:
|
||||||
- path: /etc/ssl/certs
|
- path: /etc/ssl/certs
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
# The models and their one-off preparation, kept across restarts.
|
||||||
|
# Downloaded again if lost, so no backups: the no-backup group
|
||||||
|
# keeps it out of Longhorn's default one (backup-daily), and
|
||||||
|
# Velero skips that label too (velero/skip-emptydir-policy.yaml).
|
||||||
cache:
|
cache:
|
||||||
enabled: true
|
enabled: true
|
||||||
# Optional: to avoid downloading the ML models every start, use
|
type: persistentVolumeClaim
|
||||||
# a persistentVolumeClaim with size, accessMode and storageClass
|
storageClass: longhorn
|
||||||
type: emptyDir
|
accessMode: ReadWriteOnce
|
||||||
|
size: 10Gi
|
||||||
|
labels:
|
||||||
|
recurring-job.longhorn.io/source: enabled
|
||||||
|
recurring-job-group.longhorn.io/no-backup: enabled
|
||||||
|
|
||||||
destination:
|
destination:
|
||||||
server: https://kubernetes.default.svc
|
server: https://kubernetes.default.svc
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Garage on the NAS
|
||||||
|
|
||||||
|
S3 storage for Velero, on the Synology (192.168.1.99) in Docker, outside
|
||||||
|
the cluster. Replaced MinIO, which is no longer maintained. One node, data
|
||||||
|
in `/volume1/docker/garage/{meta,data}`. S3 API on port 3900, admin API on
|
||||||
|
3903, both LAN only. Set up 2026-10-25 with Garage v2.5.0.
|
||||||
|
|
||||||
|
The NAS is reached as `ssh -p 34000 rubenhensen@192.168.1.99`; docker is
|
||||||
|
in `/usr/local/bin`, which a non-interactive SSH session doesn't have on
|
||||||
|
its PATH.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
`garage.toml` here has placeholders for the two secrets; on the NAS they
|
||||||
|
were filled in once with `openssl rand -hex 32` (`rpc_secret`) and
|
||||||
|
`openssl rand -base64 32` (`admin_token`), and only exist there.
|
||||||
|
|
||||||
|
```
|
||||||
|
cd /volume1/docker/garage
|
||||||
|
docker compose up -d
|
||||||
|
g="docker exec garage /garage"
|
||||||
|
$g layout assign -z nas -c 800G <node id from $g status>
|
||||||
|
$g layout apply --version 1
|
||||||
|
$g bucket create velero-backups
|
||||||
|
$g key create velero
|
||||||
|
$g bucket allow --read --write --key velero velero-backups
|
||||||
|
```
|
||||||
|
|
||||||
|
Velero's key is in Vault as `kv/velero-s3` (`access_key_id`,
|
||||||
|
`secret_access_key`) and reaches Velero through
|
||||||
|
`velero/external-secret.yaml`. Without the cluster, `$g key info velero
|
||||||
|
--show-secret` on the NAS shows it again.
|
||||||
|
|
||||||
|
Renovate updates the image tag in `compose.yaml`, but nothing deploys it:
|
||||||
|
after merging, `docker compose pull && docker compose up -d` on the NAS.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
services:
|
||||||
|
garage:
|
||||||
|
image: dxflrs/garage:v2.5.0
|
||||||
|
container_name: garage
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3900:3900" # S3 API
|
||||||
|
- "3903:3903" # admin API
|
||||||
|
volumes:
|
||||||
|
- ./garage.toml:/etc/garage.toml:ro
|
||||||
|
- ./meta:/var/lib/garage/meta
|
||||||
|
- ./data:/var/lib/garage/data
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# One node on the Synology. Velero's kopia data is encrypted, so
|
||||||
|
# compression would only cost CPU.
|
||||||
|
#
|
||||||
|
# SQLite rather than the default LMDB: LMDB can be corrupted by an unclean
|
||||||
|
# shutdown, and a single node has no other node to repair from. With
|
||||||
|
# metadata_fsync SQLite runs WAL with synchronous=NORMAL, which only syncs
|
||||||
|
# at checkpoints; LMDB with metadata_fsync synced every transaction, ~270
|
||||||
|
# writes/s on the NAS's two disks with Garage idle. Converted 2026-10-25
|
||||||
|
# with `garage convert-db`.
|
||||||
|
metadata_dir = "/var/lib/garage/meta"
|
||||||
|
data_dir = "/var/lib/garage/data"
|
||||||
|
db_engine = "sqlite"
|
||||||
|
metadata_fsync = true
|
||||||
|
data_fsync = true
|
||||||
|
metadata_auto_snapshot_interval = "6h"
|
||||||
|
replication_factor = 1
|
||||||
|
compression_level = "none"
|
||||||
|
|
||||||
|
rpc_bind_addr = "[::]:3901"
|
||||||
|
rpc_public_addr = "127.0.0.1:3901"
|
||||||
|
rpc_secret = "@RPC_SECRET@"
|
||||||
|
|
||||||
|
[s3_api]
|
||||||
|
s3_region = "garage"
|
||||||
|
api_bind_addr = "[::]:3900"
|
||||||
|
|
||||||
|
[admin]
|
||||||
|
api_bind_addr = "[::]:3903"
|
||||||
|
admin_token = "@ADMIN_TOKEN@"
|
||||||
@@ -173,12 +173,18 @@ in
|
|||||||
# - mail.rubenhensen.nl (and anything else) → local stalwart on 8443
|
# - mail.rubenhensen.nl (and anything else) → local stalwart on 8443
|
||||||
# - tunneled hosts → home cluster ingress on 443
|
# - tunneled hosts → home cluster ingress on 443
|
||||||
# - s1 / ntfy1.rubenhensen.nl → local nginx on 8444 (Gatus, ntfy)
|
# - s1 / ntfy1.rubenhensen.nl → local nginx on 8444 (Gatus, ntfy)
|
||||||
|
#
|
||||||
|
# Each connection starts with a PROXY header that carries the visitor's
|
||||||
|
# address, so the home cluster sees who is asking instead of this box
|
||||||
|
# (gateway/proxy-protocol.yaml in k8scd). nginx sends the header to
|
||||||
|
# every upstream or none, so the local ones go through a hop on
|
||||||
|
# 8445/8446 that takes it off again.
|
||||||
streamConfig = ''
|
streamConfig = ''
|
||||||
map $ssl_preread_server_name $tunnel_upstream {
|
map $ssl_preread_server_name $tunnel_upstream {
|
||||||
${sniMapEntries}
|
${sniMapEntries}
|
||||||
s1.rubenhensen.nl 127.0.0.1:8444;
|
s1.rubenhensen.nl 127.0.0.1:8446;
|
||||||
ntfy1.rubenhensen.nl 127.0.0.1:8444;
|
ntfy1.rubenhensen.nl 127.0.0.1:8446;
|
||||||
default 127.0.0.1:8443;
|
default 127.0.0.1:8445;
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
@@ -186,6 +192,17 @@ ${sniMapEntries}
|
|||||||
listen [::]:443;
|
listen [::]:443;
|
||||||
proxy_pass $tunnel_upstream;
|
proxy_pass $tunnel_upstream;
|
||||||
ssl_preread on;
|
ssl_preread on;
|
||||||
|
proxy_protocol on;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:8445 proxy_protocol;
|
||||||
|
proxy_pass 127.0.0.1:8443;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:8446 proxy_protocol;
|
||||||
|
proxy_pass 127.0.0.1:8444;
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: ntfy
|
- name: ntfy
|
||||||
image: binwiederhier/ntfy:v2.28.0
|
image: binwiederhier/ntfy:v2.34.0
|
||||||
args: ["serve"]
|
args: ["serve"]
|
||||||
env:
|
env:
|
||||||
- name: NTFY_BASE_URL
|
- name: NTFY_BASE_URL
|
||||||
|
|||||||
+18
-2
@@ -5,8 +5,16 @@
|
|||||||
"customManagers:githubActionsVersions"
|
"customManagers:githubActionsVersions"
|
||||||
],
|
],
|
||||||
"argocd": {
|
"argocd": {
|
||||||
|
"description": "Every app folder; Renovate's regexes are RE2, which has no lookahead, so the other folders are skipped through ignorePaths",
|
||||||
"managerFilePatterns": [
|
"managerFilePatterns": [
|
||||||
"/^(?!(nix-infra-machine|backup-testing|docs|tools|\\.github)/)[^/]+/.+\\.yaml$/"
|
"/^[^/]+/.+\\.yaml$/"
|
||||||
|
],
|
||||||
|
"ignorePaths": [
|
||||||
|
"nix-infra-machine/**",
|
||||||
|
"backup-testing/**",
|
||||||
|
"docs/**",
|
||||||
|
"tools/**",
|
||||||
|
".github/**"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"helm-values": {
|
"helm-values": {
|
||||||
@@ -15,8 +23,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"kubernetes": {
|
"kubernetes": {
|
||||||
|
"description": "Every app folder, as for argocd",
|
||||||
"managerFilePatterns": [
|
"managerFilePatterns": [
|
||||||
"/^(?!(nix-infra-machine|backup-testing|docs|tools|\\.github)/)[^/]+/.+\\.yaml$/"
|
"/^[^/]+/.+\\.yaml$/"
|
||||||
|
],
|
||||||
|
"ignorePaths": [
|
||||||
|
"nix-infra-machine/**",
|
||||||
|
"backup-testing/**",
|
||||||
|
"docs/**",
|
||||||
|
"tools/**",
|
||||||
|
".github/**"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"customManagers": [
|
"customManagers": [
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module dqlite-vacuum
|
|||||||
go 1.27.3
|
go 1.27.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/canonical/go-dqlite/v3 v3.0.1
|
github.com/canonical/go-dqlite/v3 v3.0.4
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ github.com/Rican7/retry v0.3.1 h1:scY4IbO8swckzoA/11HgBwaZRJEyY9vaNJshcdhp1Mc=
|
|||||||
github.com/Rican7/retry v0.3.1/go.mod h1:CxSDrhAyXmTMeEuRAnArMu1FHu48vtfjLREWqVl7Vw0=
|
github.com/Rican7/retry v0.3.1/go.mod h1:CxSDrhAyXmTMeEuRAnArMu1FHu48vtfjLREWqVl7Vw0=
|
||||||
github.com/canonical/go-dqlite/v3 v3.0.1 h1:eUTcEHetVi1+zr+ZFz7PU0G+0eK66GZ7DKhi+wHs1f0=
|
github.com/canonical/go-dqlite/v3 v3.0.1 h1:eUTcEHetVi1+zr+ZFz7PU0G+0eK66GZ7DKhi+wHs1f0=
|
||||||
github.com/canonical/go-dqlite/v3 v3.0.1/go.mod h1:Kb/9JVog9XeIlnlnSyO1JgMoE4PoKXZCB8fJNcvvHAc=
|
github.com/canonical/go-dqlite/v3 v3.0.1/go.mod h1:Kb/9JVog9XeIlnlnSyO1JgMoE4PoKXZCB8fJNcvvHAc=
|
||||||
|
github.com/canonical/go-dqlite/v3 v3.0.4 h1:JhITsrvVL0pii9arxDksXfsRqBI+je1VpcqVuzDllUM=
|
||||||
|
github.com/canonical/go-dqlite/v3 v3.0.4/go.mod h1:6O+E6MiYesijRUOECyywp16iT+N4QOrvjDWWiMJ/xF0=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -26,7 +29,9 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||||
|
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Velero's key for its bucket in Garage on the NAS (nas/garage), in the
|
||||||
|
# AWS credentials file format the aws plugin and kopia read.
|
||||||
|
apiVersion: external-secrets.io/v1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: velero-s3-credentials
|
||||||
|
spec:
|
||||||
|
secretStoreRef:
|
||||||
|
name: vault-backend
|
||||||
|
kind: ClusterSecretStore
|
||||||
|
refreshInterval: 15m
|
||||||
|
target:
|
||||||
|
name: velero-s3-credentials
|
||||||
|
creationPolicy: Owner
|
||||||
|
template:
|
||||||
|
engineVersion: v2
|
||||||
|
data:
|
||||||
|
cloud: |
|
||||||
|
[default]
|
||||||
|
aws_access_key_id={{ .access_key_id }}
|
||||||
|
aws_secret_access_key={{ .secret_access_key }}
|
||||||
|
data:
|
||||||
|
- secretKey: access_key_id
|
||||||
|
remoteRef:
|
||||||
|
key: kv/velero-s3
|
||||||
|
property: access_key_id
|
||||||
|
- secretKey: secret_access_key
|
||||||
|
remoteRef:
|
||||||
|
key: kv/velero-s3
|
||||||
|
property: secret_access_key
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: nas-minio-external
|
|
||||||
spec:
|
|
||||||
type: ExternalName
|
|
||||||
externalName: 192.168.1.99
|
|
||||||
ports:
|
|
||||||
- port: 9000
|
|
||||||
protocol: TCP
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
# Volume policy for the backup schedules. emptyDir volumes hold nothing
|
# Volume policy for the backup schedules. emptyDir volumes hold nothing
|
||||||
# worth keeping, and backing them up fails when the pod is gone before its
|
# worth keeping, and backing them up fails when the pod is gone before its
|
||||||
# volume is read, as happens with Velero's own repository maintenance jobs.
|
# volume is read, as happens with Velero's own repository maintenance jobs.
|
||||||
|
# PVCs in Longhorn's no-backup group (caches, such as immich's ML models)
|
||||||
|
# are skipped too.
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
@@ -14,3 +16,8 @@ data:
|
|||||||
- emptyDir
|
- emptyDir
|
||||||
action:
|
action:
|
||||||
type: skip
|
type: skip
|
||||||
|
- conditions:
|
||||||
|
pvcLabels:
|
||||||
|
recurring-job-group.longhorn.io/no-backup: enabled
|
||||||
|
action:
|
||||||
|
type: skip
|
||||||
@@ -27,20 +27,22 @@ spec:
|
|||||||
- mountPath: /target
|
- mountPath: /target
|
||||||
name: plugins
|
name: plugins
|
||||||
configuration:
|
configuration:
|
||||||
|
# Garage on the NAS (nas/garage). Until 2026-10-26 this was MinIO on
|
||||||
|
# the same NAS; the bucket was copied over with rclone, so the
|
||||||
|
# existing backups and kopia repositories carried on.
|
||||||
backupStorageLocation:
|
backupStorageLocation:
|
||||||
- name: default
|
- name: default
|
||||||
bucket: velero-backups
|
bucket: velero-backups
|
||||||
provider: aws
|
provider: aws
|
||||||
config:
|
config:
|
||||||
region: minio
|
region: garage
|
||||||
s3ForcePathStyle: true
|
s3ForcePathStyle: true
|
||||||
s3Url: http://192.168.1.99:9000
|
s3Url: http://192.168.1.99:3900
|
||||||
insecureSkipTLSVerify: "true"
|
|
||||||
volumeSnapshotLocation:
|
volumeSnapshotLocation:
|
||||||
- name: default
|
- name: default
|
||||||
provider: aws
|
provider: aws
|
||||||
config:
|
config:
|
||||||
region: minio
|
region: garage
|
||||||
features: EnableCSI
|
features: EnableCSI
|
||||||
# Kopia maintenance ran every hour for each of the 15 repositories,
|
# Kopia maintenance ran every hour for each of the 15 repositories,
|
||||||
# and once a day as a full run of several minutes that writes its
|
# and once a day as a full run of several minutes that writes its
|
||||||
@@ -59,11 +61,8 @@ spec:
|
|||||||
memoryRequest: 256Mi
|
memoryRequest: 256Mi
|
||||||
memoryLimit: 2Gi
|
memoryLimit: 2Gi
|
||||||
credentials:
|
credentials:
|
||||||
# Use existing secret created by External Secrets Operator
|
# From Vault, velero/external-secret.yaml
|
||||||
# existingSecret: velero-minio-credentials
|
existingSecret: velero-s3-credentials
|
||||||
# extraEnvVars:
|
|
||||||
# AWS_SHARED_CREDENTIALS_FILE: /credentials/cloud
|
|
||||||
existingSecret: cloud-credentials
|
|
||||||
deployNodeAgent: true
|
deployNodeAgent: true
|
||||||
nodeAgent:
|
nodeAgent:
|
||||||
containerSecurityContext:
|
containerSecurityContext:
|
||||||
|
|||||||
Reference in new issue
Block a user