Author SHA1 Message Date
renovate c34da61965 chore(k8s/jellyfin): update alpine docker tag to v3.24 2026-09-13 18:00:29 +00:00
oleksandr 848b58e289 feat(k8s/jellyfin): upgrade to Jellyfin 12.0 with migration startupProbe 2026-09-13 16:20:43 +03:00
oleksandr 8d99b0f7fe feat(k8s/jellyfin): add restic backup for database and configuration 2026-09-13 16:14:04 +03:00
renovate c34dc8790e chore(k8s/lubelogger): update ghcr.io/hargata/lubelogger docker tag to v1.7.3 2026-09-12 18:01:16 +00:00
oleksandr f8de4902fc fix(k8s/jellyfin): use MINOR.PATCH regex versioning for Jellyfin 12+ 2026-09-11 15:40:45 +03:00
oleksandr 820358e3a7 fix(k8s/homepage): allow pod IP host for probes per upstream k8s docs 2026-09-11 15:32:46 +03:00
oleksandr 492f59532e fix(k8s/homepage): send allowed Host header in probes for v2 2026-09-11 15:29:00 +03:00
renovate 1bb0e7628c chore(k8s/paperless): update ghcr.io/paperless-ngx/paperless-ngx docker tag to v3 2026-09-11 12:22:15 +00:00
renovate 6d6f3c66b3 chore(k8s/homepage): update ghcr.io/gethomepage/homepage docker tag to v2 2026-09-11 12:20:54 +00:00
renovate a93ff085ce chore(k8s/paperless): update apache/tika docker tag to v3.3.1.0 2026-09-11 12:20:10 +00:00
renovate 5234a954d0 chore(k8s/lubelogger): update ghcr.io/hargata/lubelogger docker tag to v1.7.2 2026-09-11 12:20:01 +00:00
renovate af774e543b chore(k8s/homepage): update ghcr.io/gethomepage/homepage docker tag to v1.13.2 2026-09-11 12:15:26 +00:00
renovate 443f0efd47 chore(k8s/firefly): update fireflyiii/core docker tag to version-6.6.6 2026-09-11 12:15:19 +00:00
10 changed files with 242 additions and 9 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ spec:
image: image:
# renovate: datasource=docker depName=fireflyiii/core # renovate: datasource=docker depName=fireflyiii/core
tag: version-6.5.9 tag: version-6.6.6
podSecurityContext: podSecurityContext:
seccompProfile: seccompProfile:
+8 -4
View File
@@ -24,7 +24,7 @@ spec:
type: RuntimeDefault type: RuntimeDefault
containers: containers:
- name: homepage - name: homepage
image: ghcr.io/gethomepage/homepage:v1.10.1 image: ghcr.io/gethomepage/homepage:v2.3.0
ports: ports:
- containerPort: 3000 - containerPort: 3000
name: http name: http
@@ -33,8 +33,12 @@ spec:
- secretRef: - secretRef:
name: homepage-credentials name: homepage-credentials
env: env:
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: HOMEPAGE_ALLOWED_HOSTS - name: HOMEPAGE_ALLOWED_HOSTS
value: "${HOMEPAGE_HOST}" value: "$(MY_POD_IP):3000,${HOMEPAGE_HOST}"
volumeMounts: volumeMounts:
- name: config - name: config
mountPath: /app/config/settings.yaml mountPath: /app/config/settings.yaml
@@ -54,13 +58,13 @@ spec:
livenessProbe: livenessProbe:
httpGet: httpGet:
port: 3000 port: 3000
path: / path: /api/healthcheck
initialDelaySeconds: 30 initialDelaySeconds: 30
periodSeconds: 30 periodSeconds: 30
readinessProbe: readinessProbe:
httpGet: httpGet:
port: 3000 port: 3000
path: / path: /api/healthcheck
initialDelaySeconds: 10 initialDelaySeconds: 10
periodSeconds: 10 periodSeconds: 10
resources: resources:
+163
View File
@@ -0,0 +1,163 @@
# Jellyfin SQLite database backup.
# jellyfin.db lives on the local-path PVC (/config/data/jellyfin.db is a symlink
# to it), so it is read from jellyfin-data directly. sqlite3 ".backup" uses the
# online backup API — no downtime, consistent snapshot while Jellyfin runs.
# The volume is mounted read-write on purpose: jellyfin.db is in WAL mode, and
# SQLite cannot open a WAL database from a read-only filesystem once Jellyfin has
# closed it and removed the -wal/-shm files (e.g. while scaled to zero).
apiVersion: batch/v1
kind: CronJob
metadata:
name: jellyfin-db-backup
namespace: jellyfin
labels:
app: jellyfin-backup
spec:
schedule: "30 2 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
metadata:
labels:
app: jellyfin-backup
spec:
restartPolicy: OnFailure
initContainers:
- name: sqlite-backup
image: alpine:3.24
command:
- sh
- -c
- |
set -e
apk add --no-cache sqlite
sqlite3 /db-local/jellyfin.db ".backup /backup/jellyfin.db"
volumeMounts:
- name: db
mountPath: /db-local
- name: backup-tmp
mountPath: /backup
containers:
- name: resticprofile
image: creativeprojects/resticprofile:0.32.0
command:
- sh
- -c
- |
resticprofile -c /secrets/profiles.yaml -n jellyfin-db backup
resticprofile -c /secrets/profiles.yaml -n jellyfin-db copy
env:
- name: B2_ACCOUNT_ID
valueFrom:
secretKeyRef:
name: jellyfin-backup-config
key: B2_ACCOUNT_ID
- name: B2_ACCOUNT_KEY
valueFrom:
secretKeyRef:
name: jellyfin-backup-config
key: B2_ACCOUNT_KEY
volumeMounts:
- name: secrets
mountPath: /secrets
readOnly: true
- name: backup-tmp
mountPath: /backup
volumes:
- name: secrets
secret:
secretName: jellyfin-backup-config
- name: db
persistentVolumeClaim:
claimName: jellyfin-data
- name: backup-tmp
emptyDir: {}
---
# Jellyfin configuration backup.
# Only non-regenerable state: server config, library definitions, plugin
# configuration (including the Authelia SSO settings), collections, and the
# Intro Skipper analysis results. Metadata (27G), trickplay (20G) and extracted
# subtitles (1.7G) are deliberately excluded — they are regenerable from the
# media files and the metadata providers.
# Intro Skipper keeps its results in two live WAL-mode SQLite databases, so they
# are snapshotted with sqlite3 ".backup" rather than copied as files (which could
# capture a torn page mid-write). Read-write mount for the same reason as above.
apiVersion: batch/v1
kind: CronJob
metadata:
name: jellyfin-config-backup
namespace: jellyfin
labels:
app: jellyfin-backup
spec:
schedule: "30 3 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
metadata:
labels:
app: jellyfin-backup
spec:
restartPolicy: OnFailure
initContainers:
- name: sqlite-backup
image: alpine:3.24
command:
- sh
- -c
- |
set -e
apk add --no-cache sqlite
mkdir -p /backup/introskipper
for db in introskipper.db introskipper-cache.db; do
sqlite3 "/config/data/introskipper/$db" ".backup /backup/introskipper/$db"
done
volumeMounts:
- name: config
mountPath: /config
- name: backup-tmp
mountPath: /backup
containers:
- name: resticprofile
image: creativeprojects/resticprofile:0.32.0
command:
- sh
- -c
- |
resticprofile -c /secrets/profiles.yaml -n jellyfin-config backup
resticprofile -c /secrets/profiles.yaml -n jellyfin-config copy
env:
- name: B2_ACCOUNT_ID
valueFrom:
secretKeyRef:
name: jellyfin-backup-config
key: B2_ACCOUNT_ID
- name: B2_ACCOUNT_KEY
valueFrom:
secretKeyRef:
name: jellyfin-backup-config
key: B2_ACCOUNT_KEY
volumeMounts:
- name: secrets
mountPath: /secrets
readOnly: true
- name: config
mountPath: /config
readOnly: true
- name: backup-tmp
mountPath: /backup
volumes:
- name: secrets
secret:
secretName: jellyfin-backup-config
- name: config
persistentVolumeClaim:
claimName: jellyfin-config
- name: backup-tmp
emptyDir: {}
+10 -1
View File
@@ -43,7 +43,7 @@ spec:
mountPath: /db-local mountPath: /db-local
containers: containers:
- name: jellyfin - name: jellyfin
image: jellyfin/jellyfin:10.11.11 image: jellyfin/jellyfin:12.0
env: env:
- name: JELLYFIN_PublishedServerUrl - name: JELLYFIN_PublishedServerUrl
value: https://${JELLYFIN_HOST} value: https://${JELLYFIN_HOST}
@@ -62,6 +62,15 @@ spec:
resources: resources:
limits: limits:
gpu.intel.com/i915: "1" gpu.intel.com/i915: "1"
# 12.0's first boot runs one-way database migrations that take several
# minutes and must not be interrupted. startupProbe holds liveness off
# until the server answers; liveness/readiness take over once up.
startupProbe:
httpGet:
port: 8096
path: /health
failureThreshold: 90
periodSeconds: 10
livenessProbe: livenessProbe:
httpGet: httpGet:
port: 8096 port: 8096
@@ -45,3 +45,29 @@ spec:
protocol: UDP protocol: UDP
- port: 1900 - port: 1900
protocol: UDP protocol: UDP
---
# Backup jobs need DNS, the Synology rest-server (8888) and B2 over HTTPS.
# Declaring egress here also confines the backup pods to exactly those.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-backup-egress
namespace: jellyfin
spec:
podSelector:
matchLabels:
app: jellyfin-backup
policyTypes:
- Egress
egress:
- ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
- ports:
- port: 8888
protocol: TCP
- ports:
- port: 443
protocol: TCP
+5
View File
@@ -7,6 +7,11 @@
"matchFileNames": ["kubernetes/app/jellyfin/**"], "matchFileNames": ["kubernetes/app/jellyfin/**"],
"semanticCommitScope": "k8s/jellyfin" "semanticCommitScope": "k8s/jellyfin"
}, },
{
"matchFileNames": ["kubernetes/app/jellyfin/**"],
"matchPackageNames": ["jellyfin/jellyfin"],
"versioning": "regex:^(?<minor>\\d+)\\.(?<patch>\\d+)$"
},
{ {
"matchFileNames": ["kubernetes/app/jellyfin/**"], "matchFileNames": ["kubernetes/app/jellyfin/**"],
"matchPackageNames": ["jellyfin/jellyfin"], "matchPackageNames": ["jellyfin/jellyfin"],
@@ -0,0 +1,26 @@
apiVersion: v1
kind: Secret
metadata:
name: jellyfin-backup-config
namespace: jellyfin
stringData:
profiles.yaml: ENC[AES256_GCM,data:A1EdRDhXncj5EIjg6iyw+mmiW7ObrGVlBmnQn1GJbF6P+yHmw97h4Hk/yHfMM1tDrNhiy8jpMWmXHGuHa56QFGPQzPCkaMtjqwO2pUcXrdrv8ThVtX3ZgyzvHX1fL5lwDm5aG0mMjuV51+j5URR6P3n+GCCg3OYP/DbEngroT5f7QKhFb4hwHdGnF9tHLwKH/hZrXJIH8XLeJD5BbjHBRFiXEpYlgmHtZ3Vb3SzJRMSMBMH4wBj2obxL2nOqKDXaK/NRV1XHuN+/enKvQaFm9j3h7F4G4MD1Gfr6LwjtGiV5S8ngcQo9gQptardTVqxTtKHKA80pbtSoqwP3TuR6Vh6U9KNqDiaLte6wqoOxQ6Tb3oCTnFJOLn1WE/PBxTPp9VDRfwXBlhPtYK8JhaTcQHdjkM1xw3qzyL09XMNKWf1wvlaoszurwlxI+bSKNlDQjC/dUd/j067xFf1SRNze++75QJkrp1o2+4DCrdK4QiPd9FeacgUml/uI957i0ZkBQLTn9WHUcp7kLmALzu8byDHgf2jkHYEfJ3QKg5yjpE0aQ2Iub/8fnQC732YOUGeP5HKcFTl8fU4xdRrIPbz8K728FYukeU4pzLQY+2qOENfMSFoVMIYpD5LSyv+RqIejb6x1ZYIchPQ91aBsnkXRSL6b2Lj60vccF1cyilrPAgBAzM6FeAvAHqbalSMdvyMwd6AgHgW0jOE9dGquxQfliCyoU9vcsNKY/2y01MWiH7wa80IjvrrUTiuQHh7h2g+JYqiXoyA2R9qXQBScjiWQbtUAYQjBqqKGdTSxiUzYiCBw75FMJxj2BUE739bQQNCBN18WLSyietS7LiTu50cL0jD79zNYGqi4vduHdK+MZuhZa5I/5OoHo71VGj1nkyKYBOemA5zdW3xbS4ZOnblOYeEkhx0It1KafAbN4JnY7ZPCQqH7JyAxKIV1YR4zKEMpFgEzIKUFl0Cr7PrZ5/hoA6HhuzzWq6GvtQ+4PJusp5Spu+d7AEZLSPqPrSlK6iCQA3nKG+A/wau4BI4P83Q2LzW5Aa4aujNZJ+mCYAyPkkQqt6+MTWgFCKa61fZT8Rypw+q1+BRJrY+/2PU/DUeEBb6hBTcL24YRHEkcPlcU4FkK4/WppALM/FWySFgfdwL9XqhOWwuhJ3nfMFEMFR9TkbrTHGNzC7JRTcIo4FIhvgoNtQ2WujgB01a79cqGcw2sSWDTeUCH5SaeYiQSfmI1n7OyBP9vk3pc8FrqRVZkpetlrYCdart6FKDuO+P4YhOpk42GrL+0lWis4Hkmos9REpKP0jAkvgcI9nDHTq1stHbPuqVXsBaah0mrbk450ZAREmGMx/4Gc3ltXh8YfgT26HSDCl3SRtXgnLsTA9XKYgR1zgvT8a82EYDg08af5X1yGQFutxIkfIwIijAIx+Y9NY6KeWvgBYxjgkKkNvY29kCZZ5POKw5L9YiMOcp4jSTK+dNdPqCWplSzyaLgrFGBbzOcQVagm9UkZEXLv44H60fMMExdHzuo81Bh1JV6luVqqDcHlnigxM6QNMNavgT6G60kFfRmEyRldSrpefKkH9GtxJMJgWMgZhhdDkkOzFsou/GuaWCLQp3JyOmZiKisJu5Nytkl4h0rPGR+Oe26Z5IxCYOjRgmp8+TT4JMmHIvlL7Ljf1PRQS6eWz9ICvoK/nvLaj0/rjEvQ9BZKY1WVzGGSYrvrNzNbprhaT6+DE1Xdd9qmwaP+B9czB54KCrKd17knL0GWYkneOM/M+UNeD4nDeK8fShHJeIYI5+7QRh54BsGlmwgw8mYnysagsQeav56aZC7Lcg3e1uCeVSHY7RdrUJ7i+++5v95TpIP8tQCFO5RvlIp7UPChdHOyxqlGqGMWs66Jvpq47ENN6OObAdR0uAHPA/MsvVO0ygmANE8Sog2JLJbQO6+3Wsmg/n+Is2Ps1lEjSft6Kn/SrmPG3ilOedpirrnn1cfWOHAXtfruZ9xB2GIdhBHgVFgjSUmAvd+9R+MV+urq3d/Czr1+w2WDryOPPw=,iv:+DuENxnJKTQ3ZKom2uNsW7QU1eMsrO+ZFCo5aW51pwE=,tag:4MiKklkRuvJuCvlu8tV07w==,type:str]
restic-password-nas: ENC[AES256_GCM,data:2uDszBmHa4YG1oYyhN4Wjw0Lcc6/MpmrCHldwNF22kFiokbx4R+zrd8ML4NvK1ezmQmMDhlb3RsyAcQNRgJ9Ng==,iv:96pdW1B6M97+eG1EC2YPbNmO4OQmt47uiJOWuvzdYzE=,tag:dQKHD+2LeWL5+tcQbrbN0g==,type:str]
restic-password-b2: ENC[AES256_GCM,data:ceHYbKn174cIm38qnlGvx9edxP8apPWkwx7wAkNncSF6kIE5OvXVsNAWcjsZKQLEgYXFX5NwLZMDPY0edtAhNQ==,iv:mDki/Soljcnp60cfihK2EqfTNHv/VnoieetBThFqLtE=,tag:5VMRM0fe4nFDal3BO+ninw==,type:str]
B2_ACCOUNT_ID: ENC[AES256_GCM,data:6u+UlYRILbb0TRTdISi7TdVItw4omfodFw==,iv:A+be6o3ZjfYsyvEk750DcoSlBsKYM2FncSY8kqkcpqs=,tag:9ReTmil5sAfo+04IfdaKYw==,type:str]
B2_ACCOUNT_KEY: ENC[AES256_GCM,data:p+irC0iQR3RjhmElD+tZcXcgWsV8ROGHsEOJ7kxXvg==,iv:2VgptxIaJ0v1e5jejLyBIPHnvxD6ldgTWn/3oa1M32Y=,tag:9gHDOD0524IAoQQLOytT+w==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5Mk5NMVNxOG5JWHhkMjlh
YzRyYVhYT2tncmVzRzB5NXc2aEZQcUVtVFUwCnl2U2JQSkdpTjZVb1pPTmlPeVRt
SURJNFFzUTFTdVZpckV5d2puN2xQTmcKLS0tIDUxeXFLdGxOdDVLdng2SFRWb296
NFpDSkZmYkY0Vyt4Nml3N3VXbFdZZ0kKfNoqtyb+16qpDdCHgp3uiDCHqtiDXEjB
p1kaQkgF6xqFohomV+6x0RsW+r7PaGh8oWRut0NBRahZwIKBucZUTw==
-----END AGE ENCRYPTED FILE-----
recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc
encrypted_regex: ^(data|stringData|email)$
lastmodified: "2026-09-13T13:09:04Z"
mac: ENC[AES256_GCM,data:E8xZZ+0hdRPKE/nss00gopEg7feF7t4pXBqYkEtr9yzHn1Vz4Opfv4vBDDgwqntZdekE40rpzWTuPJDAfOrhDsCsgZjOgWa6VpSRNM9GQ37Ax/fv/G42CXtwBPCzY1p2GuXr9nsJI+/P4c6sx4FIXZ0o4QLCHuStkYpjbHlyIFA=,iv:LJCUP/KbZ0tZPdG2hedHfZMfYWS+w4T73JaI7nLe2uQ=,tag:J7GT2jm6iHFZ8oMV2ecEIw==,type:str]
version: 3.13.3
+1 -1
View File
@@ -18,7 +18,7 @@ spec:
values: values:
image: image:
# renovate: datasource=docker depName=ghcr.io/hargata/lubelogger # renovate: datasource=docker depName=ghcr.io/hargata/lubelogger
tag: "v1.6.1" tag: "v1.7.3"
updateStrategy: updateStrategy:
type: RollingUpdate type: RollingUpdate
@@ -21,7 +21,7 @@ spec:
type: RuntimeDefault type: RuntimeDefault
containers: containers:
- name: tika - name: tika
image: apache/tika:3.2.3.0 image: apache/tika:3.3.1.0
env: env:
- name: TZ - name: TZ
value: Europe/Kyiv value: Europe/Kyiv
+1 -1
View File
@@ -24,7 +24,7 @@ spec:
type: RuntimeDefault type: RuntimeDefault
containers: containers:
- name: paperless - name: paperless
image: ghcr.io/paperless-ngx/paperless-ngx:2.20 image: ghcr.io/paperless-ngx/paperless-ngx:3.1
ports: ports:
- containerPort: 8000 - containerPort: 8000
name: http name: http