From c567e1908e12b5a2c8986225328c13ed5d2a9736 Mon Sep 17 00:00:00 2001 From: sHa Date: Tue, 24 Mar 2026 21:53:39 +0200 Subject: [PATCH] feat: Add persistent volume claim support for backups and update Helm chart version --- docs/home-assistant-issues.md | 116 +++++++++++++++++++ home-assistant/Chart.yaml | 2 +- home-assistant/templates/backup-cronjob.yaml | 5 + home-assistant/templates/deployment.yaml | 9 ++ home-assistant/templates/pvc.yaml | 50 ++++++++ home-assistant/values.yaml | 24 +++- 6 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 docs/home-assistant-issues.md diff --git a/docs/home-assistant-issues.md b/docs/home-assistant-issues.md new file mode 100644 index 0000000..1304716 --- /dev/null +++ b/docs/home-assistant-issues.md @@ -0,0 +1,116 @@ +# Home Assistant Helm Chart — Issues & Improvements + +## Defaults & Best Practices + +- [ ] **`hostNetwork: true` should default to `false`** + `hostNetwork` is only needed for HomeKit/mDNS discovery on the LAN. A clean install should not claim the host network by default. Users who need it can opt in via values. + File: `values.yaml` line 12, `deployment.yaml` lines 24-27 + +- [ ] **HomeKit should default to disabled** + HomeKit is a specific integration, not a core feature. Default `homekit.enabled: false` and let users enable it when needed. Currently exposes port 21063 on every install. + File: `values.yaml` lines 16-18, `deployment.yaml` lines 105-109 + +- [ ] **Timezone should default to `UTC`** + Currently hardcoded to `Europe/Kyiv`. Chart defaults should be locale-neutral. Users override via values for their deployment. + File: `values.yaml` line 14 + +- [ ] **Resource limits too high for default** + CPU limit of 8 cores is excessive for a default install. Consider `cpu: 2` / `memory: 2Gi` as default limits, with `cpu: 100m` / `memory: 256Mi` as requests. Production users can increase as needed. + File: `values.yaml` lines 40-46 + +- [ ] **`imagePullPolicy: Always` should be `IfNotPresent`** + For tagged images (not `latest`), `IfNotPresent` is the Kubernetes convention. `Always` causes unnecessary image pulls on every pod restart. + File: `values.yaml` line 5, `deployment.yaml` line 93 + +## Security + +- [ ] **No `securityContext` defined** + The deployment has no pod or container security context. Should add at minimum: + ```yaml + securityContext: + runAsNonRoot: false # HA needs root for s6 init + # But container-level: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + add: [NET_BIND_SERVICE] # if needed for homekit + ``` + Note: Home Assistant uses s6-overlay which requires root. Full non-root may not be feasible, but `capabilities` should still be restricted. + File: `deployment.yaml` + +- [ ] **No `ServiceAccount` created** + Chart should create a dedicated ServiceAccount with `automountServiceAccountToken: false` (HA doesn't need Kubernetes API access). This follows the principle of least privilege. + Files: new `templates/serviceaccount.yaml`, `deployment.yaml` + +## Reliability + +- [ ] **No liveness/readiness probes** + HA exposes HTTP on port 8123. The chart should define probes: + ```yaml + livenessProbe: + httpGet: + path: / + port: 8123 + initialDelaySeconds: 60 + periodSeconds: 30 + failureThreshold: 5 + readinessProbe: + httpGet: + path: / + port: 8123 + initialDelaySeconds: 30 + periodSeconds: 10 + ``` + `initialDelaySeconds` should be generous — HA can take 30-90s to start depending on integrations. Consider making these configurable via values. + File: `deployment.yaml` + +## Code Quality + +- [ ] **`busybox:latest` in init containers** + Init containers (`init-config`, `init-secrets`, `init-recorder`) use `busybox:latest` which is unpinned and not reproducible. Pin to a specific version, e.g. `busybox:1.37` and make it configurable via values. + File: `deployment.yaml` lines 31, 43, 63 + +- [ ] **`version-source` annotation in deployment template** + The `version-source: github-release:home-assistant/core` annotation is for the `just app` CLI tooling in the k3s repo, not a chart concern. Should be removed from the template and added via HelmRelease annotations or values. + File: `deployment.yaml` lines 8-9 + +## Future Enhancements + +- [ ] **Auto-generate `http.yaml` from chart values** + Same pattern as the recorder init container — generate `/config/http.yaml` and add `http: !include http.yaml` to `configuration.yaml`. This simplifies initial setup since `trusted_proxies` is required for Traefik/ingress to work and the values can be derived from the Kubernetes environment. + Generated file example: + ```yaml + server_port: 8123 + use_x_forwarded_for: true + trusted_proxies: + - 10.42.0.0/16 # k8s pod CIDR (Traefik) + - 10.43.0.0/16 # k8s service CIDR + ``` + Values structure: + ```yaml + http: + enabled: false + port: 8123 + useXForwardedFor: true + trustedProxies: [] + # - 10.42.0.0/16 + # - 10.43.0.0/16 + ``` + File: `values.yaml`, `deployment.yaml` (new init container) + +- [ ] **S3 backup support** + Add optional S3 sync as a post-backup step in the CronJob. Write locally to PVC first (fast restore), then push to S3 for disaster recovery. Values structure: + ```yaml + backup: + s3: + enabled: false + endpoint: "" + bucket: "" + existingSecret: "" + ``` + +- [ ] **NOTES.txt for post-install instructions** + Add `templates/NOTES.txt` to display useful info after install (URLs, first-time setup steps, how to access HA). + +- [ ] **Support `topologySpreadConstraints`** + For multi-node clusters, allow configuring topology spread via values (though HA is typically single-replica). diff --git a/home-assistant/Chart.yaml b/home-assistant/Chart.yaml index 5e5f906..8cca2a9 100644 --- a/home-assistant/Chart.yaml +++ b/home-assistant/Chart.yaml @@ -5,5 +5,5 @@ type: application annotations: version-source: github-release:home-assistant/core version-pattern: "s|^v||" -version: 0.1.10 +version: 0.1.13 appVersion: "2026.3.4" diff --git a/home-assistant/templates/backup-cronjob.yaml b/home-assistant/templates/backup-cronjob.yaml index 6e8fbba..72b0531 100644 --- a/home-assistant/templates/backup-cronjob.yaml +++ b/home-assistant/templates/backup-cronjob.yaml @@ -40,9 +40,14 @@ spec: mountPath: /backup volumes: - name: backup-storage + {{- if .Values.backup.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.backup.persistence.existingClaim | default (printf "%s-db-backup" (include "home-assistant.fullname" .)) }} + {{- else }} hostPath: path: {{ .Values.backup.storagePath }} type: DirectoryOrCreate + {{- end }} restartPolicy: OnFailure {{- if .Values.backup.node }} affinity: diff --git a/home-assistant/templates/deployment.yaml b/home-assistant/templates/deployment.yaml index 8addcf2..33ed5de 100644 --- a/home-assistant/templates/deployment.yaml +++ b/home-assistant/templates/deployment.yaml @@ -119,6 +119,10 @@ spec: - name: media mountPath: {{ .Values.persistence.media.mountPath }} {{- end }} + {{- if .Values.persistence.backups.enabled }} + - name: backups + mountPath: /config/backups + {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} volumes: @@ -144,6 +148,11 @@ spec: claimName: {{ .Values.persistence.media.existingClaim | default (printf "%s-media" (include "home-assistant.fullname" .)) }} {{- end }} {{- end }} + {{- if .Values.persistence.backups.enabled }} + - name: backups + persistentVolumeClaim: + claimName: {{ .Values.persistence.backups.existingClaim | default (printf "%s-backups" (include "home-assistant.fullname" .)) }} + {{- end }} {{- if .Values.haSecrets.enabled }} - name: ha-secrets secret: diff --git a/home-assistant/templates/pvc.yaml b/home-assistant/templates/pvc.yaml index d46e345..2280702 100644 --- a/home-assistant/templates/pvc.yaml +++ b/home-assistant/templates/pvc.yaml @@ -46,4 +46,54 @@ spec: {{- end }} {{- end }} {{- end }} +--- +{{- if .Values.persistence.backups.enabled }} +{{- if eq (.Values.persistence.backups.type | default "pvc") "pvc" }} +{{- if not .Values.persistence.backups.existingClaim }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "home-assistant.fullname" . }}-backups + {{- if .Values.namespaceOverride }} + namespace: {{ .Values.namespaceOverride }} + {{- end }} + labels: + {{- include "home-assistant.labels" . | nindent 4 }} +spec: + accessModes: + {{- toYaml (.Values.persistence.backups.accessModes | default (list "ReadWriteOnce")) | nindent 4 }} + resources: + requests: + storage: {{ .Values.persistence.backups.size | default "10Gi" | quote }} + {{- if .Values.persistence.backups.storageClass }} + storageClassName: {{ .Values.persistence.backups.storageClass | quote }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +--- +{{- if .Values.backup.enabled }} +{{- if .Values.backup.persistence.enabled }} +{{- if not .Values.backup.persistence.existingClaim }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "home-assistant.fullname" . }}-db-backup + {{- if .Values.namespaceOverride }} + namespace: {{ .Values.namespaceOverride }} + {{- end }} + labels: + {{- include "home-assistant.labels" . | nindent 4 }} +spec: + accessModes: + {{- toYaml (.Values.backup.persistence.accessModes | default (list "ReadWriteOnce")) | nindent 4 }} + resources: + requests: + storage: {{ .Values.backup.persistence.size | default "10Gi" | quote }} + {{- if .Values.backup.persistence.storageClass }} + storageClassName: {{ .Values.backup.persistence.storageClass | quote }} + {{- end }} +{{- end }} +{{- end }} {{- end }} diff --git a/home-assistant/values.yaml b/home-assistant/values.yaml index 798c42c..c2e8635 100644 --- a/home-assistant/values.yaml +++ b/home-assistant/values.yaml @@ -48,17 +48,29 @@ resources: persistence: config: enabled: true - type: hostPath - hostPath: /srv/data/home-assistant/config + type: pvc mountPath: /config size: 10Gi accessModes: - ReadWriteOnce storageClass: "" + # hostPath: /srv/data/home-assistant/config # only when type: hostPath media: enabled: true - hostPath: /srv/data/home-assistant/media + type: pvc mountPath: /media + size: 5Gi + accessModes: + - ReadWriteOnce + storageClass: "" + # hostPath: /srv/data/home-assistant/media # only when type: hostPath + backups: + enabled: false + type: pvc + size: 10Gi + accessModes: + - ReadWriteOnce + storageClass: "" haSecrets: enabled: false @@ -97,6 +109,12 @@ backup: storagePath: /storage/backups/postgres # Node to run backup on (hostname). Overrides global affinity/tolerations. node: "" + persistence: + enabled: false + size: 10Gi + accessModes: + - ReadWriteOnce + storageClass: "" postgres: host: postgres-tcp.postgres.svc.cluster.local db: homeassistant