├── charts └── home-assistant │ ├── ci │ ├── ha-values.yaml │ ├── default-values.yaml │ ├── addons-enabled-values.yaml │ ├── ingress-external-values.yaml │ ├── additional-env-values.yaml │ ├── addons-persistence-values.yaml │ ├── security-context-values.yaml │ ├── 01-hass-configuration-values.yaml │ ├── custom-resources-values.yaml │ ├── 03-hass-configuration-values.yaml │ ├── persistence-enabled-values.yaml │ ├── 02-hass-configuration-values.yaml │ ├── statefulset-persistence-values.yaml │ ├── advanced-networking-values.yaml │ ├── startupProbe-values.yaml │ ├── 04-hass-configuration-values.yaml │ ├── deployment-controller-values.yaml │ ├── ingress-custom-values.yaml │ ├── 05-hass-configuration-values.yaml │ ├── addon-vscode-additional-mount-values.yaml │ ├── addon-codeserver-auth-values.yaml │ └── addon-codeserver-auth-disabled-values.yaml │ ├── templates │ ├── configmap-init-script.yaml │ ├── configmap-hass-config.yaml │ ├── serviceaccount.yaml │ ├── tests │ │ └── test-connection.yaml │ ├── service-codeserver.yaml │ ├── service-monitor.yaml │ ├── pvc.yaml │ ├── service.yaml │ ├── ingress.yaml │ ├── ingress-codeserver.yaml │ ├── _helpers.tpl │ ├── NOTES.txt │ ├── deployment.yaml │ └── statefulset.yaml │ ├── .helmignore │ ├── Chart.yaml │ ├── values.yaml │ └── README.md ├── renovate.json ├── LICENSE ├── .github └── workflows │ ├── changelog.yaml │ ├── update_version.py │ ├── lint-test.yaml │ ├── build-helm-chart-release.yaml │ └── check_ha_release.yml └── README.md /charts/home-assistant/ci/ha-values.yaml: -------------------------------------------------------------------------------- 1 | replicaCount: 3 2 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/default-values.yaml: -------------------------------------------------------------------------------- 1 | # No overrides provided; using chart defaults. 2 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/addons-enabled-values.yaml: -------------------------------------------------------------------------------- 1 | addons: 2 | codeserver: 3 | enabled: true 4 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/ingress-external-values.yaml: -------------------------------------------------------------------------------- 1 | ingress: 2 | enabled: false 3 | external: true 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/additional-env-values.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | - name: TZ 3 | value: "America/New_York" 4 | - name: DEBUG_MODE 5 | value: "true" 6 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/addons-persistence-values.yaml: -------------------------------------------------------------------------------- 1 | persistence: 2 | enabled: true 3 | size: "8Gi" 4 | addons: 5 | codeserver: 6 | enabled: true 7 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/security-context-values.yaml: -------------------------------------------------------------------------------- 1 | securityContext: 2 | runAsUser: 1000 3 | runAsNonRoot: true 4 | podAnnotations: 5 | hello: world 6 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/01-hass-configuration-values.yaml: -------------------------------------------------------------------------------- 1 | # test case 01: Test the default values of the configuration 2 | configuration: 3 | enabled: true 4 | forceInit: false 5 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/custom-resources-values.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | requests: 3 | cpu: "250m" 4 | memory: "256Mi" 5 | limits: 6 | cpu: "500m" 7 | memory: "512Mi" 8 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/03-hass-configuration-values.yaml: -------------------------------------------------------------------------------- 1 | # test case 03: Test the configuration with forceInit set to true 2 | configuration: 3 | enabled: true 4 | forceInit: true 5 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/persistence-enabled-values.yaml: -------------------------------------------------------------------------------- 1 | persistence: 2 | enabled: true 3 | size: "10Gi" 4 | annotations: 5 | backup: "true" 6 | second-annotation: "another value" 7 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/02-hass-configuration-values.yaml: -------------------------------------------------------------------------------- 1 | # Test case 02: Test for setting trusted_proxies and use_x_forwarded_for 2 | ingress: 3 | enabled: true 4 | configuration: 5 | enabled: true 6 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/statefulset-persistence-values.yaml: -------------------------------------------------------------------------------- 1 | # Test values for StatefulSet with persistence 2 | controller: 3 | type: StatefulSet 4 | 5 | # Enable persistence 6 | persistence: 7 | enabled: true 8 | size: 1Gi 9 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/advanced-networking-values.yaml: -------------------------------------------------------------------------------- 1 | hostNetwork: true 2 | dnsConfig: 3 | nameservers: 4 | - 8.8.8.8 5 | - 8.8.4.4 6 | options: 7 | - name: ndots 8 | value: "1" 9 | hostPort: 10 | enabled: true 11 | port: 8123 12 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/startupProbe-values.yaml: -------------------------------------------------------------------------------- 1 | startupProbe: 2 | initialDelaySeconds: 1 3 | periodSeconds: 5 4 | timeoutSeconds: 1 5 | successThreshold: 1 6 | failureThreshold: 1 7 | httpGet: 8 | scheme: HTTP 9 | path: / 10 | port: http 11 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/04-hass-configuration-values.yaml: -------------------------------------------------------------------------------- 1 | # Test case 04: Test for setting custom trusted_proxies and use_x_forwarded_for 2 | ingress: 3 | enabled: true 4 | configuration: 5 | enabled: true 6 | forceInit: true 7 | trusted_proxies: 8 | - 172.16.100.0/24 9 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/configmap-init-script.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.configuration.enabled -}} 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: init-script 6 | namespace: {{ .Release.Namespace }} 7 | data: 8 | init.sh: | 9 | {{- tpl .Values.configuration.initScript . | nindent 4 }} 10 | {{- end -}} -------------------------------------------------------------------------------- /charts/home-assistant/templates/configmap-hass-config.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.configuration.enabled -}} 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: hass-configuration 6 | namespace: {{ .Release.Namespace }} 7 | data: 8 | configuration.yaml: | 9 | {{- tpl .Values.configuration.templateConfig . | nindent 4 }} 10 | {{- end -}} -------------------------------------------------------------------------------- /charts/home-assistant/ci/deployment-controller-values.yaml: -------------------------------------------------------------------------------- 1 | # Test values for using Deployment instead of StatefulSet 2 | controller: 3 | type: Deployment 4 | 5 | # Enable persistence to test PVC creation 6 | persistence: 7 | enabled: true 8 | size: 1Gi 9 | 10 | # Add some deployment annotations 11 | deploymentAnnotations: 12 | test: "true" 13 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "home-assistant.serviceAccountName" . }} 6 | labels: 7 | {{- include "home-assistant.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} -------------------------------------------------------------------------------- /charts/home-assistant/ci/ingress-custom-values.yaml: -------------------------------------------------------------------------------- 1 | ingress: 2 | enabled: true 3 | annotations: 4 | kubernetes.io/ingress.class: nginx 5 | cert-manager.io/cluster-issuer: "letsencrypt-prod" 6 | hosts: 7 | - host: homeassistant.example.com 8 | paths: 9 | - path: / 10 | pathType: ImplementationSpecific 11 | tls: 12 | - secretName: homeassistant-tls 13 | hosts: 14 | - homeassistant.example.com 15 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/05-hass-configuration-values.yaml: -------------------------------------------------------------------------------- 1 | # Test case 05: Test for setting custom template configuration and init script 2 | configuration: 3 | enabled: true 4 | forceInit: false 5 | templateConfig: |- 6 | default_config: 7 | automation: !include automations.yaml 8 | script: !include scripts.yaml 9 | scene: !include scenes.yaml 10 | initScript: |- 11 | #!/bin/bash 12 | echo "Custom trusted proxies init script" 13 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/addon-vscode-additional-mount-values.yaml: -------------------------------------------------------------------------------- 1 | # Addons configuration for additional services 2 | addons: 3 | # Code-server addon configuration 4 | codeserver: 5 | # if you need any additional volume mounts, you can define them here 6 | additionalMounts: 7 | - mountPath: /home/coder/.ssh/id_rsa 8 | name: id-rsa 9 | 10 | # empty dir volume for id_rsa 11 | additionalVolumes: 12 | - name: id-rsa 13 | emptyDir: {} 14 | -------------------------------------------------------------------------------- /charts/home-assistant/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "home-assistant.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "home-assistant.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "home-assistant.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/addon-codeserver-auth-values.yaml: -------------------------------------------------------------------------------- 1 | # CI test configuration for code-server addon with authentication enabled 2 | persistence: 3 | enabled: true 4 | size: 1Gi 5 | 6 | addons: 7 | codeserver: 8 | enabled: true 9 | auth: 10 | enabled: true 11 | password: "test-password-123" 12 | service: 13 | type: ClusterIP 14 | ingress: 15 | enabled: true 16 | hosts: 17 | - host: code-server-test.local 18 | paths: 19 | - path: / 20 | pathType: ImplementationSpecific 21 | -------------------------------------------------------------------------------- /charts/home-assistant/ci/addon-codeserver-auth-disabled-values.yaml: -------------------------------------------------------------------------------- 1 | # CI test configuration for code-server addon with authentication disabled 2 | # This tests backward compatibility for users who want no authentication 3 | persistence: 4 | enabled: true 5 | size: 1Gi 6 | 7 | addons: 8 | codeserver: 9 | enabled: true 10 | auth: 11 | enabled: false 12 | service: 13 | type: ClusterIP 14 | ingress: 15 | enabled: true 16 | hosts: 17 | - host: code-server-test.local 18 | paths: 19 | - path: / 20 | pathType: ImplementationSpecific 21 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/service-codeserver.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.addons.codeserver.enabled -}} 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ include "home-assistant.fullname" . }}-codeserver 6 | labels: 7 | {{- include "home-assistant.labels" . | nindent 4 }} 8 | spec: 9 | type: {{ .Values.addons.codeserver.service.type }} 10 | ports: 11 | - port: {{ .Values.addons.codeserver.service.port }} 12 | targetPort: codeserver 13 | protocol: TCP 14 | name: codeserver 15 | selector: 16 | {{- include "home-assistant.selectorLabels" . | nindent 4 }} 17 | {{- end }} 18 | -------------------------------------------------------------------------------- /charts/home-assistant/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | appVersion: 2025.12.4 3 | description: Automatically Updated Helm Chart for Home Assistant 4 | home: https://github.com/pajikos/home-assistant-helm-chart 5 | icon: https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Home_Assistant_Logo.svg/519px-Home_Assistant_Logo.svg.png 6 | keywords: 7 | - home-assistant 8 | - hass 9 | - homeassistant 10 | kubeVersion: '>=1.16.0-0' 11 | maintainers: 12 | - email: sklenar.pav@gmail.com 13 | name: pajikos 14 | name: home-assistant 15 | sources: 16 | - https://github.com/pajikos/home-assistant-helm-chart 17 | - https://github.com/cdr/code-server 18 | - https://github.com/pajikos/home-assistant-helm-chart/tree/main/charts/home-assistant 19 | type: application 20 | version: 0.3.36 21 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/service-monitor.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.serviceMonitor.enabled }} 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: ServiceMonitor 4 | metadata: 5 | name: {{ include "home-assistant.fullname" . }} 6 | labels: 7 | {{- include "home-assistant.labels" . | nindent 4 }} 8 | {{- with .Values.serviceMonitor.labels }} 9 | {{ toYaml . | nindent 4 }} 10 | {{- end }} 11 | spec: 12 | endpoints: 13 | - interval: {{ .Values.serviceMonitor.scrapeInterval }} 14 | port: http 15 | path: /api/prometheus 16 | {{- if and .Values.serviceMonitor.bearerToken (and .Values.serviceMonitor.bearerToken.secretName .Values.serviceMonitor.bearerToken.secretKey) }} 17 | bearerTokenSecret: 18 | name: {{ .Values.serviceMonitor.bearerToken.secretName }} 19 | key: {{ .Values.serviceMonitor.bearerToken.secretKey }} 20 | {{- end }} 21 | namespaceSelector: 22 | matchNames: 23 | - {{ .Release.Namespace }} 24 | selector: 25 | matchLabels: 26 | {{- include "home-assistant.selectorLabels" . | nindent 6 }} 27 | {{- end }} 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Pavel Sklenář 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yaml: -------------------------------------------------------------------------------- 1 | name: 'Changelogs' 2 | on: 3 | push: 4 | tags: 5 | - '*' 6 | 7 | jobs: 8 | release: 9 | if: startsWith(github.ref, 'refs/tags/') 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Build Changelog 13 | id: github_release 14 | uses: mikepenz/release-changelog-builder-action@v5 15 | with: 16 | configurationJson: | 17 | { 18 | "pr_template": "- #{{TITLE}}", 19 | "tag_resolver": { 20 | "method": "semver", 21 | "transformer": { 22 | "pattern": "home-assistant-(.+)", 23 | "target": "$1" 24 | } 25 | } 26 | } 27 | mode: "COMMIT" 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | 31 | - name: Print output 32 | run: echo "${{steps.github_release.outputs.changelog}}" 33 | 34 | - name: Create Release 35 | uses: mikepenz/action-gh-release@v0.2.0-a03 #softprops/action-gh-release 36 | with: 37 | body: ${{steps.github_release.outputs.changelog}} 38 | -------------------------------------------------------------------------------- /.github/workflows/update_version.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | import sys 3 | from pathlib import Path 4 | 5 | def increment_semver(version, version_part): 6 | major, minor, patch = map(int, version.split(".")) 7 | 8 | if version_part == "major": 9 | major += 1 10 | minor, patch = 0, 0 11 | elif version_part == "minor": 12 | minor += 1 13 | patch = 0 14 | elif version_part == "patch": 15 | patch += 1 16 | else: 17 | raise ValueError(f"Invalid version part: {version_part}") 18 | 19 | return f"{major}.{minor}.{patch}" 20 | 21 | def update_version_in_yaml(file_path, version_part): 22 | with open(file_path, "r") as file: 23 | data = yaml.safe_load(file) 24 | 25 | current_version = data["version"] 26 | new_version = increment_semver(current_version, version_part) 27 | data["version"] = new_version 28 | 29 | with open(file_path, "w") as file: 30 | yaml.safe_dump(data, file) 31 | 32 | print(f"Updated SemVer from {current_version} to {new_version}") 33 | 34 | if __name__ == "__main__": 35 | version_part = sys.argv[1] if len(sys.argv) > 1 else "patch" 36 | config_file_path = Path("charts/home-assistant/Chart.yaml") 37 | update_version_in_yaml(config_file_path, version_part) 38 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/pvc.yaml: -------------------------------------------------------------------------------- 1 | {{- if and (eq .Values.controller.type "Deployment") .Values.persistence.enabled (not .Values.persistence.existingClaim) }} 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: {{ include "home-assistant.fullname" . }}-pvc 6 | labels: 7 | {{- include "home-assistant.labels" . | nindent 4 }} 8 | {{- with .Values.persistence.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | spec: 13 | accessModes: 14 | - {{ .Values.persistence.accessMode }} 15 | {{- if .Values.persistence.existingVolume }} 16 | volumeName: {{ .Values.persistence.existingVolume }} 17 | {{- end }} 18 | {{- if or .Values.persistence.matchLabels (.Values.persistence.matchExpressions) }} 19 | selector: 20 | {{- if .Values.persistence.matchLabels }} 21 | matchLabels: 22 | {{ toYaml .Values.persistence.matchLabels | indent 4 }} 23 | {{- end -}} 24 | {{- if .Values.persistence.matchExpressions }} 25 | matchExpressions: 26 | {{ toYaml .Values.persistence.matchExpressions | indent 4 }} 27 | {{- end -}} 28 | {{- end }} 29 | resources: 30 | requests: 31 | storage: {{ .Values.persistence.size }} 32 | {{- if .Values.persistence.storageClass }} 33 | storageClassName: {{ .Values.persistence.storageClass }} 34 | {{- end }} 35 | {{- end }} 36 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "home-assistant.fullname" . }} 5 | labels: 6 | {{- include "home-assistant.labels" . | nindent 4 }} 7 | {{- with .Values.service.annotations }} 8 | annotations: 9 | {{- toYaml . | nindent 4 }} 10 | {{ end }} 11 | spec: 12 | type: {{ .Values.service.type }} 13 | ports: 14 | - port: {{ .Values.service.port }} 15 | targetPort: http 16 | protocol: TCP 17 | name: http 18 | selector: 19 | {{- include "home-assistant.selectorLabels" . | nindent 4 }} 20 | 21 | {{- if .Values.additionalServices }} 22 | {{- range .Values.additionalServices }} 23 | --- 24 | apiVersion: v1 25 | kind: Service 26 | metadata: 27 | name: {{ include "home-assistant.fullname" $ }}-{{ .name }} 28 | labels: 29 | {{- include "home-assistant.labels" $ | nindent 4 }} 30 | {{- with .labels }} 31 | {{- toYaml . | nindent 4 }} 32 | {{- end }} 33 | {{- with .annotations }} 34 | annotations: 35 | {{- toYaml . | nindent 4 }} 36 | {{ end }} 37 | spec: 38 | type: {{ .type }} 39 | {{- if .loadBalancerClass}} 40 | loadBalancerClass: {{.loadBalancerClass}} 41 | {{- end}} 42 | ports: 43 | - port: {{ .port }} 44 | targetPort: {{ .targetPort }} 45 | protocol: {{ .protocol }} 46 | name: {{ .name }} 47 | {{- if .nodePort }} 48 | nodePort: {{ .nodePort }} 49 | {{- end }} 50 | selector: 51 | {{- include "home-assistant.selectorLabels" $ | nindent 4 }} 52 | {{- end }} 53 | {{- end}} 54 | -------------------------------------------------------------------------------- /.github/workflows/lint-test.yaml: -------------------------------------------------------------------------------- 1 | name: Lint and Test Charts 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | lint-test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v5 11 | with: 12 | fetch-depth: 0 13 | 14 | - name: Set up Helm 15 | uses: azure/setup-helm@v4 16 | with: 17 | version: v3.11.2 18 | 19 | - uses: actions/setup-python@v6 20 | with: 21 | python-version: '3.13' 22 | check-latest: true 23 | 24 | - name: Set up chart-testing 25 | uses: helm/chart-testing-action@v2.7.0 26 | 27 | - name: Run chart-testing (list-changed) 28 | id: list-changed 29 | run: | 30 | changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }}) 31 | if [[ -n "$changed" ]]; then 32 | echo "changed=true" >> "$GITHUB_OUTPUT" 33 | fi 34 | 35 | - name: Run chart-testing (lint) 36 | if: steps.list-changed.outputs.changed == 'true' 37 | run: ct lint --target-branch ${{ github.event.repository.default_branch }} --check-version-increment=false 38 | 39 | - name: Create kind cluster 40 | if: steps.list-changed.outputs.changed == 'true' 41 | uses: helm/kind-action@v1.12.0 42 | 43 | - name: Run chart-testing (install) 44 | if: steps.list-changed.outputs.changed == 'true' 45 | run: ct install --target-branch ${{ github.event.repository.default_branch }} 46 | 47 | # Add explicit test for invalid configuration 48 | - name: Test invalid ingress configuration 49 | if: steps.list-changed.outputs.changed == 'true' 50 | run: | 51 | if helm template . --set ingress.enabled=true,ingress.external=true > /dev/null 2>&1; then 52 | echo "Error: Template validation should have failed when both ingress types are enabled" 53 | exit 1 54 | else 55 | echo "Successfully caught invalid ingress configuration" 56 | fi 57 | 58 | 59 | -------------------------------------------------------------------------------- /.github/workflows/build-helm-chart-release.yaml: -------------------------------------------------------------------------------- 1 | name: Release Charts 2 | 3 | on: 4 | # push: 5 | # branches: 6 | # - main 7 | # paths: 8 | # - 'charts/**' 9 | # # - 'README.md' 10 | workflow_dispatch: 11 | inputs: 12 | version_part: 13 | description: 'Which part of the version to increment? (major, minor, patch)' 14 | required: true 15 | default: 'patch' 16 | 17 | jobs: 18 | release: 19 | # depending on default permission settings for your org (contents being read-only or read-write for workloads), you will have to add permissions 20 | # see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token 21 | permissions: 22 | contents: write 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v5 27 | with: 28 | fetch-depth: 0 29 | 30 | - name: Configure Git 31 | run: | 32 | git config user.name "$GITHUB_ACTOR" 33 | git config user.email "$GITHUB_ACTOR@users.noreply.github.com" 34 | 35 | - name: Set up Python 36 | uses: actions/setup-python@v6 37 | with: 38 | python-version: 3.x 39 | 40 | - name: Install Python dependencies 41 | run: pip install pyyaml yq 42 | 43 | - name: Update SemVer in config.yaml 44 | run: python .github/workflows/update_version.py ${{ github.event.inputs.version_part }} 45 | 46 | - name: Get latest release version 47 | id: get_latest_release 48 | run: | 49 | VERSION=$(yq '.version' charts/home-assistant/Chart.yaml) 50 | echo "VERSION=$VERSION" >> $GITHUB_ENV 51 | echo "Version: $VERSION" 52 | 53 | - name: Copy README.md from chart to root 54 | run: cp charts/home-assistant/README.md README.md 55 | 56 | - name: Commit latest release version 57 | run: | 58 | git add charts/home-assistant/Chart.yaml 59 | git add README.md 60 | git commit -m "Released version ${{ env.VERSION }} of the helm chart" 61 | git push 62 | 63 | - name: Install Helm 64 | env: 65 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 66 | uses: azure/setup-helm@v4 67 | 68 | - name: Run chart-releaser 69 | uses: helm/chart-releaser-action@v1.7.0 70 | env: 71 | CR_TOKEN: ${{ secrets.REPO_SCOPED_TOKEN }} 72 | 73 | 74 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- include "home-assistant.validateIngress" . }} 3 | {{- $fullName := include "home-assistant.fullname" . -}} 4 | {{- $svcPort := .Values.service.port -}} 5 | {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} 6 | {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} 7 | {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} 8 | {{- end }} 9 | {{- end }} 10 | {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} 11 | apiVersion: networking.k8s.io/v1 12 | {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 13 | apiVersion: networking.k8s.io/v1beta1 14 | {{- else -}} 15 | apiVersion: extensions/v1beta1 16 | {{- end }} 17 | kind: Ingress 18 | metadata: 19 | name: {{ $fullName }} 20 | labels: 21 | {{- include "home-assistant.labels" . | nindent 4 }} 22 | {{- with .Values.ingress.labels }} 23 | {{- toYaml . | nindent 4 }} 24 | {{- end }} 25 | {{- with .Values.ingress.annotations }} 26 | annotations: 27 | {{- toYaml . | nindent 4 }} 28 | {{- end }} 29 | spec: 30 | {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} 31 | ingressClassName: {{ .Values.ingress.className }} 32 | {{- end }} 33 | {{- if .Values.ingress.tls }} 34 | tls: 35 | {{- range .Values.ingress.tls }} 36 | - hosts: 37 | {{- range .hosts }} 38 | - {{ . | quote }} 39 | {{- end }} 40 | secretName: {{ .secretName }} 41 | {{- end }} 42 | {{- end }} 43 | rules: 44 | {{- range .Values.ingress.hosts }} 45 | - host: {{ .host | quote }} 46 | http: 47 | paths: 48 | {{- range .paths }} 49 | - path: {{ .path }} 50 | {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} 51 | pathType: {{ .pathType }} 52 | {{- end }} 53 | backend: 54 | {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} 55 | service: 56 | name: {{ $fullName }} 57 | port: 58 | number: {{ $svcPort }} 59 | {{- else }} 60 | serviceName: {{ $fullName }} 61 | servicePort: {{ $svcPort }} 62 | {{- end }} 63 | {{- end }} 64 | {{- end }} 65 | {{- end }} 66 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/ingress-codeserver.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.addons.codeserver .Values.addons.codeserver.ingress.enabled -}} 2 | {{- $fullName := print (include "home-assistant.fullname" .) "-codeserver" -}} 3 | {{- $svcPort := 12321 -}} 4 | {{- if and .Values.addons.codeserver.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} 5 | {{- if not (hasKey .Values.addons.codeserver.ingress.annotations "kubernetes.io/ingress.class") }} 6 | {{- $_ := set .Values.addons.codeserver.ingress.annotations "kubernetes.io/ingress.class" .Values.addons.codeserver.ingress.className}} 7 | {{- end }} 8 | {{- end }} 9 | {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} 10 | apiVersion: networking.k8s.io/v1 11 | {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 12 | apiVersion: networking.k8s.io/v1beta1 13 | {{- else -}} 14 | apiVersion: extensions/v1beta1 15 | {{- end }} 16 | kind: Ingress 17 | metadata: 18 | name: {{ $fullName }} 19 | labels: 20 | {{- include "home-assistant.labels" . | nindent 4 }} 21 | {{- with .Values.addons.codeserver.ingress.annotations }} 22 | annotations: 23 | {{- toYaml . | nindent 4 }} 24 | {{- end }} 25 | spec: 26 | {{- if and .Values.addons.codeserver.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} 27 | ingressClassName: {{ .Values.addons.codeserver.ingress.className }} 28 | {{- end }} 29 | {{- if .Values.addons.codeserver.ingress.tls }} 30 | tls: 31 | {{- range .Values.addons.codeserver.ingress.tls }} 32 | - hosts: 33 | {{- range .hosts }} 34 | - {{ . | quote }} 35 | {{- end }} 36 | secretName: {{ .secretName }} 37 | {{- end }} 38 | {{- end }} 39 | rules: 40 | {{- range .Values.addons.codeserver.ingress.hosts }} 41 | - host: {{ .host | quote }} 42 | http: 43 | paths: 44 | {{- range .paths }} 45 | - path: {{ .path }} 46 | {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} 47 | pathType: {{ .pathType }} 48 | {{- end }} 49 | backend: 50 | {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} 51 | service: 52 | name: {{ $fullName }} 53 | port: 54 | number: {{ $svcPort }} 55 | {{- else }} 56 | serviceName: {{ $fullName }} 57 | servicePort: {{ $svcPort }} 58 | {{- end }} 59 | {{- end }} 60 | {{- end }} 61 | {{- end }} 62 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "home-assistant.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "home-assistant.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "home-assistant.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "home-assistant.labels" -}} 37 | helm.sh/chart: {{ include "home-assistant.chart" . }} 38 | {{ include "home-assistant.selectorLabels" . }} 39 | {{- if .Chart.AppVersion }} 40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 41 | {{- end }} 42 | app.kubernetes.io/managed-by: {{ .Release.Service }} 43 | {{- end }} 44 | 45 | {{/* 46 | Selector labels 47 | */}} 48 | {{- define "home-assistant.selectorLabels" -}} 49 | app.kubernetes.io/name: {{ include "home-assistant.name" . }} 50 | app.kubernetes.io/instance: {{ .Release.Name }} 51 | {{- end }} 52 | 53 | {{/* 54 | Create the name of the service account to use 55 | */}} 56 | {{- define "home-assistant.serviceAccountName" -}} 57 | {{- if .Values.serviceAccount.create }} 58 | {{- default (include "home-assistant.fullname" .) .Values.serviceAccount.name }} 59 | {{- else }} 60 | {{- default "default" .Values.serviceAccount.name }} 61 | {{- end }} 62 | {{- end }} 63 | 64 | {{/* 65 | Validate ingress configuration 66 | */}} 67 | {{- define "home-assistant.validateIngress" -}} 68 | {{- if and .Values.ingress.enabled .Values.ingress.external -}} 69 | {{- fail "ingress.enabled and ingress.external cannot both be true" -}} 70 | {{- end -}} 71 | {{- end -}} 72 | 73 | {{/* 74 | Validate controller type 75 | */}} 76 | {{- define "home-assistant.validateController" -}} 77 | {{- if not (or (eq .Values.controller.type "StatefulSet") (eq .Values.controller.type "Deployment")) -}} 78 | {{- fail "controller.type must be either 'StatefulSet' or 'Deployment'" -}} 79 | {{- end -}} 80 | {{- end -}} 81 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range $host := .Values.ingress.hosts }} 4 | {{- range .paths }} 5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} 6 | {{- end }} 7 | {{- end }} 8 | {{- else if contains "NodePort" .Values.service.type }} 9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "home-assistant.fullname" . }}) 10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 11 | echo http://$NODE_IP:$NODE_PORT 12 | {{- else if contains "LoadBalancer" .Values.service.type }} 13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "home-assistant.fullname" . }}' 15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "home-assistant.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") 16 | echo http://$SERVICE_IP:{{ .Values.service.port }} 17 | {{- else if contains "ClusterIP" .Values.service.type }} 18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "home-assistant.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 19 | export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") 20 | echo "Visit http://127.0.0.1:8123 to use your application" 21 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8123:$CONTAINER_PORT 22 | {{- end }} 23 | 24 | {{- if .Values.configuration.enabled }} 25 | {{- if or (has "10.0.0.0/8" .Values.configuration.trusted_proxies) (has "172.16.0.0/12" .Values.configuration.trusted_proxies) (has "192.168.0.0/16" .Values.configuration.trusted_proxies) (has "127.0.0.0/8" .Values.configuration.trusted_proxies) }} 26 | WARNING: You have enabled configuration setup for Home Assistant and are using widely trusted proxy IP ranges. Please ensure that this is intended and secure for your environment. Misconfiguration can lead to security vulnerabilities. 27 | {{- end }} 28 | {{- end }} 29 | 30 | {{- if and .Values.ingress.enabled (not .Values.configuration.enabled) }} 31 | WARNING: Ingress is enabled for Home Assistant, but the configuration setup is disabled. If you are using a reverse proxy, it is recommended to configure trusted proxies to ensure Home Assistant functions correctly and securely. For more information on configuring trusted proxies, visit: https://www.home-assistant.io/integrations/http/#reverse-proxies 32 | {{- end }} 33 | 34 | {{- if and .Values.configuration.enabled (or .Values.securityContext .Values.podSecurityContext) }} 35 | WARNING: You have enabled the configuration setup for Home Assistant and defined a securityContext or podSecurityContext. Please ensure to update the configuration.initContainer.securityContext to have the same user setup to avoid running the init container as the root user. This is crucial for maintaining the security of your deployment. 36 | {{- end }} 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /.github/workflows/check_ha_release.yml: -------------------------------------------------------------------------------- 1 | name: Auto-update latest HA version 2 | on: 3 | schedule: 4 | - cron: '0 10 * * *' 5 | workflow_dispatch: 6 | 7 | jobs: 8 | get-version: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v5 12 | with: 13 | token: ${{ secrets.REPO_SCOPED_TOKEN }} 14 | - name: Prepare git 15 | run: | 16 | git config user.name "GitHub Action" 17 | git config user.email "action@github.com" 18 | - name: Fetch ha release version 19 | id: ha-version 20 | run: | 21 | HA_VERSION=$(curl -sL https://api.github.com/repos/home-assistant/home-assistant/releases/latest | jq -r ".tag_name") 22 | if [[ -z "$HA_VERSION" || "$HA_VERSION" == "null" ]]; then 23 | echo "Returned HA version is null or empty, exiting." 24 | exit 1 25 | fi 26 | if ! [[ "$HA_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then 27 | echo "HA_VERSION does not contain a version in semver format." 28 | exit 1 29 | fi 30 | echo "Setting appVersion to $HA_VERSION in Chart.yaml" 31 | sed -i "s/^appVersion:.*/appVersion: $HA_VERSION/" charts/home-assistant/Chart.yaml 32 | git add charts/home-assistant/Chart.yaml 33 | echo "UPDATED=true" >> $GITHUB_ENV 34 | git commit -m "Updated Home Assistant version to $HA_VERSION" || EXIT_CODE=$? 35 | if [ "$EXIT_CODE" -ne 0 ]; then 36 | echo "Probably no changes to commit, exiting." 37 | echo "changed=false" >> "$GITHUB_OUTPUT" 38 | else 39 | echo "changed=true" >> "$GITHUB_OUTPUT" 40 | fi 41 | # - name: Fetch code server release version 42 | # id: code-server-version 43 | # run: | 44 | # CODE_SERVER_VERSION_WITH_PREFIX=$(curl -sL https://api.github.com/repos/coder/code-server/releases/latest | jq -r ".tag_name") 45 | # if [ -z "$CODE_SERVER_VERSION_WITH_PREFIX" ]; then 46 | # echo "Returned Code Server version is null or empty, exiting." 47 | # exit 1 48 | # fi 49 | # CODE_SERVER_VERSION=$(echo $CODE_SERVER_VERSION_WITH_PREFIX | sed 's/^v//') 50 | # echo "Setting code server version to $CODE_SERVER_VERSION" 51 | # sed -i '/^addons:/,/^\w/{ s/\(tag: \).*$/\1"'$CODE_SERVER_VERSION'"/ }' charts/home-assistant/values.yaml 52 | # git add charts/home-assistant/values.yaml 53 | # git commit -m "Updated code-server version to $CODE_SERVER_VERSION" || EXIT_CODE=$? 54 | # if [ "$EXIT_CODE" -ne 0 ]; then 55 | # echo "Probably no changes to commit, exiting." 56 | # echo "changed=false" >> "$GITHUB_OUTPUT" 57 | # else 58 | # echo "changed=true" >> "$GITHUB_OUTPUT" 59 | # fi 60 | - name: Commit latest release version 61 | run: | 62 | git config user.name "GitHub Action" 63 | git config user.email "action@github.com" 64 | git push 65 | - name: Run second step if the previous was successful 66 | # if: steps.ha-version.outputs.changed == 'true' || steps.code-server-version.outputs.changed == 'true' 67 | if: steps.ha-version.outputs.changed == 'true' 68 | uses: benc-uk/workflow-dispatch@v1 69 | with: 70 | workflow: Release Charts 71 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | {{- if eq .Values.controller.type "Deployment" }} 2 | {{- include "home-assistant.validateController" . | trim }} 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: {{ include "home-assistant.fullname" . }} 7 | labels: 8 | {{- include "home-assistant.labels" . | nindent 4 }} 9 | {{- if .Values.deploymentAnnotations }} 10 | annotations: 11 | {{- toYaml .Values.deploymentAnnotations | nindent 4 }} 12 | {{- end }} 13 | spec: 14 | replicas: {{ .Values.replicaCount }} 15 | selector: 16 | matchLabels: 17 | {{- include "home-assistant.selectorLabels" . | nindent 6 }} 18 | strategy: 19 | type: {{ .Values.deploymentStrategy }} 20 | template: 21 | metadata: 22 | labels: 23 | {{- include "home-assistant.selectorLabels" . | nindent 8 }} 24 | annotations: 25 | {{- if .Values.configuration.enabled }} 26 | checksum/init-script: {{ include (print $.Template.BasePath "/configmap-init-script.yaml") . | sha256sum }} 27 | checksum/hass-configuration: {{ include (print $.Template.BasePath "/configmap-hass-config.yaml") . | sha256sum }} 28 | {{- end }} 29 | {{- with .Values.podAnnotations }} 30 | {{- toYaml . | nindent 8 }} 31 | {{- end }} 32 | spec: 33 | {{- if .Values.hostNetwork }} 34 | hostNetwork: true 35 | {{- end }} 36 | {{- if .Values.dnsPolicy }} 37 | dnsPolicy: {{ .Values.dnsPolicy }} 38 | {{- end }} 39 | {{- with .Values.imagePullSecrets }} 40 | imagePullSecrets: 41 | {{- toYaml . | nindent 8 }} 42 | {{- end }} 43 | serviceAccountName: {{ include "home-assistant.serviceAccountName" . }} 44 | securityContext: 45 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 46 | {{- if .Values.dnsConfig }} 47 | dnsConfig: 48 | {{- toYaml .Values.dnsConfig | nindent 8 }} 49 | {{- end }} 50 | containers: 51 | - name: {{ .Chart.Name }} 52 | securityContext: 53 | {{- toYaml .Values.securityContext | nindent 12 }} 54 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 55 | imagePullPolicy: {{ .Values.image.pullPolicy }} 56 | {{- with .Values.envFrom }} 57 | envFrom: 58 | {{- toYaml . | nindent 12 }} 59 | {{- end }} 60 | {{- with .Values.env }} 61 | env: 62 | {{- toYaml . | nindent 12 }} 63 | {{- end }} 64 | ports: 65 | - name: http 66 | containerPort: 8123 67 | protocol: TCP 68 | {{- if .Values.hostPort.enabled }} 69 | hostPort: {{ .Values.hostPort.port }} 70 | {{- end }} 71 | {{- if .Values.additionalPorts }} 72 | {{- .Values.additionalPorts | toYaml | nindent 12 }} 73 | {{- end }} 74 | {{- with $.Values.livenessProbe }} 75 | livenessProbe: 76 | {{- toYaml . | nindent 12 }} 77 | {{- end }} 78 | {{- with $.Values.readinessProbe }} 79 | readinessProbe: 80 | {{- toYaml . | nindent 12 }} 81 | {{- end }} 82 | {{- with $.Values.startupProbe }} 83 | startupProbe: 84 | {{- toYaml . | nindent 12 }} 85 | {{- end }} 86 | {{- with .Values.resources }} 87 | resources: 88 | {{- toYaml . | nindent 12 }} 89 | {{- end }} 90 | volumeMounts: 91 | - mountPath: /config 92 | name: {{ include "home-assistant.fullname" . }}-pvc 93 | {{- if .Values.additionalMounts }} 94 | {{- .Values.additionalMounts | toYaml | nindent 10 }} 95 | {{- end }} 96 | {{- if .Values.addons.codeserver.enabled }} 97 | - name: codeserver 98 | securityContext: 99 | {{- toYaml .Values.securityContext | nindent 12 }} 100 | args: 101 | - --auth 102 | {{- if .Values.addons.codeserver.auth.enabled }} 103 | - password 104 | {{- else }} 105 | - none 106 | {{- end }} 107 | - --user-data-dir 108 | - "/config/.vscode" 109 | - --extensions-dir 110 | - "/config/.vscode" 111 | - --port 112 | - "12321" 113 | - "/config" 114 | {{- if and .Values.addons.codeserver.auth.enabled (or .Values.addons.codeserver.auth.existingSecret .Values.addons.codeserver.auth.password) }} 115 | env: 116 | {{- if .Values.addons.codeserver.auth.existingSecret }} 117 | - name: PASSWORD 118 | valueFrom: 119 | secretKeyRef: 120 | name: {{ .Values.addons.codeserver.auth.existingSecret }} 121 | key: password 122 | {{- else if .Values.addons.codeserver.auth.password }} 123 | - name: PASSWORD 124 | value: {{ .Values.addons.codeserver.auth.password | quote }} 125 | {{- end }} 126 | {{- end }} 127 | image: "{{ .Values.addons.codeserver.image.repository }}:{{ .Values.addons.codeserver.image.tag }}" 128 | imagePullPolicy: "{{ .Values.addons.codeserver.image.pullPolicy }}" 129 | ports: 130 | - containerPort: 12321 131 | name: codeserver 132 | protocol: TCP 133 | {{- with .Values.addons.codeserver.resources }} 134 | resources: 135 | {{- toYaml . | nindent 12 }} 136 | {{- end }} 137 | volumeMounts: 138 | - mountPath: /config 139 | name: {{ include "home-assistant.fullname" . }}-pvc 140 | {{- if .Values.addons.codeserver.additionalMounts }} 141 | {{- .Values.addons.codeserver.additionalMounts | toYaml | nindent 10 }} 142 | {{- end }} 143 | {{- end }} 144 | {{- if or (.Values.configuration.enabled) .Values.initContainers }} 145 | initContainers: 146 | {{- if .Values.initContainers }} 147 | {{- toYaml .Values.initContainers | nindent 8 }} 148 | {{- end }} 149 | {{- if .Values.configuration.enabled }} 150 | - name: {{ .Values.configuration.initContainer.name }} 151 | image: {{ .Values.configuration.initContainer.image }} 152 | {{- if .Values.configuration.initContainer.securityContext }} 153 | securityContext: 154 | {{- toYaml .Values.configuration.initContainer.securityContext | nindent 12 }} 155 | {{- end }} 156 | {{- if .Values.configuration.initContainer.command }} 157 | command: {{ toYaml .Values.configuration.initContainer.command | nindent 12 }} 158 | {{- end }} 159 | {{- if .Values.configuration.initContainer.args }} 160 | args: {{ toYaml .Values.configuration.initContainer.args | nindent 12 }} 161 | {{- end }} 162 | {{- if .Values.configuration.initContainer.env }} 163 | env: 164 | {{- toYaml .Values.configuration.initContainer.env | nindent 12 }} 165 | {{- end }} 166 | volumeMounts: 167 | {{- range .Values.configuration.initContainer.volumeMounts }} 168 | - name: {{ .name }} 169 | mountPath: {{ .mountPath }} 170 | {{- if .subPath }} 171 | subPath: {{ .subPath }} 172 | {{ end }} 173 | {{- end }} 174 | - mountPath: /config 175 | name: {{ include "home-assistant.fullname" . }}-pvc 176 | {{- end }} 177 | {{- end }} 178 | {{- with .Values.nodeSelector }} 179 | nodeSelector: 180 | {{- toYaml . | nindent 8 }} 181 | {{- end }} 182 | {{- with .Values.affinity }} 183 | affinity: 184 | {{- toYaml . | nindent 8 }} 185 | {{- end }} 186 | {{- with .Values.tolerations }} 187 | tolerations: 188 | {{- toYaml . | nindent 8 }} 189 | {{- end }} 190 | {{- if .Values.priorityClassName }} 191 | priorityClassName: {{ .Values.priorityClassName }} 192 | {{- end }} 193 | volumes: 194 | {{- if .Values.configuration.enabled }} 195 | - name: init-volume 196 | configMap: 197 | name: init-script 198 | - name: config-volume 199 | configMap: 200 | name: hass-configuration 201 | {{- end }} 202 | {{- if not .Values.persistence.enabled }} 203 | - name: {{ include "home-assistant.fullname" . }}-pvc 204 | emptyDir: {} 205 | {{- else if .Values.persistence.existingClaim }} 206 | - name: {{ include "home-assistant.fullname" . }}-pvc 207 | persistentVolumeClaim: 208 | claimName: {{ .Values.persistence.existingClaim }} 209 | {{- else }} 210 | - name: {{ include "home-assistant.fullname" . }}-pvc 211 | persistentVolumeClaim: 212 | claimName: {{ include "home-assistant.fullname" . }}-pvc 213 | {{- end }} 214 | {{- if .Values.additionalVolumes }} 215 | {{- .Values.additionalVolumes | toYaml | nindent 6 }} 216 | {{- end }} 217 | {{- end }} 218 | -------------------------------------------------------------------------------- /charts/home-assistant/templates/statefulset.yaml: -------------------------------------------------------------------------------- 1 | {{- include "home-assistant.validateController" . }} 2 | {{- if eq .Values.controller.type "StatefulSet" }} 3 | apiVersion: apps/v1 4 | kind: StatefulSet 5 | metadata: 6 | name: {{ include "home-assistant.fullname" . }} 7 | labels: 8 | {{- include "home-assistant.labels" . | nindent 4 }} 9 | {{- if .Values.statefulSetAnnotations }} 10 | annotations: 11 | {{- toYaml .Values.statefulSetAnnotations | nindent 4 }} 12 | {{- end }} 13 | spec: 14 | serviceName: {{ include "home-assistant.fullname" . }} 15 | replicas: {{ .Values.replicaCount }} 16 | selector: 17 | matchLabels: 18 | {{- include "home-assistant.selectorLabels" . | nindent 6 }} 19 | template: 20 | metadata: 21 | labels: 22 | {{- include "home-assistant.selectorLabels" . | nindent 8 }} 23 | annotations: 24 | {{- if .Values.configuration.enabled }} 25 | checksum/init-script: {{ include (print $.Template.BasePath "/configmap-init-script.yaml") . | sha256sum }} 26 | checksum/hass-configuration: {{ include (print $.Template.BasePath "/configmap-hass-config.yaml") . | sha256sum }} 27 | {{- end }} 28 | {{- with .Values.podAnnotations }} 29 | {{- toYaml . | nindent 8 }} 30 | {{- end }} 31 | spec: 32 | {{- if .Values.hostNetwork }} 33 | hostNetwork: true 34 | {{- end }} 35 | {{- if .Values.dnsPolicy }} 36 | dnsPolicy: {{ .Values.dnsPolicy }} 37 | {{- end }} 38 | {{- with .Values.imagePullSecrets }} 39 | imagePullSecrets: 40 | {{- toYaml . | nindent 8 }} 41 | {{- end }} 42 | serviceAccountName: {{ include "home-assistant.serviceAccountName" . }} 43 | securityContext: 44 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 45 | {{- if .Values.dnsConfig }} 46 | dnsConfig: 47 | {{- toYaml .Values.dnsConfig | nindent 8 }} 48 | {{- end }} 49 | containers: 50 | - name: {{ .Chart.Name }} 51 | securityContext: 52 | {{- toYaml .Values.securityContext | nindent 12 }} 53 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 54 | imagePullPolicy: {{ .Values.image.pullPolicy }} 55 | {{- with .Values.envFrom }} 56 | envFrom: 57 | {{- toYaml . | nindent 12 }} 58 | {{- end }} 59 | {{- with .Values.env }} 60 | env: 61 | {{- toYaml . | nindent 12 }} 62 | {{- end }} 63 | ports: 64 | - name: http 65 | containerPort: 8123 66 | protocol: TCP 67 | {{- if .Values.hostPort.enabled }} 68 | hostPort: {{ .Values.hostPort.port }} 69 | {{- end }} 70 | {{- if .Values.additionalPorts }} 71 | {{- .Values.additionalPorts | toYaml | nindent 12 }} 72 | {{- end }} 73 | {{- with $.Values.livenessProbe }} 74 | livenessProbe: 75 | {{- toYaml . | nindent 12 }} 76 | {{- end }} 77 | {{- with $.Values.readinessProbe }} 78 | readinessProbe: 79 | {{- toYaml . | nindent 12 }} 80 | {{- end }} 81 | {{- with $.Values.startupProbe }} 82 | startupProbe: 83 | {{- toYaml . | nindent 12 }} 84 | {{- end }} 85 | {{- with .Values.resources }} 86 | resources: 87 | {{- toYaml . | nindent 12 }} 88 | {{- end }} 89 | volumeMounts: 90 | - mountPath: /config 91 | name: {{ include "home-assistant.fullname" . }} 92 | {{- if .Values.additionalMounts }} 93 | {{- .Values.additionalMounts | toYaml | nindent 10 }} 94 | {{- end }} 95 | {{- if .Values.addons.codeserver.enabled }} 96 | - name: codeserver 97 | securityContext: 98 | {{- toYaml .Values.securityContext | nindent 12 }} 99 | args: 100 | - --auth 101 | {{- if .Values.addons.codeserver.auth.enabled }} 102 | - password 103 | {{- else }} 104 | - none 105 | {{- end }} 106 | - --user-data-dir 107 | - "/config/.vscode" 108 | - --extensions-dir 109 | - "/config/.vscode" 110 | - --port 111 | - "12321" 112 | - "/config" 113 | {{- if and .Values.addons.codeserver.auth.enabled (or .Values.addons.codeserver.auth.existingSecret .Values.addons.codeserver.auth.password) }} 114 | env: 115 | {{- if .Values.addons.codeserver.auth.existingSecret }} 116 | - name: PASSWORD 117 | valueFrom: 118 | secretKeyRef: 119 | name: {{ .Values.addons.codeserver.auth.existingSecret }} 120 | key: password 121 | {{- else if .Values.addons.codeserver.auth.password }} 122 | - name: PASSWORD 123 | value: {{ .Values.addons.codeserver.auth.password | quote }} 124 | {{- end }} 125 | {{- end }} 126 | image: "{{ .Values.addons.codeserver.image.repository }}:{{ .Values.addons.codeserver.image.tag }}" 127 | imagePullPolicy: "{{ .Values.addons.codeserver.image.pullPolicy }}" 128 | ports: 129 | - containerPort: 12321 130 | name: codeserver 131 | protocol: TCP 132 | {{- with .Values.addons.codeserver.resources }} 133 | resources: 134 | {{- toYaml . | nindent 12 }} 135 | {{- end }} 136 | volumeMounts: 137 | - mountPath: /config 138 | name: {{ include "home-assistant.fullname" . }} 139 | {{- if .Values.addons.codeserver.additionalMounts }} 140 | {{- .Values.addons.codeserver.additionalMounts | toYaml | nindent 10 }} 141 | {{- end }} 142 | {{- end }} 143 | {{- if or (.Values.configuration.enabled) .Values.initContainers }} 144 | initContainers: 145 | {{- if .Values.initContainers }} 146 | {{- toYaml .Values.initContainers | nindent 8 }} 147 | {{- end }} 148 | {{- if .Values.configuration.enabled }} 149 | - name: {{ .Values.configuration.initContainer.name }} 150 | image: {{ .Values.configuration.initContainer.image }} 151 | {{- if .Values.configuration.initContainer.securityContext }} 152 | securityContext: 153 | {{- toYaml .Values.configuration.initContainer.securityContext | nindent 12 }} 154 | {{- end }} 155 | {{- if .Values.configuration.initContainer.command }} 156 | command: {{ toYaml .Values.configuration.initContainer.command | nindent 12 }} 157 | {{- end }} 158 | {{- if .Values.configuration.initContainer.args }} 159 | args: {{ toYaml .Values.configuration.initContainer.args | nindent 12 }} 160 | {{- end }} 161 | {{- if .Values.configuration.initContainer.env }} 162 | env: 163 | {{- toYaml .env | nindent 12 }} 164 | {{- end }} 165 | volumeMounts: 166 | {{- range .Values.configuration.initContainer.volumeMounts }} 167 | - name: {{ .name }} 168 | mountPath: {{ .mountPath }} 169 | {{- if .subPath }} 170 | subPath: {{ .subPath }} 171 | {{ end }} 172 | {{- end }} 173 | - mountPath: /config 174 | name: {{ include "home-assistant.fullname" $ }} 175 | {{- end }} 176 | {{- end }} 177 | {{- with .Values.nodeSelector }} 178 | nodeSelector: 179 | {{- toYaml . | nindent 8 }} 180 | {{- end }} 181 | {{- with .Values.affinity }} 182 | affinity: 183 | {{- toYaml . | nindent 8 }} 184 | {{- end }} 185 | {{- with .Values.tolerations }} 186 | tolerations: 187 | {{- toYaml . | nindent 8 }} 188 | {{- end }} 189 | {{- if .Values.priorityClassName }} 190 | priorityClassName: {{ .Values.priorityClassName }} 191 | {{- end }} 192 | volumes: 193 | {{- if .Values.configuration.enabled }} 194 | - name: init-volume 195 | configMap: 196 | name: init-script 197 | - name: config-volume 198 | configMap: 199 | name: hass-configuration 200 | {{- end }} 201 | {{- if not .Values.persistence.enabled }} 202 | - name: {{ include "home-assistant.fullname" . }} 203 | emptyDir: {} 204 | {{- end }} 205 | {{- if .Values.additionalVolumes }} 206 | {{- .Values.additionalVolumes | toYaml | nindent 6 }} 207 | {{- end }} 208 | {{- if .Values.persistence.enabled }} 209 | volumeClaimTemplates: 210 | - metadata: 211 | name: {{ include "home-assistant.fullname" . }} 212 | {{- with .Values.persistence.annotations }} 213 | annotations: 214 | {{- toYaml . | nindent 8 }} 215 | {{- end }} 216 | spec: 217 | accessModes: 218 | - {{ .Values.persistence.accessMode }} 219 | {{- if .Values.persistence.existingVolume }} 220 | volumeName: {{ .Values.persistence.existingVolume }} 221 | {{- end }} 222 | {{- if or .Values.persistence.matchLabels (.Values.persistence.matchExpressions) }} 223 | selector: 224 | {{- if .Values.persistence.matchLabels }} 225 | matchLabels: 226 | {{ toYaml .Values.persistence.matchLabels | indent 8 }} 227 | {{- end -}} 228 | {{- if .Values.persistence.matchExpressions }} 229 | matchExpressions: 230 | {{ toYaml .Values.persistence.matchExpressions | indent 8 }} 231 | {{- end -}} 232 | {{- end }} 233 | resources: 234 | requests: 235 | storage: {{ .Values.persistence.size }} 236 | {{- if .Values.persistence.storageClass }} 237 | storageClassName: {{ .Values.persistence.storageClass }} 238 | {{- end }} 239 | 240 | {{- end }} 241 | {{- end }} 242 | 243 | -------------------------------------------------------------------------------- /charts/home-assistant/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for home-assistant. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | # Number of replicas for the deployment 6 | replicaCount: 1 7 | 8 | # Image settings 9 | image: 10 | # Repository for the Home Assistant image 11 | repository: ghcr.io/home-assistant/home-assistant 12 | # Image pull policy 13 | pullPolicy: IfNotPresent 14 | # Overrides the image tag whose default is the chart appVersion. 15 | tag: "" 16 | 17 | # List of imagePullSecrets for private image repositories 18 | imagePullSecrets: [] 19 | # Override the default name of the Helm chart 20 | nameOverride: "" 21 | # Override the default full name of the Helm chart 22 | fullnameOverride: "" 23 | 24 | # Service account settings 25 | serviceAccount: 26 | # Specifies whether a service account should be created 27 | create: true 28 | # Annotations to add to the service account 29 | annotations: {} 30 | # The name of the service account to use. 31 | # If not set and create is true, a name is generated using the fullname template 32 | name: "" 33 | 34 | # Annotations to add to the pod 35 | podAnnotations: {} 36 | 37 | # Pod security context settings 38 | podSecurityContext: 39 | {} 40 | # runAsUser: 568 41 | # runAsGroup: 568 42 | # fsGroup: 568 43 | # fsGroupChangePolicy: "OnRootMismatch" 44 | 45 | # Environment variables 46 | env: [] 47 | # - name: TZ 48 | # value: Europe/Prague 49 | # - name: SOME_VAR_FROM_CONFIG_MAP 50 | # valueFrom: 51 | # configMapRef: 52 | # name: configmap-name 53 | # key: config-key 54 | # - name: SOME_SECRET 55 | # valueFrom: 56 | # secretKeyRef: 57 | # name: secret-name 58 | # key: secret-key 59 | 60 | # Use environment variables from ConfigMaps or Secrets 61 | envFrom: [] 62 | # - configMapRef: 63 | # name: config-map-name 64 | # - secretRef: 65 | # name: secret-name 66 | 67 | hostPort: 68 | # Enable 'hostPort' or not 69 | enabled: false 70 | port: 8123 71 | 72 | # Specifies if the containers should be started in hostNetwork mode. 73 | # 74 | # Required for use auto-discovery feature of Home Assistant 75 | hostNetwork: false 76 | 77 | # Set the dnsPolicy (you'll want ClusterFirstWithHostNet if running on hostNetwork to reac 78 | # other k8s services via DNS 79 | # dnsPolicy: ClusterFirst 80 | 81 | # Container security context settings 82 | securityContext: 83 | {} 84 | # capabilities: 85 | # drop: 86 | # - ALL 87 | # readOnlyRootFilesystem: true 88 | # runAsNonRoot: true 89 | # runAsUser: 1000 90 | 91 | # Pod's DNS Configuration 92 | # https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config 93 | # This value is useful if you need to reduce the DNS load: set "ndots" to 0 and only use FQDNs. 94 | dnsConfig: {} 95 | # nameservers: 96 | # - 1.2.3.4 97 | # searches: 98 | # - ns1.svc.cluster-domain.example 99 | # - my.dns.search.suffix 100 | # options: 101 | # - name: ndots 102 | # value: "2" 103 | 104 | # Service settings 105 | service: 106 | # Service type (ClusterIP, NodePort, LoadBalancer, or ExternalName) 107 | type: ClusterIP 108 | # Service port 109 | port: 8080 110 | # Annotations to add to the service 111 | annotations: {} 112 | 113 | # Ingress settings 114 | ingress: 115 | # Enable ingress for home assistant 116 | enabled: false 117 | # Enable external ingress (cannot be true when ingress.enabled is true) 118 | external: false 119 | className: "" 120 | labels: {} 121 | annotations: 122 | {} 123 | # kubernetes.io/ingress.class: nginx 124 | # kubernetes.io/tls-acme: "true" 125 | hosts: 126 | - host: chart-example.local 127 | paths: 128 | - path: / 129 | pathType: ImplementationSpecific 130 | tls: [] 131 | # - secretName: chart-example-tls 132 | # hosts: 133 | # - chart-example.local 134 | 135 | # Resource settings for the container 136 | resources: 137 | {} 138 | # We usually recommend not to specify default resources and to leave this as a conscious 139 | # choice for the user. This also increases chances charts run on environments with little 140 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 141 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 142 | # limits: 143 | # cpu: 100m 144 | # memory: 128Mi 145 | # requests: 146 | # cpu: 100m 147 | # memory: 128Mi 148 | 149 | # Node selector settings for scheduling the pod on specific nodes 150 | nodeSelector: {} 151 | 152 | # Tolerations settings for scheduling the pod based on node taints 153 | tolerations: [] 154 | 155 | # Affinity settings for controlling pod scheduling 156 | affinity: {} 157 | 158 | # Set a priorityClassName on Home Assistant pods 159 | priorityClassName: "" 160 | 161 | initContainers: [] 162 | # Example of integrating a custom component before starting the Home Assistant container 163 | # Uses emptyDir custom-components, see below 164 | # - name: init-panasonic-cc 165 | # image: alpine/git 166 | # command: [ "/bin/sh", "-c" ] 167 | # args: 168 | # - | 169 | # git clone https://github.com/sockless-coding/panasonic_cc.git /git/panasonic_cc 170 | # cp -r /git/panasonic_cc/custom_components/panasonic_cc/* /panasonic_cc 171 | # chown -R 1000:1000 /panasonic_cc/* 172 | # volumeMounts: 173 | # - name: custom-components 174 | # mountPath: /panasonic_cc 175 | 176 | # Configuration for Home Assistant 177 | configuration: 178 | # Enable or disable the configuration setup for Home Assistant 179 | enabled: false 180 | # Force init will merge the current configuration file with the default configuration on every start 181 | # This is useful when you want to ensure that the configuration file is always up to date 182 | forceInit: false 183 | # List of trusted proxies in the format of CIDR notation in a case of using a reverse proxy 184 | # Here is the list of the most common private IP ranges, use your list of possible trusted proxies, usually, it's the IP of the reverse proxy 185 | trusted_proxies: 186 | - 10.0.0.0/8 187 | - 172.16.0.0/12 188 | - 192.168.0.0/16 189 | - 127.0.0.0/8 190 | # Template for the configuration.yaml file 191 | # Used the `tpl` function to render the template, so you can use Go template functions 192 | templateConfig: |- 193 | # Loads default set of integrations. Do not remove. 194 | default_config: 195 | 196 | {{- if or .Values.ingress.enabled .Values.ingress.external }} 197 | http: 198 | use_x_forwarded_for: true 199 | trusted_proxies: 200 | {{- range .Values.configuration.trusted_proxies }} 201 | - {{ . }} 202 | {{- end }} 203 | {{- end}} 204 | # Load frontend themes from the themes folder 205 | frontend: 206 | themes: !include_dir_merge_named themes 207 | 208 | automation: !include automations.yaml 209 | script: !include scripts.yaml 210 | scene: !include scenes.yaml 211 | 212 | # Init script for the Home Assistant initialization, you can use Go template functions 213 | # Script is executed before the Home Assistant container starts and is used to prepare the configuration 214 | # Will be executed only if the configuration.enabled is set to true 215 | initScript: |- 216 | #!/bin/bash 217 | set -e 218 | 219 | # Check if the configuration file exists 220 | if [ ! -f /config/configuration.yaml ]; then 221 | echo "Configuration file not found, creating a new one" 222 | cp /config-templates/configuration.yaml /config/configuration.yaml 223 | fi 224 | 225 | # Check if the force init is enabled 226 | forceInit="{{ .Values.configuration.forceInit }}" 227 | if [ "$forceInit" = "true" ]; then 228 | echo "Force init is enabled, overwriting the configuration file" 229 | current_time=$(date +%Y%m%d_%H%M%S) 230 | echo "Backup the current configuration file to configuration.yaml.$current_time" 231 | cp /config/configuration.yaml /config/configuration.yaml.$current_time 232 | echo "Before cleanup - all backup files:" 233 | ls -l /config/configuration.yaml.* 234 | echo "Cleaning up - keeping only 10 most recent backups..." 235 | ls -t /config/configuration.yaml.* 2>/dev/null | tail -n +11 | xargs -r rm 236 | echo "After cleanup - remaining backup files:" 237 | ls -l /config/configuration.yaml.* 238 | echo "The current configuration file will be merged with the default configuration file with this content:" 239 | cat /config-templates/configuration.yaml 240 | if [[ ! -s /config/configuration.yaml ]]; then 241 | # If /config/configuration.yaml is empty, use the content of /config-templates/configuration.yaml 242 | cat /config-templates/configuration.yaml > /config/configuration.yaml 243 | else 244 | # Perform the merge operation if /config/configuration.yaml is not empty 245 | yq eval-all --inplace 'select(fileIndex == 0) *d select(fileIndex == 1)' /config/configuration.yaml /config-templates/configuration.yaml 246 | fi 247 | fi 248 | 249 | # Check if the automations file exists 250 | if [ ! -f /config/automations.yaml ]; then 251 | echo "Automations file not found, creating a new one" 252 | touch /config/automations.yaml 253 | echo "[]" >> /config/automations.yaml 254 | fi 255 | 256 | # Check if the scripts file exists 257 | if [ ! -f /config/scripts.yaml ]; then 258 | echo "Scripts file not found, creating a new one" 259 | touch /config/scripts.yaml 260 | fi 261 | 262 | # Check if the scenes file exists 263 | if [ ! -f /config/scenes.yaml ]; then 264 | echo "Scenes file not found, creating a new one" 265 | touch /config/scenes.yaml 266 | fi 267 | 268 | initContainer: 269 | name: setup-config 270 | image: mikefarah/yq:4 271 | securityContext: 272 | runAsUser: 0 273 | command: ["/bin/sh", "-c"] 274 | args: 275 | - /bin/sh /mnt/init/init.sh 276 | # env: 277 | # - name: FORCE_INIT 278 | # valueFrom: 279 | # configMapKeyRef: 280 | # name: init-script 281 | # key: forceInit 282 | # Home Assistant configuration volume will be mounted to /config automatically 283 | volumeMounts: 284 | - name: init-volume 285 | mountPath: /mnt/init/init.sh 286 | subPath: init.sh 287 | - name: config-volume 288 | mountPath: /config-templates 289 | 290 | # Persistence values for the Home Assistant instance 291 | persistence: 292 | # Enable or disable persistence 293 | enabled: false 294 | # Access mode for the persistent volume claim 295 | accessMode: ReadWriteOnce 296 | # Size of the persistent volume claim 297 | size: 5Gi 298 | # Storage class for the persistent volume claim 299 | storageClass: "" 300 | # Name of the existing volume for the StatefulSet, this option can be used to bind to an existing PV 301 | existingVolume: "" 302 | # Name of the existing PVC to use with Deployment, this option can be used to use an existing PVC 303 | existingClaim: "" 304 | # Annotations to add to the persistent volume claim 305 | annotations: {} 306 | # k8up.io/backup: "true" 307 | # another-annotation: "value" 308 | ## Persistent Volume selectors 309 | ## https://kubernetes.io/docs/concepts/storage/persistent-volumes/#selector 310 | matchLabels: {} 311 | matchExpressions: {} 312 | 313 | # if you need any additional volumes, you can define them here 314 | additionalVolumes: [] 315 | # - name: custom-components 316 | # emptyDir: {} 317 | 318 | # - hostPath: 319 | # path: >- 320 | # /dev/serial/by-id/usb-Silicon_Labs_Sonoff_Zigbee_3.0_USB_Dongle_Plus_0001-if00-port0 321 | # type: CharDevice 322 | # name: usb 323 | # if you need any additional volume mounts, you can define them here 324 | additionalMounts: [] 325 | # - mountPath: /config/custom_components/panasonic_cc 326 | # name: custom-components 327 | 328 | # - mountPath: /dev/ttyACM0 329 | # name: usb 330 | 331 | # if you need to expose additional ports 332 | additionalPorts: [] 333 | # - name: sia 334 | # containerPort: 8124 335 | # protocol: TCP 336 | 337 | # if you need to expose additional services 338 | additionalServices: [] 339 | # - name: sia 340 | # port: 8124 341 | # targetPort: sia 342 | # type: NodePort 343 | # protocol: TCP 344 | # nodePort: 30124 345 | 346 | livenessProbe: 347 | failureThreshold: 3 348 | httpGet: 349 | path: / 350 | port: http 351 | scheme: HTTP 352 | periodSeconds: 20 353 | successThreshold: 1 354 | timeoutSeconds: 2 355 | readinessProbe: 356 | failureThreshold: 3 357 | httpGet: 358 | path: / 359 | port: http 360 | scheme: HTTP 361 | periodSeconds: 10 362 | successThreshold: 1 363 | timeoutSeconds: 1 364 | startupProbe: {} 365 | # initialDelaySeconds: 1 366 | # periodSeconds: 5 367 | # timeoutSeconds: 1 368 | # successThreshold: 1 369 | # failureThreshold: 1 370 | # httpGet: 371 | # scheme: HTTP 372 | # path: / 373 | # port: http 374 | 375 | serviceMonitor: 376 | # requires HA integration: https://www.home-assistant.io/integrations/prometheus/ 377 | enabled: false 378 | scrapeInterval: 30s 379 | labels: {} 380 | # Bearer token authentication configuration 381 | bearerToken: {} 382 | # Name of the secret containing the bearer token 383 | # secretName: "" 384 | # Key in the secret containing the bearer token 385 | # secretKey: "" 386 | 387 | # Addons configuration for additional services 388 | addons: 389 | # Code-server addon configuration 390 | codeserver: 391 | # Enable or disable the code-server addon 392 | enabled: false 393 | # Resource settings for the code-server container 394 | resources: {} 395 | # Image settings for the code-server addon 396 | image: 397 | # Repository for the code-server image 398 | repository: ghcr.io/coder/code-server 399 | # Image pull policy for the code-server image 400 | pullPolicy: IfNotPresent 401 | # Tag for the code-server image 402 | tag: "4.105.1" 403 | # Authentication settings for the code-server addon 404 | auth: 405 | # Enable or disable authentication (if disabled, --auth none is used) 406 | enabled: false 407 | # Existing secret containing the password 408 | # The secret should have a key 'password' containing the code-server password 409 | existingSecret: "" 410 | # Password for code-server (only used if existingSecret is not set) 411 | # If neither existingSecret nor password is set, code-server will generate a random password 412 | # which will be printed in the logs 413 | password: "" 414 | # Service settings 415 | service: 416 | # Service type (ClusterIP, NodePort, LoadBalancer, or ExternalName) 417 | type: ClusterIP 418 | # Service port 419 | port: 12321 420 | # Ingress settings for the code-server addon 421 | ingress: 422 | # Enable or disable the ingress for the code-server addon 423 | enabled: false 424 | # Ingress class name 425 | className: "" 426 | # Ingress annotations 427 | annotations: {} 428 | # Ingress hosts configuration 429 | hosts: 430 | - host: chart-example.local 431 | paths: 432 | - path: / 433 | pathType: ImplementationSpecific 434 | # Ingress TLS configuration 435 | tls: [] 436 | # if you need any additional volume mounts, you can define them here 437 | additionalMounts: [] 438 | # - mountPath: /home/coder/.ssh/id_rsa 439 | # name: id-rsa 440 | 441 | # Controller configuration 442 | controller: 443 | # Type of controller to use: StatefulSet or Deployment 444 | type: StatefulSet 445 | 446 | # Annotations to add to the stateful set 447 | statefulSetAnnotations: {} 448 | 449 | # Annotations to add to the deployment 450 | deploymentAnnotations: {} 451 | 452 | # Deployment strategy type (RollingUpdate, Recreate) 453 | deploymentStrategy: RollingUpdate 454 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Helm chart for Home Assistant 2 | 3 | ![Latest Released Version](https://img.shields.io/github/v/tag/pajikos/home-assistant-helm-chart?sort=semver) 4 | ![Helm Chart Release](https://github.com/pajikos/home-assistant-helm-chart/actions/workflows/build-helm-chart-release.yaml/badge.svg) 5 | ![Auto-update latest HA version](https://github.com/pajikos/home-assistant-helm-chart/actions/workflows/check_ha_release.yml/badge.svg) 6 | [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/helm-hass)](https://artifacthub.io/packages/search?repo=helm-hass) 7 | 8 | ## Introduction 9 | 10 | This chart bootstraps a [Home Assistant](https://home-assistant.io) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. 11 | 12 | It is updated **automatically** with each new release of Home Assistant, ensuring you always have access to the latest features and improvements. 13 | 14 | ## Features 15 | 16 | - **Automatic Updates**: The chart is updated with each new release of Home Assistant. 17 | - **Flexibility**: Extensive configuration options to tailor Home Assistant to your needs. 18 | - **Addons Support**: Extend Home Assistant's functionality with supported addons, such as code-server. 19 | 20 | ## Quick Start 21 | 22 | To deploy Home Assistant using this Helm chart, follow these steps: 23 | 24 | ```console 25 | $ helm repo add pajikos http://pajikos.github.io/home-assistant-helm-chart/ 26 | $ helm repo update 27 | $ helm install home-assistant pajikos/home-assistant 28 | ``` 29 | 30 | This will deploy Home Assistant with the default configuration. See the [Configuration](#configuration) section for details on customizing the deployment. 31 | 32 | 33 | > **Tip**: List all releases using `helm list` 34 | 35 | ## Uninstalling the Chart 36 | 37 | To uninstall/delete the `home-assistant` deployment: 38 | 39 | ```console 40 | $ helm delete home-assistant 41 | ``` 42 | 43 | The command removes all the Kubernetes components associated with the chart and deletes the release. 44 | 45 | ## Configuration 46 | 47 | The following table lists the configurable parameters of the Home Assistant chart and their default values. 48 | 49 | # Home Assistant Helm Chart 50 | 51 | This document provides detailed configuration options for the Home Assistant Helm chart. 52 | 53 | | Parameter | Description | Default | 54 | | --------- | ----------- | ------- | 55 | | `replicaCount` | Number of replicas for the deployment | `1` | 56 | | `image.repository` | Repository for the Home Assistant image | `ghcr.io/home-assistant/home-assistant` | 57 | | `image.pullPolicy` | Image pull policy | `IfNotPresent` | 58 | | `image.tag` | Overrides the image tag (default is the chart appVersion) | `""` | 59 | | `image.imagePullSecrets` | List of imagePullSecrets for private image repositories | `[]` | 60 | | `nameOverride` | Override the default name of the Helm chart | `""` | 61 | | `fullnameOverride` | Override the default full name of the Helm chart | `""` | 62 | | `serviceAccount.create` | Specifies whether a service account should be created | `true` | 63 | | `serviceAccount.annotations` | Annotations to add to the service account | `{}` | 64 | | `serviceAccount.name` | The name of the service account to use | `""` | 65 | | `podAnnotations` | Annotations to add to the pod | `{}` | 66 | | `controller.type` | Type of controller to use: StatefulSet or Deployment | `StatefulSet` | 67 | | `statefulSetAnnotations` | Annotations to add to the StatefulSet | `{}` | 68 | | `deploymentAnnotations` | Annotations to add to the Deployment | `{}` | 69 | | `podSecurityContext` | Pod security context settings | `{}` | 70 | | `env` | Environment variables | `[]` | 71 | | `envFrom` | Use environment variables from ConfigMaps or Secrets | `[]` | 72 | | `hostNetwork` | Specifies if the containers should be started in `hostNetwork` mode. | `false` | 73 | | `dnsPolicy` | Specifies the [`dnsPolicy`](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) for the pod. | `false` | 74 | | `hostPort.enabled` | Enable 'hostPort' or not | `false` | 75 | | `hostPort.port` | Port number | `8123` | 76 | | `dnsConfig` | Override the default dnsConfig and set your own nameservers or ndots, among other options | `{}` | 77 | | `service.type` | Service type (ClusterIP, NodePort, LoadBalancer, or ExternalName) | `ClusterIP` | 78 | | `service.port` | Service port | `8080` | 79 | | `service.annotations` | Annotations to add to the service | `{}` | 80 | | `ingress.enabled` | Enable ingress for Home Assistant | `false` | 81 | | `ingress.external` | Enable external ingress (cannot be true when ingress.enabled is true) | `false` | 82 | | `resources` | Resource settings for the container | `{}` | 83 | | `nodeSelector` | Node selector settings for scheduling the pod on specific nodes | `{}` | 84 | | `tolerations` | Tolerations settings for scheduling the pod based on node taints | `[]` | 85 | | `affinity` | Affinity settings for controlling pod scheduling | `{}` | 86 | | `priorityClassName` | Priority class name for Home Assistant pods | `""` | 87 | | `persistence.enabled` | Enables the creation of a Persistent Volume Claim (PVC) for Home Assistant. | `false` | 88 | | `persistence.accessMode` | The access mode of the PVC. | `ReadWriteOnce` | 89 | | `persistence.size` | The size of the PVC to create. | `5Gi` | 90 | | `persistence.storageClass` | The storage class to use for the PVC. If empty, the default storage class is used. | `""` | 91 | | `persistence.existingVolume` | The name of an existing Persistent Volume to bind to when using StatefulSet. This bypasses dynamic provisioning. | `""` | 92 | | `persistence.existingClaim` | The name of an existing PVC to use when using Deployment. | `""` | 93 | | `persistence.matchLabels` | Label selectors to apply when binding to an existing Persistent Volume. | `{}` | 94 | | `persistence.matchExpressions` | Expression selectors to apply when binding to an existing Persistent Volume. | `{}` | 95 | | `persistence.annotations` | Annotations to add to the PVC. | `{}` | 96 | | `additionalVolumes` | Additional volumes to be mounted in the home assistant container | `[]` | 97 | | `additionalMounts` | Additional volume mounts to be mounted in the home assistant container | `[]` | 98 | | `initContainers` | List of initialization containers | `[]` | 99 | | `configuration.enabled` | Enable or disable the configuration setup for Home Assistant | `false` | 100 | | `configuration.forceInit` | Force init will merge the current configuration file with the default configuration on every start | `false` | 101 | | `configuration.trusted_proxies` | List of trusted proxies in CIDR notation | `["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8"]` | 102 | | `configuration.templateConfig` | Template for the `configuration.yaml` file | See Advanced Configuration | 103 | | `configuration.initScript` | Init script for Home Assistant initialization | See values.yaml for the complete configuration options | 104 | | `configuration.initContainer` | Configuration for the init container | See values.yaml for the complete configuration options | 105 | | `addons.codeserver.enabled` | Enable or disable the code-server addon | `false` | 106 | | `addons.codeserver.resources` | Resource settings for the code-server container | `{}` | 107 | | `addons.codeserver.image.repository` | Repository for the code-server image | `ghcr.io/coder/code-server` | 108 | | `addons.codeserver.image.pullPolicy` | Image pull policy for the code-server image | `IfNotPresent` | 109 | | `addons.codeserver.image.tag` | Tag for the code-server image | `latest released version, automatically updated` | 110 | | `addons.codeserver.auth.enabled` | Enable or disable authentication for the code-server addon | `false` | 111 | | `addons.codeserver.auth.existingSecret` | Existing secret containing the password (key: `password`) | `""` | 112 | | `addons.codeserver.auth.password` | Password for code-server (only used if existingSecret is not set) | `""` | 113 | | `addons.codeserver.service.type` | Service type for the code-server addon | `ClusterIP` | 114 | | `addons.codeserver.service.port` | Service port for the code-server addon | `12321` | 115 | | `addons.codeserver.ingress.enabled` | Enable or disable the ingress for the code-server addon | `false` | 116 | | `addons.codeserver.ingress.hosts` | Hosts for the code-server addon | `[]` | 117 | | `addons.codeserver.ingress.tls` | TLS settings for the code-server addon | `[]` | 118 | | `addons.codeserver.ingress.annotations` | Annotations for the code-server addon | `{}` | 119 | 120 | ## Controller Type 121 | 122 | This chart supports two types of controllers for deploying Home Assistant: 123 | 124 | 1. `StatefulSet` (default): Recommended for production use, especially when persistence is enabled. StatefulSets provide stable network identities and persistent storage that survives pod rescheduling. 125 | 126 | 2. `Deployment`: Simpler controller type that might be preferred in some scenarios. When using a Deployment with persistence enabled, a separate PVC is created instead of using volumeClaimTemplates. 127 | 128 | To specify the controller type, set the `controller.type` value: 129 | 130 | ```yaml 131 | controller: 132 | type: StatefulSet # or Deployment 133 | ``` 134 | 135 | ## Persistence 136 | 137 | The default configuration of this chart uses an `emptyDir` volume for persistence, which means that data is lost when the pod is removed. To enable persistent storage that survives pod restarts and redeployments, you can configure the chart to use a Persistent Volume Claim (PVC). 138 | 139 | ### Enabling Persistence 140 | 141 | To enable persistence, set `persistence.enabled` to `true`. You can also specify the desired `accessMode` and `size` for the PVC. By default, the `accessMode` is set to `ReadWriteOnce`, and there is no default storage class (`storageClass: ""`), meaning the cluster's default storage class will be used. 142 | 143 | ```yaml 144 | persistence: 145 | enabled: true 146 | accessMode: ReadWriteOnce 147 | size: 5Gi 148 | storageClass: "" 149 | ``` 150 | 151 | ### Using an Existing Volume or PVC 152 | 153 | Depending on the controller type you're using, you can either bind to an existing Persistent Volume (PV) or use an existing Persistent Volume Claim (PVC): 154 | 155 | #### With StatefulSet (default) 156 | 157 | If you already have a Persistent Volume (PV) that you wish to use with a StatefulSet, you can specify the name of this existing volume in the `persistence.existingVolume` field. This will direct the chart to use the specified PV, bypassing dynamic volume provisioning. 158 | 159 | ```yaml 160 | controller: 161 | type: StatefulSet 162 | persistence: 163 | enabled: true 164 | existingVolume: "my-existing-volume" 165 | ``` 166 | 167 | #### With Deployment 168 | 169 | If you're using a Deployment and already have a PVC that you wish to use, you can specify the name of this existing claim in the `persistence.existingClaim` field: 170 | 171 | ```yaml 172 | controller: 173 | type: Deployment 174 | persistence: 175 | enabled: true 176 | existingClaim: "my-existing-pvc" 177 | ``` 178 | 179 | Alternatively, if you want to bind to a specific PV with a Deployment, you can create a new PVC that binds to the PV by setting `persistence.existingVolume`: 180 | 181 | ```yaml 182 | controller: 183 | type: Deployment 184 | persistence: 185 | enabled: true 186 | existingVolume: "my-existing-volume" 187 | ``` 188 | 189 | When using an existing volume or claim, ensure that the `accessMode` and `size` specified in the chart values match the capabilities and capacity of the existing PV/PVC. 190 | 191 | ### Selectors 192 | 193 | You can further refine the selection of an existing PV using `matchLabels` or `matchExpressions` under the `persistence` section. These selectors will be used to match the existing PVs based on their labels. 194 | 195 | ```yaml 196 | persistence: 197 | enabled: true 198 | matchLabels: 199 | type: fast-ssd 200 | matchExpressions: 201 | - key: "failure-domain.beta.kubernetes.io/zone" 202 | operator: "In" 203 | values: ["us-west-1a"] 204 | ``` 205 | 206 | ### PVC Annotations 207 | 208 | You can add annotations to the PVC by specifying them in the `persistence.annotations` field. This is useful for backup solutions like k8up that use annotations to identify resources for backup. 209 | 210 | ```yaml 211 | persistence: 212 | enabled: true 213 | annotations: 214 | k8up.io/backup: "true" 215 | another-annotation: "value" 216 | ``` 217 | 218 | > **Note**: When specifying an `existingVolume`, ensure that the PV is not already bound to another PVC, as a PV can only be bound to a single PVC at a time. 219 | 220 | ## Ingress 221 | 222 | The chart provides two mutually exclusive ways to configure ingress: 223 | 224 | 1. `ingress.enabled`: Traditional Kubernetes ingress configuration 225 | 2. `ingress.external`: For scenarios where the ingress is managed externally 226 | 227 | Note: These two options cannot be enabled simultaneously. Attempting to set both to `true` will result in a validation error. 228 | 229 | Example configuration: 230 | 231 | ```yaml 232 | ingress: 233 | enabled: true # Traditional ingress 234 | external: false # External ingress 235 | ``` 236 | 237 | In addition, you can specify the `ingress.hosts` and `ingress.tls` values. The default values are `[]` and `[]` respectively. 238 | The second option is to set `service.type` to `NodePort` or `LoadBalancer` (when ingress is not available in your cluster) 239 | 240 | ## HostPort and HostNetwork 241 | 242 | To enable hostPort, set `hostPort.enabled` to `true`. In addition, you can specify the `hostPort.port` value. The default value is `8123`. 243 | To enable hostNetwork, set `hostNetwork` to `true`. 244 | HostNetwork is required for auto-discovery of Home Assistant, when not using auto-discovery, hostNetwork is not required and not recommended. 245 | 246 | ## ServiceMonitor 247 | 248 | If you have the Prometheus Operator installed, you can enable a ServiceMonitor to scrape Home Assistant metrics by setting `serviceMonitor.enabled` to `true`. This requires the [Prometheus integration](https://www.home-assistant.io/integrations/prometheus/) to be configured in Home Assistant. 249 | 250 | ```yaml 251 | serviceMonitor: 252 | enabled: true 253 | scrapeInterval: 30s 254 | labels: 255 | release: prometheus 256 | # Bearer token authentication configuration (optional) 257 | bearerToken: 258 | secretName: "prometheus-token" 259 | secretKey: "token" 260 | ``` 261 | 262 | ### Bearer Token Authentication 263 | 264 | To use bearer token authentication for the ServiceMonitor, simply provide both: 265 | - `secretName`: The name of the Kubernetes secret containing the token 266 | - `secretKey`: The key within that secret containing the actual token 267 | 268 | This lets you secure the metrics endpoint with a bearer token that Prometheus will use for authentication. 269 | 270 | 271 | ## Addons 272 | 273 | The Home Assistant chart supports the following addons: 274 | 275 | * [code-server](https://github.com/coder/code-server) 276 | 277 | ## Additional volumes and volume mounts 278 | 279 | To add additional volumes and volume mounts, you can use the `additionalVolumes` and `additionalMounts` values. The default values are `[]`. 280 | Example mounting usb devices: 281 | 282 | ```yaml 283 | 284 | additionalVolumes: 285 | - hostPath: 286 | path: >- 287 | /dev/serial/by-id/usb-ITEAD_SONOFF_Zigbee_3.0_USB_Dongle_Plus_V2_20230509111242-if00 288 | type: CharDevice 289 | name: usb 290 | 291 | additionalMounts: 292 | - mountPath: /dev/ttyACM0 293 | name: usb 294 | 295 | ``` 296 | 297 | Note: When mounting usb devices, you need to set the `securityContext.privileged` value to `true`. 298 | 299 | ## Advanced Configuration 300 | 301 | ### Init Containers 302 | 303 | Use init containers to perform tasks before starting Home Assistant, such as waiting for a dependency: 304 | 305 | ```yaml 306 | initContainers: 307 | - name: init-myservice 308 | image: busybox 309 | command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;'] 310 | ``` 311 | 312 | ### Home Assistant Configuration 313 | 314 | Customize Home Assistant's configuration directly through the Helm chart: 315 | 316 | ```yaml 317 | # Configuration for Home Assistant 318 | configuration: 319 | # Enable or disable the configuration setup for Home Assistant 320 | enabled: true 321 | # Force init will merge the current configuration file with the default configuration on every start 322 | # This is useful when you want to ensure that the configuration file is always up to date 323 | forceInit: true 324 | # List of trusted proxies in the format of CIDR notation in a case of using a reverse proxy 325 | # Here is the list of the most common private IP ranges, use your list of possible trusted proxies, usually, it's the IP of the reverse proxy 326 | trusted_proxies: 327 | - 10.42.0.0/16 # Add the IP address of your cluster CIDR 328 | # Editing templateConfig allows you to customize the configuration.yaml file 329 | # You can use Go template functions to customize the configuration 330 | templateConfig: |- 331 | # Loads default set of integrations. Do not remove. 332 | default_config: 333 | 334 | {{- if .Values.ingress.enabled }} 335 | http: 336 | use_x_forwarded_for: true 337 | trusted_proxies: 338 | {{- range .Values.configuration.trusted_proxies }} 339 | - {{ . }} 340 | {{- end }} 341 | {{- end}} 342 | # Load frontend themes from the themes folder 343 | frontend: 344 | themes: !include_dir_merge_named themes 345 | 346 | automation: !include automations.yaml 347 | script: !include scripts.yaml 348 | scene: !include scenes.yaml 349 | ``` 350 | 351 | This allows for dynamic configuration based on your Helm values. 352 | 353 | 354 | ## code-server 355 | 356 | To enable the code-server addon, set `addons.codeserver.enabled` to `true`. In addition, you can specify the `addons.codeserver.resources` values. The default value is `{}`. 357 | To be able to access the code-server addon, you need to enable the ingress for the code-server addon by setting `addons.codeserver.ingress.enabled` to `true` or setting `service.type` to `NodePort` or `LoadBalancer`. 358 | 359 | ### Authentication 360 | 361 | By default, authentication is disabled for code-server for backward compatibility (`addons.codeserver.auth.enabled: false`). However, it is **strongly recommended** to enable authentication, especially when exposing code-server via ingress or external access. You have three options for configuring authentication: 362 | 363 | 1. **Use an existing Kubernetes secret** (recommended for production): 364 | ```yaml 365 | addons: 366 | codeserver: 367 | enabled: true 368 | auth: 369 | enabled: true 370 | existingSecret: "my-codeserver-secret" 371 | ``` 372 | The secret should contain a key named `password` with your desired password. 373 | 374 | 2. **Set a password directly in values**: 375 | ```yaml 376 | addons: 377 | codeserver: 378 | enabled: true 379 | auth: 380 | enabled: true 381 | password: "my-secure-password" 382 | ``` 383 | 384 | 3. **Let code-server generate a random password**: 385 | ```yaml 386 | addons: 387 | codeserver: 388 | enabled: true 389 | auth: 390 | enabled: true 391 | ``` 392 | The randomly generated password will be printed in the code-server container logs. You can retrieve it with: 393 | ```bash 394 | kubectl logs -c codeserver 395 | ``` 396 | 397 | 4. **Disable authentication** (not recommended for production): 398 | ```yaml 399 | addons: 400 | codeserver: 401 | enabled: true 402 | auth: 403 | enabled: false 404 | ``` 405 | 406 | ## Upgrade Notes (v0.3) 407 | 408 | This release adds support for both `StatefulSet` (default/legacy) and `Deployment` controllers, and clarifies persistence usage. 409 | 410 | ### Key Changes 411 | - New value: `controller.type`—default remains `StatefulSet` for backward compatibility; use `Deployment` by setting `controller.type: Deployment`. 412 | - The auto-generated PVC for `controller.type: Deployment` is now named `-pvc` (vs. prior defaults for StatefulSet). Update all references if switching controller type! 413 | - `persistence.existingClaim` is only supported with Deployment; `persistence.existingVolume` with StatefulSet. 414 | - Manual cleanup may be needed if switching controller kind (e.g. legacy StatefulSet/PVC may remain until manually deleted). 415 | 416 | ### Migration Guidance 417 | - If you keep using StatefulSet, no changes required. 418 | - If switching to Deployment: 419 | - Update any automation or manifests to refer to the new PVC name (`-pvc` suffix). 420 | - Review and clean up old StatefulSet/volume resources as needed after migration. 421 | 422 | See "Controller Type" and "Persistence" sections above for full explanation. 423 | -------------------------------------------------------------------------------- /charts/home-assistant/README.md: -------------------------------------------------------------------------------- 1 | # Helm chart for Home Assistant 2 | 3 | ![Latest Released Version](https://img.shields.io/github/v/tag/pajikos/home-assistant-helm-chart?sort=semver) 4 | ![Helm Chart Release](https://github.com/pajikos/home-assistant-helm-chart/actions/workflows/build-helm-chart-release.yaml/badge.svg) 5 | ![Auto-update latest HA version](https://github.com/pajikos/home-assistant-helm-chart/actions/workflows/check_ha_release.yml/badge.svg) 6 | [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/helm-hass)](https://artifacthub.io/packages/search?repo=helm-hass) 7 | 8 | ## Introduction 9 | 10 | This chart bootstraps a [Home Assistant](https://home-assistant.io) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. 11 | 12 | It is updated **automatically** with each new release of Home Assistant, ensuring you always have access to the latest features and improvements. 13 | 14 | ## Features 15 | 16 | - **Automatic Updates**: The chart is updated with each new release of Home Assistant. 17 | - **Flexibility**: Extensive configuration options to tailor Home Assistant to your needs. 18 | - **Addons Support**: Extend Home Assistant's functionality with supported addons, such as code-server. 19 | 20 | ## Quick Start 21 | 22 | To deploy Home Assistant using this Helm chart, follow these steps: 23 | 24 | ```console 25 | $ helm repo add pajikos http://pajikos.github.io/home-assistant-helm-chart/ 26 | $ helm repo update 27 | $ helm install home-assistant pajikos/home-assistant 28 | ``` 29 | 30 | This will deploy Home Assistant with the default configuration. See the [Configuration](#configuration) section for details on customizing the deployment. 31 | 32 | 33 | > **Tip**: List all releases using `helm list` 34 | 35 | ## Uninstalling the Chart 36 | 37 | To uninstall/delete the `home-assistant` deployment: 38 | 39 | ```console 40 | $ helm delete home-assistant 41 | ``` 42 | 43 | The command removes all the Kubernetes components associated with the chart and deletes the release. 44 | 45 | ## Configuration 46 | 47 | The following table lists the configurable parameters of the Home Assistant chart and their default values. 48 | 49 | # Home Assistant Helm Chart 50 | 51 | This document provides detailed configuration options for the Home Assistant Helm chart. 52 | 53 | | Parameter | Description | Default | 54 | | --------- | ----------- | ------- | 55 | | `replicaCount` | Number of replicas for the deployment | `1` | 56 | | `image.repository` | Repository for the Home Assistant image | `ghcr.io/home-assistant/home-assistant` | 57 | | `image.pullPolicy` | Image pull policy | `IfNotPresent` | 58 | | `image.tag` | Overrides the image tag (default is the chart appVersion) | `""` | 59 | | `image.imagePullSecrets` | List of imagePullSecrets for private image repositories | `[]` | 60 | | `nameOverride` | Override the default name of the Helm chart | `""` | 61 | | `fullnameOverride` | Override the default full name of the Helm chart | `""` | 62 | | `serviceAccount.create` | Specifies whether a service account should be created | `true` | 63 | | `serviceAccount.annotations` | Annotations to add to the service account | `{}` | 64 | | `serviceAccount.name` | The name of the service account to use | `""` | 65 | | `podAnnotations` | Annotations to add to the pod | `{}` | 66 | | `controller.type` | Type of controller to use: StatefulSet or Deployment | `StatefulSet` | 67 | | `statefulSetAnnotations` | Annotations to add to the StatefulSet | `{}` | 68 | | `deploymentAnnotations` | Annotations to add to the Deployment | `{}` | 69 | | `podSecurityContext` | Pod security context settings | `{}` | 70 | | `env` | Environment variables | `[]` | 71 | | `envFrom` | Use environment variables from ConfigMaps or Secrets | `[]` | 72 | | `hostNetwork` | Specifies if the containers should be started in `hostNetwork` mode. | `false` | 73 | | `dnsPolicy` | Specifies the [`dnsPolicy`](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) for the pod. | `false` | 74 | | `hostPort.enabled` | Enable 'hostPort' or not | `false` | 75 | | `hostPort.port` | Port number | `8123` | 76 | | `dnsConfig` | Override the default dnsConfig and set your own nameservers or ndots, among other options | `{}` | 77 | | `service.type` | Service type (ClusterIP, NodePort, LoadBalancer, or ExternalName) | `ClusterIP` | 78 | | `service.port` | Service port | `8080` | 79 | | `service.annotations` | Annotations to add to the service | `{}` | 80 | | `ingress.enabled` | Enable ingress for Home Assistant | `false` | 81 | | `ingress.external` | Enable external ingress (cannot be true when ingress.enabled is true) | `false` | 82 | | `resources` | Resource settings for the container | `{}` | 83 | | `nodeSelector` | Node selector settings for scheduling the pod on specific nodes | `{}` | 84 | | `tolerations` | Tolerations settings for scheduling the pod based on node taints | `[]` | 85 | | `affinity` | Affinity settings for controlling pod scheduling | `{}` | 86 | | `priorityClassName` | Priority class name for Home Assistant pods | `""` | 87 | | `persistence.enabled` | Enables the creation of a Persistent Volume Claim (PVC) for Home Assistant. | `false` | 88 | | `persistence.accessMode` | The access mode of the PVC. | `ReadWriteOnce` | 89 | | `persistence.size` | The size of the PVC to create. | `5Gi` | 90 | | `persistence.storageClass` | The storage class to use for the PVC. If empty, the default storage class is used. | `""` | 91 | | `persistence.existingVolume` | The name of an existing Persistent Volume to bind to when using StatefulSet. This bypasses dynamic provisioning. | `""` | 92 | | `persistence.existingClaim` | The name of an existing PVC to use when using Deployment. | `""` | 93 | | `persistence.matchLabels` | Label selectors to apply when binding to an existing Persistent Volume. | `{}` | 94 | | `persistence.matchExpressions` | Expression selectors to apply when binding to an existing Persistent Volume. | `{}` | 95 | | `persistence.annotations` | Annotations to add to the PVC. | `{}` | 96 | | `additionalVolumes` | Additional volumes to be mounted in the home assistant container | `[]` | 97 | | `additionalMounts` | Additional volume mounts to be mounted in the home assistant container | `[]` | 98 | | `initContainers` | List of initialization containers | `[]` | 99 | | `configuration.enabled` | Enable or disable the configuration setup for Home Assistant | `false` | 100 | | `configuration.forceInit` | Force init will merge the current configuration file with the default configuration on every start | `false` | 101 | | `configuration.trusted_proxies` | List of trusted proxies in CIDR notation | `["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8"]` | 102 | | `configuration.templateConfig` | Template for the `configuration.yaml` file | See Advanced Configuration | 103 | | `configuration.initScript` | Init script for Home Assistant initialization | See values.yaml for the complete configuration options | 104 | | `configuration.initContainer` | Configuration for the init container | See values.yaml for the complete configuration options | 105 | | `addons.codeserver.enabled` | Enable or disable the code-server addon | `false` | 106 | | `addons.codeserver.resources` | Resource settings for the code-server container | `{}` | 107 | | `addons.codeserver.image.repository` | Repository for the code-server image | `ghcr.io/coder/code-server` | 108 | | `addons.codeserver.image.pullPolicy` | Image pull policy for the code-server image | `IfNotPresent` | 109 | | `addons.codeserver.image.tag` | Tag for the code-server image | `latest released version, automatically updated` | 110 | | `addons.codeserver.auth.enabled` | Enable or disable authentication for the code-server addon | `false` | 111 | | `addons.codeserver.auth.existingSecret` | Existing secret containing the password (key: `password`) | `""` | 112 | | `addons.codeserver.auth.password` | Password for code-server (only used if existingSecret is not set) | `""` | 113 | | `addons.codeserver.service.type` | Service type for the code-server addon | `ClusterIP` | 114 | | `addons.codeserver.service.port` | Service port for the code-server addon | `12321` | 115 | | `addons.codeserver.ingress.enabled` | Enable or disable the ingress for the code-server addon | `false` | 116 | | `addons.codeserver.ingress.hosts` | Hosts for the code-server addon | `[]` | 117 | | `addons.codeserver.ingress.tls` | TLS settings for the code-server addon | `[]` | 118 | | `addons.codeserver.ingress.annotations` | Annotations for the code-server addon | `{}` | 119 | 120 | ## Controller Type 121 | 122 | This chart supports two types of controllers for deploying Home Assistant: 123 | 124 | 1. `StatefulSet` (default): Recommended for production use, especially when persistence is enabled. StatefulSets provide stable network identities and persistent storage that survives pod rescheduling. 125 | 126 | 2. `Deployment`: Simpler controller type that might be preferred in some scenarios. When using a Deployment with persistence enabled, a separate PVC is created instead of using volumeClaimTemplates. 127 | 128 | To specify the controller type, set the `controller.type` value: 129 | 130 | ```yaml 131 | controller: 132 | type: StatefulSet # or Deployment 133 | ``` 134 | 135 | ## Persistence 136 | 137 | The default configuration of this chart uses an `emptyDir` volume for persistence, which means that data is lost when the pod is removed. To enable persistent storage that survives pod restarts and redeployments, you can configure the chart to use a Persistent Volume Claim (PVC). 138 | 139 | ### Enabling Persistence 140 | 141 | To enable persistence, set `persistence.enabled` to `true`. You can also specify the desired `accessMode` and `size` for the PVC. By default, the `accessMode` is set to `ReadWriteOnce`, and there is no default storage class (`storageClass: ""`), meaning the cluster's default storage class will be used. 142 | 143 | ```yaml 144 | persistence: 145 | enabled: true 146 | accessMode: ReadWriteOnce 147 | size: 5Gi 148 | storageClass: "" 149 | ``` 150 | 151 | ### Using an Existing Volume or PVC 152 | 153 | Depending on the controller type you're using, you can either bind to an existing Persistent Volume (PV) or use an existing Persistent Volume Claim (PVC): 154 | 155 | #### With StatefulSet (default) 156 | 157 | If you already have a Persistent Volume (PV) that you wish to use with a StatefulSet, you can specify the name of this existing volume in the `persistence.existingVolume` field. This will direct the chart to use the specified PV, bypassing dynamic volume provisioning. 158 | 159 | ```yaml 160 | controller: 161 | type: StatefulSet 162 | persistence: 163 | enabled: true 164 | existingVolume: "my-existing-volume" 165 | ``` 166 | 167 | #### With Deployment 168 | 169 | If you're using a Deployment and already have a PVC that you wish to use, you can specify the name of this existing claim in the `persistence.existingClaim` field: 170 | 171 | ```yaml 172 | controller: 173 | type: Deployment 174 | persistence: 175 | enabled: true 176 | existingClaim: "my-existing-pvc" 177 | ``` 178 | 179 | Alternatively, if you want to bind to a specific PV with a Deployment, you can create a new PVC that binds to the PV by setting `persistence.existingVolume`: 180 | 181 | ```yaml 182 | controller: 183 | type: Deployment 184 | persistence: 185 | enabled: true 186 | existingVolume: "my-existing-volume" 187 | ``` 188 | 189 | When using an existing volume or claim, ensure that the `accessMode` and `size` specified in the chart values match the capabilities and capacity of the existing PV/PVC. 190 | 191 | ### Selectors 192 | 193 | You can further refine the selection of an existing PV using `matchLabels` or `matchExpressions` under the `persistence` section. These selectors will be used to match the existing PVs based on their labels. 194 | 195 | ```yaml 196 | persistence: 197 | enabled: true 198 | matchLabels: 199 | type: fast-ssd 200 | matchExpressions: 201 | - key: "failure-domain.beta.kubernetes.io/zone" 202 | operator: "In" 203 | values: ["us-west-1a"] 204 | ``` 205 | 206 | ### PVC Annotations 207 | 208 | You can add annotations to the PVC by specifying them in the `persistence.annotations` field. This is useful for backup solutions like k8up that use annotations to identify resources for backup. 209 | 210 | ```yaml 211 | persistence: 212 | enabled: true 213 | annotations: 214 | k8up.io/backup: "true" 215 | another-annotation: "value" 216 | ``` 217 | 218 | > **Note**: When specifying an `existingVolume`, ensure that the PV is not already bound to another PVC, as a PV can only be bound to a single PVC at a time. 219 | 220 | ## Ingress 221 | 222 | The chart provides two mutually exclusive ways to configure ingress: 223 | 224 | 1. `ingress.enabled`: Traditional Kubernetes ingress configuration 225 | 2. `ingress.external`: For scenarios where the ingress is managed externally 226 | 227 | Note: These two options cannot be enabled simultaneously. Attempting to set both to `true` will result in a validation error. 228 | 229 | Example configuration: 230 | 231 | ```yaml 232 | ingress: 233 | enabled: true # Traditional ingress 234 | external: false # External ingress 235 | ``` 236 | 237 | In addition, you can specify the `ingress.hosts` and `ingress.tls` values. The default values are `[]` and `[]` respectively. 238 | The second option is to set `service.type` to `NodePort` or `LoadBalancer` (when ingress is not available in your cluster) 239 | 240 | ## HostPort and HostNetwork 241 | 242 | To enable hostPort, set `hostPort.enabled` to `true`. In addition, you can specify the `hostPort.port` value. The default value is `8123`. 243 | To enable hostNetwork, set `hostNetwork` to `true`. 244 | HostNetwork is required for auto-discovery of Home Assistant, when not using auto-discovery, hostNetwork is not required and not recommended. 245 | 246 | ## ServiceMonitor 247 | 248 | If you have the Prometheus Operator installed, you can enable a ServiceMonitor to scrape Home Assistant metrics by setting `serviceMonitor.enabled` to `true`. This requires the [Prometheus integration](https://www.home-assistant.io/integrations/prometheus/) to be configured in Home Assistant. 249 | 250 | ```yaml 251 | serviceMonitor: 252 | enabled: true 253 | scrapeInterval: 30s 254 | labels: 255 | release: prometheus 256 | # Bearer token authentication configuration (optional) 257 | bearerToken: 258 | secretName: "prometheus-token" 259 | secretKey: "token" 260 | ``` 261 | 262 | ### Bearer Token Authentication 263 | 264 | To use bearer token authentication for the ServiceMonitor, simply provide both: 265 | - `secretName`: The name of the Kubernetes secret containing the token 266 | - `secretKey`: The key within that secret containing the actual token 267 | 268 | This lets you secure the metrics endpoint with a bearer token that Prometheus will use for authentication. 269 | 270 | 271 | ## Addons 272 | 273 | The Home Assistant chart supports the following addons: 274 | 275 | * [code-server](https://github.com/coder/code-server) 276 | 277 | ## Additional volumes and volume mounts 278 | 279 | To add additional volumes and volume mounts, you can use the `additionalVolumes` and `additionalMounts` values. The default values are `[]`. 280 | Example mounting usb devices: 281 | 282 | ```yaml 283 | 284 | additionalVolumes: 285 | - hostPath: 286 | path: >- 287 | /dev/serial/by-id/usb-ITEAD_SONOFF_Zigbee_3.0_USB_Dongle_Plus_V2_20230509111242-if00 288 | type: CharDevice 289 | name: usb 290 | 291 | additionalMounts: 292 | - mountPath: /dev/ttyACM0 293 | name: usb 294 | 295 | ``` 296 | 297 | Note: When mounting usb devices, you need to set the `securityContext.privileged` value to `true`. 298 | 299 | ## Advanced Configuration 300 | 301 | ### Init Containers 302 | 303 | Use init containers to perform tasks before starting Home Assistant, such as waiting for a dependency: 304 | 305 | ```yaml 306 | initContainers: 307 | - name: init-myservice 308 | image: busybox 309 | command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;'] 310 | ``` 311 | 312 | ### Home Assistant Configuration 313 | 314 | Customize Home Assistant's configuration directly through the Helm chart: 315 | 316 | ```yaml 317 | # Configuration for Home Assistant 318 | configuration: 319 | # Enable or disable the configuration setup for Home Assistant 320 | enabled: true 321 | # Force init will merge the current configuration file with the default configuration on every start 322 | # This is useful when you want to ensure that the configuration file is always up to date 323 | forceInit: true 324 | # List of trusted proxies in the format of CIDR notation in a case of using a reverse proxy 325 | # Here is the list of the most common private IP ranges, use your list of possible trusted proxies, usually, it's the IP of the reverse proxy 326 | trusted_proxies: 327 | - 10.42.0.0/16 # Add the IP address of your cluster CIDR 328 | # Editing templateConfig allows you to customize the configuration.yaml file 329 | # You can use Go template functions to customize the configuration 330 | templateConfig: |- 331 | # Loads default set of integrations. Do not remove. 332 | default_config: 333 | 334 | {{- if .Values.ingress.enabled }} 335 | http: 336 | use_x_forwarded_for: true 337 | trusted_proxies: 338 | {{- range .Values.configuration.trusted_proxies }} 339 | - {{ . }} 340 | {{- end }} 341 | {{- end}} 342 | # Load frontend themes from the themes folder 343 | frontend: 344 | themes: !include_dir_merge_named themes 345 | 346 | automation: !include automations.yaml 347 | script: !include scripts.yaml 348 | scene: !include scenes.yaml 349 | ``` 350 | 351 | This allows for dynamic configuration based on your Helm values. 352 | 353 | 354 | ## code-server 355 | 356 | To enable the code-server addon, set `addons.codeserver.enabled` to `true`. In addition, you can specify the `addons.codeserver.resources` values. The default value is `{}`. 357 | To be able to access the code-server addon, you need to enable the ingress for the code-server addon by setting `addons.codeserver.ingress.enabled` to `true` or setting `service.type` to `NodePort` or `LoadBalancer`. 358 | 359 | ### Authentication 360 | 361 | By default, authentication is disabled for code-server for backward compatibility (`addons.codeserver.auth.enabled: false`). However, it is **strongly recommended** to enable authentication, especially when exposing code-server via ingress or external access. You have three options for configuring authentication: 362 | 363 | 1. **Use an existing Kubernetes secret** (recommended for production): 364 | ```yaml 365 | addons: 366 | codeserver: 367 | enabled: true 368 | auth: 369 | enabled: true 370 | existingSecret: "my-codeserver-secret" 371 | ``` 372 | The secret should contain a key named `password` with your desired password. 373 | 374 | 2. **Set a password directly in values**: 375 | ```yaml 376 | addons: 377 | codeserver: 378 | enabled: true 379 | auth: 380 | enabled: true 381 | password: "my-secure-password" 382 | ``` 383 | 384 | 3. **Let code-server generate a random password**: 385 | ```yaml 386 | addons: 387 | codeserver: 388 | enabled: true 389 | auth: 390 | enabled: true 391 | ``` 392 | The randomly generated password will be printed in the code-server container logs. You can retrieve it with: 393 | ```bash 394 | kubectl logs -c codeserver 395 | ``` 396 | 397 | 4. **Disable authentication** (not recommended for production): 398 | ```yaml 399 | addons: 400 | codeserver: 401 | enabled: true 402 | auth: 403 | enabled: false 404 | ``` 405 | 406 | ## Upgrade Notes (v0.3) 407 | 408 | This release adds support for both `StatefulSet` (default/legacy) and `Deployment` controllers, and clarifies persistence usage. 409 | 410 | ### Key Changes 411 | - New value: `controller.type`—default remains `StatefulSet` for backward compatibility; use `Deployment` by setting `controller.type: Deployment`. 412 | - The auto-generated PVC for `controller.type: Deployment` is now named `-pvc` (vs. prior defaults for StatefulSet). Update all references if switching controller type! 413 | - `persistence.existingClaim` is only supported with Deployment; `persistence.existingVolume` with StatefulSet. 414 | - Manual cleanup may be needed if switching controller kind (e.g. legacy StatefulSet/PVC may remain until manually deleted). 415 | 416 | ### Migration Guidance 417 | - If you keep using StatefulSet, no changes required. 418 | - If switching to Deployment: 419 | - Update any automation or manifests to refer to the new PVC name (`-pvc` suffix). 420 | - Review and clean up old StatefulSet/volume resources as needed after migration. 421 | 422 | See "Controller Type" and "Persistence" sections above for full explanation. 423 | --------------------------------------------------------------------------------