From d3798f47ace46d71f31a22fe3e409c45ba519286 Mon Sep 17 00:00:00 2001 From: Roger Oriol Date: Tue, 28 Jul 2026 00:31:23 +0200 Subject: [PATCH] fix homeassistant configuration.yaml not being writable --- homeassistant/backup-cronjob.yaml | 109 ++++++++++++++++++++++++++++++ homeassistant/homeassistant.yaml | 25 ++++++- 2 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 homeassistant/backup-cronjob.yaml diff --git a/homeassistant/backup-cronjob.yaml b/homeassistant/backup-cronjob.yaml new file mode 100644 index 0000000..3f4569e --- /dev/null +++ b/homeassistant/backup-cronjob.yaml @@ -0,0 +1,109 @@ +--- +# Home Assistant configuration backup. +# +# This CronJob copies Home Assistant backup tarballs from the config PVC +# (/config/backups) to the Synology NAS over SSH (rsync), so you have an +# off-PVC copy of your configuration. +# +# Prerequisite 1 (in the Home Assistant UI): +# Enable automatic backups so tarballs land in /config/backups: +# Settings -> System -> Backups -> (menu) -> Schedule backups +# Recommended: daily backup, e.g. "Every day at 04:00", keep the last 7. +# +# Prerequisite 2 (manual, once - secrets are NOT committed to Git): +# Create the NAS SSH credentials secret in the cluster: +# +# kubectl create secret generic home-assistant-backup-nas \ +# --from-literal=NAS_USER= \ +# --from-literal=NAS_HOST=10.88.30.10 \ +# --from-literal=NAS_PORT=22 \ +# --from-literal=NAS_PATH='/home-assistant-backups' \ +# --from-file=ssh-privatekey=$HOME/.ssh/id_rsa_nas \ +# -n home-assistant +# +# Also add the NAS to known_hosts (so rsync doesn't prompt): +# +# ssh-keyscan -p 22 -H 10.88.30.10 > /tmp/nas_known_hosts +# kubectl create configmap home-assistant-backup-known-hosts \ +# --from-file=known_hosts=/tmp/nas_known_hosts \ +# -n home-assistant +# +# The NAS_HOST above (10.88.30.10) matches the NAS IP used by the nas-proxy +# service in this repo. Adjust NAS_PATH/credentials to your Synology share. +apiVersion: batch/v1 +kind: CronJob +metadata: + name: home-assistant-backup + namespace: home-assistant +spec: + schedule: "30 4 * * *" # daily at 04:30 (after the HA 04:00 backup) + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 3 + jobTemplate: + spec: + backoffLimit: 2 + template: + spec: + restartPolicy: OnFailure + containers: + - name: backup + image: alpine:3.20 + env: + - name: NAS_USER + valueFrom: + secretKeyRef: + name: home-assistant-backup-nas + key: NAS_USER + - name: NAS_HOST + valueFrom: + secretKeyRef: + name: home-assistant-backup-nas + key: NAS_HOST + - name: NAS_PORT + valueFrom: + secretKeyRef: + name: home-assistant-backup-nas + key: NAS_PORT + - name: NAS_PATH + valueFrom: + secretKeyRef: + name: home-assistant-backup-nas + key: NAS_PATH + command: ["/bin/sh", "-c"] + args: + - | + set -e + apk add --no-cache rsync openssh-client + mkdir -p ~/.ssh + cp /ssh-keys/ssh-privatekey ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + cp /known-hosts/known_hosts ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts + echo "Syncing /config/backups -> ${NAS_USER}@${NAS_HOST}:${NAS_PATH}/" + rsync -a --delete -e "ssh -p ${NAS_PORT} -o StrictHostKeyChecking=yes" \ + /config/backups/ "${NAS_USER}@${NAS_HOST}:${NAS_PATH}/" + echo "Backup sync complete." + volumeMounts: + - name: config + mountPath: /config + readOnly: true + - name: ssh-keys + mountPath: /ssh-keys + readOnly: true + - name: known-hosts + mountPath: /known-hosts + readOnly: true + volumes: + - name: config + persistentVolumeClaim: + claimName: home-assistant-config + - name: ssh-keys + secret: + secretName: home-assistant-backup-nas + items: + - key: ssh-privatekey + path: ssh-privatekey + - name: known-hosts + configMap: + name: home-assistant-backup-known-hosts diff --git a/homeassistant/homeassistant.yaml b/homeassistant/homeassistant.yaml index b42f9b3..06a5c2f 100644 --- a/homeassistant/homeassistant.yaml +++ b/homeassistant/homeassistant.yaml @@ -66,9 +66,6 @@ spec: volumeMounts: - name: config mountPath: /config - - name: configuration - mountPath: /config/configuration.yaml - subPath: configuration.yaml - name: localtime mountPath: /etc/localtime readOnly: true @@ -82,6 +79,28 @@ spec: - NET_ADMIN - NET_RAW - SYS_ADMIN + # Seed configuration.yaml from the ConfigMap onto the writable PVC so that + # Home Assistant has a writable configuration.yaml (required for the UI to + # create/save scripts, helpers, reloads, etc.). The ConfigMap stays the + # GitOps source of truth: it is re-copied on every pod start. UI-managed + # entities (scripts, automations, integrations) live in /config/.storage + # on the PVC and are never overwritten by this step. + initContainers: + - name: seed-config + image: busybox:1.36 + command: ["/bin/sh", "-c"] + args: + - | + set -e + echo "Seeding /config/configuration.yaml from ConfigMap..." + cp /config-cm/configuration.yaml /config/configuration.yaml + echo "Done." + volumeMounts: + - name: config + mountPath: /config + - name: configuration + mountPath: /config-cm + readOnly: true hostNetwork: true volumes: - name: config