Compare commits

..
Author SHA1 Message Date
Ruben Hensen 7b12ee2028 cnpg: back the databases up to Garage with the barman cloud plugin 2026-10-26 23:38:13 +01:00
Ruben Hensen 66207de2dd Merge pull request #52 from rubenhensen/renovate-automerge
Renovate: automerge patch updates of low-risk apps
2026-10-26 23:28:09 +01:00
Ruben Hensen d0ce5ddbec Merge pull request #51 from rubenhensen/netpol-acme
NetworkPolicies: let Envoy reach cert-manager's HTTP-01 solver
2026-10-26 23:27:48 +01:00
Ruben Hensen 6c727346f6 renovate: patch updates of low-risk apps merge themselves 2026-10-26 23:23:25 +01:00
Ruben Hensen ed828b426f networkpolicy: let Envoy reach cert-manager's HTTP-01 solver 2026-10-26 23:19:28 +01:00
19 changed files with 579 additions and 38 deletions

No files matched your search

+26
View File
@@ -55,3 +55,29 @@ spec:
- 169.254.0.0/16 - 169.254.0.0/16
- 172.16.0.0/12 - 172.16.0.0/12
- 192.168.0.0/16 - 192.168.0.0/16
---
# cert-manager's HTTP-01 solver, which runs only while a certificate
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
# through Envoy. The policies above let Envoy reach the app's port only,
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
# hung).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: acme-http01-solver
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: envoy-gateway-system
podSelector:
matchLabels:
app.kubernetes.io/name: envoy
ports:
- port: 8089
@@ -0,0 +1,32 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: plugin-barman-cloud
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
chart: plugin-barman-cloud
repoURL: https://cloudnative-pg.github.io/charts
targetRevision: 0.8.1
helm:
values: |
# CNPG's barman cloud plugin, which backs the PostgreSQL clusters
# up to Garage on the NAS (authentik/postgresql-backup.yaml,
# immich/postgresql-backup.yaml). It has to run in the operator's
# namespace; the chart gets its TLS certificates from cert-manager.
resources:
requests:
cpu: 10m
memory: 64Mi
destination:
server: https://kubernetes.default.svc
namespace: cnpg-system
syncPolicy:
syncOptions:
- ServerSideApply=true
automated:
prune: true
selfHeal: true
+33 -1
View File
@@ -108,7 +108,8 @@ spec:
--- ---
# The CNPG operator checks each instance on its status port and connects to # The CNPG operator checks each instance on its status port and connects to
# PostgreSQL; the instance manager inside each pod watches its Cluster # PostgreSQL; the instance manager inside each pod watches its Cluster
# through the API server. # through the API server. The barman cloud sidecar sends the WAL and the
# base backups to Garage on the NAS (postgresql-backup.yaml).
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -135,3 +136,34 @@ spec:
cidr: 192.168.1.204/32 cidr: 192.168.1.204/32
ports: ports:
- port: 16443 - port: 16443
- to:
- ipBlock:
cidr: 192.168.1.99/32
ports:
- port: 3900
---
# cert-manager's HTTP-01 solver, which runs only while a certificate
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
# through Envoy. The policies above let Envoy reach the app's port only,
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
# hung).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: acme-http01-solver
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: envoy-gateway-system
podSelector:
matchLabels:
app.kubernetes.io/name: envoy
ports:
- port: 8089
+92
View File
@@ -0,0 +1,92 @@
# Backups of authentik's database by CNPG itself, through the barman cloud
# plugin (apps/templates/plugin-barman-cloud-helm.yaml): a base backup every
# night, and every WAL segment as soon as it is full or 5 minutes old
# (archive_timeout). Both go to the bucket cnpg-authentik in Garage on the
# NAS (nas/garage), so the database can be restored to any moment of the
# last 30 days. Velero copies the data directory while PostgreSQL runs,
# which is not a consistent copy.
# The key is Garage's cnpg-authentik, in Vault as kv/cnpg-authentik-s3; it
# can only reach this bucket.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: authentik-postgres-s3
spec:
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
refreshInterval: 15m
target:
name: authentik-postgres-s3
creationPolicy: Owner
template:
engineVersion: v2
data:
ACCESS_KEY_ID: "{{ .access_key_id }}"
ACCESS_SECRET_KEY: "{{ .secret_access_key }}"
# Garage rejects requests signed for another region
REGION: garage
data:
- secretKey: access_key_id
remoteRef:
key: kv/cnpg-authentik-s3
property: access_key_id
- secretKey: secret_access_key
remoteRef:
key: kv/cnpg-authentik-s3
property: secret_access_key
---
apiVersion: barmancloud.cnpg.io/v1
kind: ObjectStore
metadata:
name: authentik-postgres
annotations:
# The CRD comes with the plugin's own app
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
spec:
retentionPolicy: 30d
configuration:
destinationPath: s3://cnpg-authentik/
endpointURL: http://192.168.1.99:3900
s3Credentials:
accessKeyId:
name: authentik-postgres-s3
key: ACCESS_KEY_ID
secretAccessKey:
name: authentik-postgres-s3
key: ACCESS_SECRET_KEY
region:
name: authentik-postgres-s3
key: REGION
wal:
compression: gzip
data:
compression: gzip
instanceSidecarConfiguration:
resources:
requests:
cpu: 10m
memory: 64Mi
env:
# Newer boto3 adds checksums that S3-compatible stores can reject;
# the plugin's docs suggest sending them only when required.
- name: AWS_REQUEST_CHECKSUM_CALCULATION
value: when_required
- name: AWS_RESPONSE_CHECKSUM_VALIDATION
value: when_required
---
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: authentik-postgres-daily
spec:
cluster:
name: authentik-postgres
# 03:00 UTC, after Longhorn's and Velero's backups; immediate: one right
# away, so WAL archiving has a base backup to start from.
schedule: "0 0 3 * * *"
immediate: true
backupOwnerReference: self
method: plugin
pluginConfiguration:
name: barman-cloud.cloudnative-pg.io
+6
View File
@@ -8,6 +8,12 @@ spec:
# start an in-place major upgrade. CNPG filled this in at creation. # start an in-place major upgrade. CNPG filled this in at creation.
imageName: ghcr.io/cloudnative-pg/postgresql:17.4 imageName: ghcr.io/cloudnative-pg/postgresql:17.4
primaryUpdateStrategy: unsupervised primaryUpdateStrategy: unsupervised
# WAL archiving and base backups to Garage, see postgresql-backup.yaml
plugins:
- name: barman-cloud.cloudnative-pg.io
isWALArchiver: true
parameters:
barmanObjectName: authentik-postgres
resources: resources:
requests: requests:
cpu: 50m cpu: 50m
+26
View File
@@ -54,3 +54,29 @@ spec:
- 169.254.0.0/16 - 169.254.0.0/16
- 172.16.0.0/12 - 172.16.0.0/12
- 192.168.0.0/16 - 192.168.0.0/16
---
# cert-manager's HTTP-01 solver, which runs only while a certificate
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
# through Envoy. The policies above let Envoy reach the app's port only,
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
# hung).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: acme-http01-solver
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: envoy-gateway-system
podSelector:
matchLabels:
app.kubernetes.io/name: envoy
ports:
- port: 8089
+15 -36
View File
@@ -38,8 +38,7 @@ docs/longhorn-second-ssd.md:
2026-10-25). The snap is held, so microk8s gets no updates at all until 2026-10-25). The snap is held, so microk8s gets no updates at all until
someone refreshes it someone refreshes it
- [ ] Second SSD, docs/longhorn-second-ssd.md - [ ] Second SSD, docs/longhorn-second-ssd.md
- [x] `MACHINE_LEARNING_MODEL_REVISION=v2` for immich ML (PR #39, - [ ] Optional: `MACHINE_LEARNING_MODEL_REVISION=v2` for immich ML
2026-10-25; the models are cached on a volume)
## Next, in order ## Next, in order
@@ -83,9 +82,7 @@ friend.
certificate; the admin API stays on the LAN. The cluster side (DNS, certificate; the admin API stays on the LAN. The cluster side (DNS,
node001 tunnel, Certificate, HTTPRoute) goes in git. Needed: how much node001 tunnel, Certificate, HTTPRoute) goes in git. Needed: how much
space he wants, free space on the NAS space he wants, free space on the NAS
- [x] Which copy of the immich library goes off-site: Velero's, with the - [ ] Decide which copy of the immich library goes off-site (see 5)
rest of the bucket (decided 2026-10-26). It is most of the ~190 GB, so
ask him for room for it
- [ ] 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.
Planned on 2026-10-25, not run: a second Velero on the real bucket is Planned on 2026-10-25, not run: a second Velero on the real bucket is
@@ -205,8 +202,8 @@ Findings from 2026-10-25:
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`.
- [x] Whether the immich library stays in Velero: it stays, since - [ ] Whether the immich library stays in Velero. It stays for now
Velero's bucket is what goes off-site (2; decided 2026-10-26) (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
- [x] 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
@@ -301,22 +298,10 @@ Counted 2026-10-25 over the 73 running containers:
OIDC logins of Vault and FreshRSS reach authentik, all 24 OIDC logins of Vault and FreshRSS reach authentik, all 24
ExternalSecrets refresh, raft intact; Gitea no longer reaches the NAS ExternalSecrets refresh, raft intact; Gitea no longer reaches the NAS
or Vault, and a pod in `default` no longer reaches Vault or Gitea or Vault, and a pod in `default` no longer reaches Vault or Gitea
- [ ] Certificates couldn't renew in those 11 namespaces: Envoy may only
reach each app's own port, and cert-manager's HTTP-01 solver listens on
8089. ynab's renewal hung from 2026-10-26 17:35 UTC; a policy for the
solver pods, applied by hand in actual-budget that evening, let it
through within 20 seconds (now valid until 2027-01-24). PR #51 adds it
to all 11. Without it the next renewals hang too: gitea on 11-15,
bolletjestrui on 11-18, the rest from 12-03
- [ ] The infrastructure namespaces (cert-manager, cnpg-system, - [ ] The infrastructure namespaces (cert-manager, cnpg-system,
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. Prepared on 2026-10-26, not yet merged: PR #53 for without a policy
all but Envoy (one commit per namespace; `default` denies everything),
PR #54 for envoy-gateway-system on its own, since a mistake there takes
every public host down. Merge them when someone is home to check: the
cert-manager, CNPG, ESO and MariaDB webhooks fail closed. Velero's
policy only lets it reach Garage, not MinIO's port 9000
- [x] Requests: PR #33, merged 2026-10-25 11:23 UTC, for everything that - [x] Requests: PR #33, merged 2026-10-25 11:23 UTC, for everything that
had none except the Vault server, from `kubectl top` every 5 minutes 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 from 03:13 to 08:40 UTC (highest memory plus 10%, CPU at p95, 10m
@@ -349,14 +334,10 @@ Counted 2026-10-25 over the 73 running containers:
`litespeed-cache` only caches pages on a LiteSpeed server, so it does `litespeed-cache` only caches pages on a LiteSpeed server, so it does
little here, `wordpress-importer` is usually for a one-off import, and little here, `wordpress-importer` is usually for a one-off import, and
`code-snippets` runs PHP from the dashboard `code-snippets` runs PHP from the dashboard
- [ ] Rate limiting per visitor in Envoy, on hold. Since PR #50 - [ ] Rate limiting per visitor in Envoy needs the client's address, which
(2026-10-26) node001 passes the visitor's address on with the PROXY node001 loses: it passes TLS through, so every request arrives from
protocol, and the apps see it: authentik logs it, Apache logs it for 46.224.26.65. That needs the PROXY protocol from node001's nginx to Envoy
WordPress, so Wordfence's lockout after 20 failed logins now hits one (ClientTrafficPolicy `enableProxyProtocol`)
visitor instead of everyone. Envoy Gateway's local rate limit can't
count per address (no `Distinct`); that takes its global rate limit,
with a Redis and a ratelimit service next to it. Not worth two more
components on the one node while the apps' own limits work
- [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
@@ -394,10 +375,7 @@ every PR.
fail the run; tested with a planted Secret and an untagged image), plus fail the run; tested with a planted Secret and an untagged image), plus
PR annotations for containers without requests or probes. freshrss's PR annotations for containers without requests or probes. freshrss's
`alpine/git` init container had no tag; now `v2.52.0` `alpine/git` init container had no tag; now `v2.52.0`
- [ ] Automerge patch versions of low-risk apps now that CI runs: PR #52 - [ ] Automerge patch versions of low-risk apps now that CI runs
(ntfy, gatus, freshrss, serpbear, actual-budget and CI's own tools;
three days after the release, only on weekday evenings and at the
weekend, when the cluster can be reached)
- [x] Renovate listed external-snapshotter twice (#15 and #24): its - [x] Renovate listed external-snapshotter twice (#15 and #24): its
`argocd` manager and a custom `github-tags` regex both matched. The `argocd` manager and a custom `github-tags` regex both matched. The
regex is gone; Renovate should close #15 by itself regex is gone; Renovate should close #15 by itself
@@ -454,10 +432,11 @@ step after that, if downtime starts to matter.
there and the volumes hold nothing (0 bytes in every backup). Drop both 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 mounts and the two PVCs, or make them ConfigMap files if custom config
is wanted is wanted
- [x] The namespace `secrets` was already gone on 2026-10-26 - [ ] `kubectl delete namespace secrets`, empty since its app went
- [x] The 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; 40 by then, one had expired), deleted through the weeklies since 08-02. Velero doesn't restore from a Failed backup;
Velero on 2026-10-26. Left: 13 Completed, 3 PartiallyFailed left alone they expire by 2027-01-16.
`kubectl -n velero exec deploy/velero -c velero -- /velero backup delete $(kubectl -n velero get backups.velero.io -o json | jq -r '.items[] | select(.status.phase=="Failed") | .metadata.name' | xargs) --confirm`
- [ ] The mariadb-operator Backup `mbgwp-pre-migration-backup`, a dump - [ ] The mariadb-operator Backup `mbgwp-pre-migration-backup`, a dump
from 2026-02-22 on its own 10 GiB volume (which has a Longhorn backup from 2026-02-22 on its own 10 GiB volume (which has a Longhorn backup
from 2026-10-22). The PVC has no owner, so it goes separately: from 2026-10-22). The PVC has no owner, so it goes separately:
+26
View File
@@ -56,3 +56,29 @@ spec:
- 169.254.0.0/16 - 169.254.0.0/16
- 172.16.0.0/12 - 172.16.0.0/12
- 192.168.0.0/16 - 192.168.0.0/16
---
# cert-manager's HTTP-01 solver, which runs only while a certificate
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
# through Envoy. The policies above let Envoy reach the app's port only,
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
# hung).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: acme-http01-solver
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: envoy-gateway-system
podSelector:
matchLabels:
app.kubernetes.io/name: envoy
ports:
- port: 8089
+26
View File
@@ -83,3 +83,29 @@ spec:
cidr: 192.168.1.204/32 cidr: 192.168.1.204/32
ports: ports:
- port: 16443 - port: 16443
---
# cert-manager's HTTP-01 solver, which runs only while a certificate
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
# through Envoy. The policies above let Envoy reach the app's port only,
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
# hung).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: acme-http01-solver
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: envoy-gateway-system
podSelector:
matchLabels:
app.kubernetes.io/name: envoy
ports:
- port: 8089
+26
View File
@@ -55,3 +55,29 @@ spec:
- 169.254.0.0/16 - 169.254.0.0/16
- 172.16.0.0/12 - 172.16.0.0/12
- 192.168.0.0/16 - 192.168.0.0/16
---
# cert-manager's HTTP-01 solver, which runs only while a certificate
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
# through Envoy. The policies above let Envoy reach the app's port only,
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
# hung).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: acme-http01-solver
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: envoy-gateway-system
podSelector:
matchLabels:
app.kubernetes.io/name: envoy
ports:
- port: 8089
+34 -1
View File
@@ -73,7 +73,9 @@ spec:
--- ---
# The CNPG operator checks each instance on its status port and connects to # The CNPG operator checks each instance on its status port and connects to
# PostgreSQL; the instance manager inside each pod watches its Cluster # PostgreSQL; the instance manager inside each pod watches its Cluster
# through the API server (the node, port 16443). # through the API server (the node, port 16443). The barman cloud sidecar
# sends the WAL and the base backups to Garage on the NAS
# (postgresql-backup.yaml).
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -100,3 +102,34 @@ spec:
cidr: 192.168.1.204/32 cidr: 192.168.1.204/32
ports: ports:
- port: 16443 - port: 16443
- to:
- ipBlock:
cidr: 192.168.1.99/32
ports:
- port: 3900
---
# cert-manager's HTTP-01 solver, which runs only while a certificate
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
# through Envoy. The policies above let Envoy reach the app's port only,
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
# hung).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: acme-http01-solver
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: envoy-gateway-system
podSelector:
matchLabels:
app.kubernetes.io/name: envoy
ports:
- port: 8089
+97
View File
@@ -0,0 +1,97 @@
# Backups of immich's database (albums, faces, the photos' metadata; the
# photos themselves are files on immich-claim) by CNPG itself, through the
# barman cloud plugin (apps/templates/plugin-barman-cloud-helm.yaml): a base
# backup every night, and every WAL segment as soon as it is full or 5
# minutes old (archive_timeout). Both go to the bucket cnpg-immich in Garage
# on the NAS (nas/garage), so the database can be restored to any moment of
# the last 30 days. Velero copies the data directory while PostgreSQL runs,
# which is not a consistent copy. Only the PostgreSQL 17 cluster; the 14
# one is about to go.
# The key is Garage's cnpg-immich, in Vault as kv/cnpg-immich-s3; it can
# only reach this bucket.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: immich-postgres-17-s3
namespace: immich
spec:
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
refreshInterval: 15m
target:
name: immich-postgres-17-s3
creationPolicy: Owner
template:
engineVersion: v2
data:
ACCESS_KEY_ID: "{{ .access_key_id }}"
ACCESS_SECRET_KEY: "{{ .secret_access_key }}"
# Garage rejects requests signed for another region
REGION: garage
data:
- secretKey: access_key_id
remoteRef:
key: kv/cnpg-immich-s3
property: access_key_id
- secretKey: secret_access_key
remoteRef:
key: kv/cnpg-immich-s3
property: secret_access_key
---
apiVersion: barmancloud.cnpg.io/v1
kind: ObjectStore
metadata:
name: immich-postgres-17
namespace: immich
annotations:
# The CRD comes with the plugin's own app
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
spec:
retentionPolicy: 30d
configuration:
destinationPath: s3://cnpg-immich/
endpointURL: http://192.168.1.99:3900
s3Credentials:
accessKeyId:
name: immich-postgres-17-s3
key: ACCESS_KEY_ID
secretAccessKey:
name: immich-postgres-17-s3
key: ACCESS_SECRET_KEY
region:
name: immich-postgres-17-s3
key: REGION
wal:
compression: gzip
data:
compression: gzip
instanceSidecarConfiguration:
resources:
requests:
cpu: 10m
memory: 64Mi
env:
# Newer boto3 adds checksums that S3-compatible stores can reject;
# the plugin's docs suggest sending them only when required.
- name: AWS_REQUEST_CHECKSUM_CALCULATION
value: when_required
- name: AWS_RESPONSE_CHECKSUM_VALIDATION
value: when_required
---
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: immich-postgres-17-daily
namespace: immich
spec:
cluster:
name: immich-postgres-17
# 03:00 UTC, after Longhorn's and Velero's backups; immediate: one right
# away, so WAL archiving has a base backup to start from.
schedule: "0 0 3 * * *"
immediate: true
backupOwnerReference: self
method: plugin
pluginConfiguration:
name: barman-cloud.cloudnative-pg.io
+7
View File
@@ -18,6 +18,13 @@ spec:
shared_preload_libraries: shared_preload_libraries:
- "vchord.so" - "vchord.so"
# WAL archiving and base backups to Garage, see postgresql-backup.yaml
plugins:
- name: barman-cloud.cloudnative-pg.io
isWALArchiver: true
parameters:
barmanObjectName: immich-postgres-17
# Filled once from the PostgreSQL 14 cluster (immich-postgres) after its # Filled once from the PostgreSQL 14 cluster (immich-postgres) after its
# embedding columns were turned into real[] and pgvecto.rs was dropped, # embedding columns were turned into real[] and pgvecto.rs was dropped,
# see docs/upgrade-k8s-1.33.md. # see docs/upgrade-k8s-1.33.md.
+26
View File
@@ -85,3 +85,29 @@ spec:
kubernetes.io/metadata.name: mariadb-system kubernetes.io/metadata.name: mariadb-system
ports: ports:
- port: 3306 - port: 3306
---
# cert-manager's HTTP-01 solver, which runs only while a certificate
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
# through Envoy. The policies above let Envoy reach the app's port only,
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
# hung).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: acme-http01-solver
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: envoy-gateway-system
podSelector:
matchLabels:
app.kubernetes.io/name: envoy
ports:
- port: 8089
+7
View File
@@ -31,5 +31,12 @@ Velero's key is in Vault as `kv/velero-s3` (`access_key_id`,
`velero/external-secret.yaml`. Without the cluster, `$g key info velero `velero/external-secret.yaml`. Without the cluster, `$g key info velero
--show-secret` on the NAS shows it again. --show-secret` on the NAS shows it again.
CNPG's backups (`authentik/postgresql-backup.yaml`,
`immich/postgresql-backup.yaml`) have a bucket and a key per cluster, so
neither database can touch the other's backups. Made on 2026-10-26 the
same way as Velero's, with `cnpg-authentik` and `cnpg-immich` as both
bucket and key name; the keys are in Vault as `kv/cnpg-authentik-s3` and
`kv/cnpg-immich-s3`.
Renovate updates the image tag in `compose.yaml`, but nothing deploys it: Renovate updates the image tag in `compose.yaml`, but nothing deploys it:
after merging, `docker compose pull && docker compose up -d` on the NAS. after merging, `docker compose pull && docker compose up -d` on the NAS.
+26
View File
@@ -65,3 +65,29 @@ spec:
- 169.254.0.0/16 - 169.254.0.0/16
- 172.16.0.0/12 - 172.16.0.0/12
- 192.168.0.0/16 - 192.168.0.0/16
---
# cert-manager's HTTP-01 solver, which runs only while a certificate
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
# through Envoy. The policies above let Envoy reach the app's port only,
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
# hung).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: acme-http01-solver
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: envoy-gateway-system
podSelector:
matchLabels:
app.kubernetes.io/name: envoy
ports:
- port: 8089
+22
View File
@@ -4,6 +4,7 @@
"config:recommended", "config:recommended",
"customManagers:githubActionsVersions" "customManagers:githubActionsVersions"
], ],
"timezone": "Europe/Amsterdam",
"argocd": { "argocd": {
"description": "Every app folder; Renovate's regexes are RE2, which has no lookahead, so the other folders are skipped through ignorePaths", "description": "Every app folder; Renovate's regexes are RE2, which has no lookahead, so the other folders are skipped through ignorePaths",
"managerFilePatterns": [ "managerFilePatterns": [
@@ -88,6 +89,27 @@
"major" "major"
], ],
"enabled": false "enabled": false
},
{
"description": "Patch updates of apps that hold little state and are easy to roll back, and of CI's tools, merge themselves once CI passes: three days after the release, in the evening or at the weekend, when the cluster can be reached. Not the databases, operators, Vault, authentik, Longhorn, Velero, immich, Gitea or WordPress. Renovate merges itself; the repo has no branch protection for GitHub's auto-merge to wait on",
"matchFileNames": [
"ntfy/**",
"gatus/**",
"freshrss/**",
"serpbear/**",
"actual-budget/**",
".github/workflows/**"
],
"matchUpdateTypes": [
"patch"
],
"minimumReleaseAge": "3 days",
"automerge": true,
"platformAutomerge": false,
"automergeSchedule": [
"* 17-21 * * 1-5",
"* 10-21 * * 0,6"
]
} }
] ]
} }
+26
View File
@@ -54,3 +54,29 @@ spec:
- 169.254.0.0/16 - 169.254.0.0/16
- 172.16.0.0/12 - 172.16.0.0/12
- 192.168.0.0/16 - 192.168.0.0/16
---
# cert-manager's HTTP-01 solver, which runs only while a certificate
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
# through Envoy. The policies above let Envoy reach the app's port only,
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
# hung).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: acme-http01-solver
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: envoy-gateway-system
podSelector:
matchLabels:
app.kubernetes.io/name: envoy
ports:
- port: 8089
+26
View File
@@ -66,3 +66,29 @@ spec:
- 169.254.0.0/16 - 169.254.0.0/16
- 172.16.0.0/12 - 172.16.0.0/12
- 192.168.0.0/16 - 192.168.0.0/16
---
# cert-manager's HTTP-01 solver, which runs only while a certificate
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
# through Envoy. The policies above let Envoy reach the app's port only,
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
# hung).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: acme-http01-solver
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: envoy-gateway-system
podSelector:
matchLabels:
app.kubernetes.io/name: envoy
ports:
- port: 8089