# Scale the whole chart down without uninstalling it: scaleDown: true renders # every Deployment/StatefulSet with replicas: 0 (and suspends CronJobs) and # takes precedence over any per-workload replicas value. PVCs, Secrets and # Services stay in place; scaleDown: false brings the workloads back up. scaleDown: false # Replica count for this workload; 0 is allowed and scales it down. # The chart-global scaleDown flag takes precedence. replicas: 1 # This is for setting up a service more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/ service: # This sets the service type more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types type: ClusterIP # This sets the ports more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports port: 80 # This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/ ingress: # MUTUALLY EXCLUSIVE WITH httpRoute.enabled - the render aborts when both are # true, and the message names both value paths. enabled: false # Ingress class; set to "" to omit ingressClassName from the rendered Ingress. # NOT READ IN GATEWAY MODE: the class is a property of the Gateway there, and # the Gateway belongs to the platform. className: nginx # NOT READ IN GATEWAY MODE either, and this is the one to be aware of: the # Gateway API has no annotation channel for controller behaviour, so # everything put here is simply gone in that mode. This chart sets nothing # itself, and the installation this repository serves does not deploy # homeassistant at all (checked 2026-10-12), so there is no operator-supplied # annotation here today that the switch would drop - unlike its siblings in # the same sweep. That is an absence of evidence about this installation, not # a guarantee about yours: whatever you put here disappears in Gateway mode. annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" # THE PUBLIC NAMES OF THIS RELEASE, AND READ IN BOTH MODES. # # This chart is the documented ingress exception (README): it keeps upstream's # flexible host list rather than the repository's required # domain/clusterIssuer pair, and that exception stays. The consequence for # Gitea #114 is that httpRoute has NO hostnames key - a chart-global hostname # list could not say which host entry, and therefore which set of paths, it # belonged to. So this list is where the hostnames live in BOTH modes, and in # Gateway mode the chart renders ONE HTTPRoute PER ENTRY, with one rule per # path of that entry. # # pathType IN GATEWAY MODE, because the default below is the awkward value. # Ingress has Exact, Prefix and ImplementationSpecific; the Gateway API has # Exact, PathPrefix and RegularExpression. Exact and Prefix map cleanly. # ImplementationSpecific means "ask the controller" and has NO counterpart, # so: # - on the path "/" it is rendered as PathPrefix (every controller treats a # root ImplementationSpecific path as a prefix match, so the default # below works in Gateway mode unchanged); # - on ANY OTHER path the render ABORTS and asks you to say which you mean. # In ingress-nginx an ImplementationSpecific sub-path is a REGEX and # PathPrefix is not one, so mapping it silently would change how the path # matches while the render stayed green. # RegularExpression is deliberately not offered: its support level is # Implementation-specific and would bind this chart to one Gateway # implementation. hosts: - host: chart-example.local paths: - path: / pathType: ImplementationSpecific # NOT READ IN GATEWAY MODE: the certificate sits on the Gateway's listener. tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local # THE GATEWAY-API ALTERNATIVE TO THE INGRESS (Gitea #114). # # Mutually exclusive with ingress.enabled - the render aborts when both are # true. The chart renders ONLY HTTPRoutes with parentRefs: no Gateway, no # class, no Certificate, and no implementation-specific policy object. Class # and certificate sit on the Gateway and its listeners and belong to the # platform (cluster-config), which is what keeps the Gateway implementation # replaceable without touching this chart again. # # TWO KEYS ONLY, and the missing third one is deliberate: there is no # httpRoute.hostnames here, because this chart has no ingress.domain either. # The hostnames and the paths come from ingress.hosts above - see the comment # there, including the pathType mapping. This is the freesailarr shape from # group 2 of the sweep, for the same reason. # # ONE HTTPRoute PER ENTRY of ingress.hosts, named # -homeassistant-httproute for the first and ...-httproute- for # the rest. REORDERING ingress.hosts RENAMES ROUTES and so deletes and # recreates them on the next upgrade instead of editing them - harmless, # because a route holds no state and no certificate, but worth knowing before # it looks like a bug. # # THE GATEWAY-API CRDs MUST BE PRESENT. The chart cannot detect them # (.Capabilities.APIVersions is unreliable at template time) and an HTTPRoute # without them fails at install, so this is a value and not a guess. httpRoute: enabled: false # The Gateway(s) EVERY route of this release attaches to - a platform object, # usually in another namespace, whose listener must allow this namespace in # allowedRoutes. Entries are the Gateway API's ParentReference (name, and # optionally namespace, sectionName, port, group, kind), passed through # unchanged: # # parentRefs: # - name: public # namespace: gateway # sectionName: https # # REQUIRED while httpRoute.enabled is true - the render aborts on an empty # list, because a route without a parent is created, is valid and routes # nothing, with no event to say so. The same list is used for every route # this chart renders; the hosts differ, the Gateway does not. parentRefs: [] # Documented deviation from the repo-wide hardening standard: the official # Home Assistant container must start as root — it boots via the s6-overlay # init system, which performs its setup as uid 0 (the image supports no # non-root user), so runAsNonRoot/runAsUser are deliberately not set. # Everything else is hardened: no privilege escalation, all capabilities # dropped and a read-only root filesystem with writable emptyDirs for # /tmp and /run (s6-overlay state). NET_RAW is added back because Home # Assistant's device discovery (DHCP watcher, ping/arp integrations) uses # raw sockets and logs "Operation not permitted" without it. securityContext: pod: {} container: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL add: - NET_RAW resources: # Limits are optional: memory and ephemeral-storage limits default to # limitFactor x the request, an explicitly set limit always wins, and a # CPU limit is only set when configured explicitly under "limits". limitFactor: 3 requests: cpu: 0.2 memory: 512Mi ephemeralStorage: 128Mi # This is to setup the liveness, readiness and startup probes more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ livenessProbe: httpGet: path: / port: http periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: / port: http periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 # The startup probe gates liveness/readiness. Home Assistant can take a while to # start up, so grant a generous budget: 10s * 30 = 300s. startupProbe: httpGet: path: / port: http periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 30 # Extra environment variables, appended to the ones the chart always sets. # Values are rendered through tpl; value, secretKeyRef and configMapKeyRef # forms are supported. env: [] # Additional volumes on the output Deployment definition. volumes: [] # - name: foo # secret: # secretName: mysecret # optional: false # Additional volumeMounts on the output Deployment definition. volumeMounts: [] # - name: foo # mountPath: "/etc/foo" # readOnly: true nodeSelector: {} tolerations: [] affinity: {} core: timezone: "UTC" storage: size: 5Gi storageClass: "default" # Must include the following keys: # - config # - http-config # - recorder-config # # Changing the op:// path below IS covered: the StatefulSet carries a # checksum/secret-ref annotation over the rendered OnePasswordItem, so pointing # this value at a different vault item restarts the pod. # # Known limitation (#82): changing the CONTENT of that item in the vault does # NOT restart Home Assistant. The operator updates the Kubernetes Secret, but # the running pod keeps the old files - they are mounted with subPath, which the # kubelet never refreshes, and Home Assistant reads its configuration only at # start. The change then takes effect at some unrelated later restart, possibly # months afterwards, and a broken value surfaces long after the edit that caused # it. That is especially likely for this release, which is currently kept at # zero replicas via scaleDown: whoever brings it back up gets whatever the vault # holds at that moment. # # The checksum/config mechanism the other charts use cannot close this gap: # Helm never sees the content (the operator fills the Secret in-cluster after # rendering), so there is nothing to hash. The operator's own # "operator.1password.io/auto-restart" annotation does not close it either - it # only ever restarts Deployments, never StatefulSets. Verified against # onepassword-operator 1.12.0: pkg/onepassword/secret_update_handler.go lists # exactly one workload type (appsv1.DeploymentList), and the PR adding # StatefulSets is still open. Setting the annotation here would look like a fix # and do nothing, so this chart deliberately does not set it. # # After changing the item, restart Home Assistant by hand: # kubectl rollout restart statefulset -homeassistant-sfs secrets: configSecretRef: null # --------------------------------------------------------------------------- # NetworkPolicy (Gitea #5). On by default. The reference implementation with # the full reasoning is template-chart; the convention is in README, "Chart # style guidelines", and the operator's view on this chart's wiki page. # --------------------------------------------------------------------------- networkPolicy: # WHAT THIS POLICY ALLOWS: inbound 8123 only. THERE IS NO EGRESS SECTION, and # that is the decision in this chart rather than an omission. # # Home Assistant's outbound traffic is the union of every integration enabled # in a release: mDNS/SSDP discovery (5353/UDP, 1900/UDP), MQTT (1883/8883), a # Zigbee or Z-Wave bridge on whatever port its firmware chose, ESPHome (6053), # printers, speakers, TVs, inverters, cloud APIs on 443, NTP on 123/UDP. A # chart cannot know that set, a cap would protect little - the devices sit on # the node's own LAN - and it would break integrations one at a time, months # apart, each looking like a flaky device rather than like a policy. So # policyTypes names Ingress only, for the same reason valkey names Ingress # only: a rule that has to be widened until it permits everything makes the # object look stricter than it is. # # An installation that wants an egress cap should write one against its own # integrations, at release level, where they are visible. # # Inbound 8123 is not restricted by source in the default (probe port), and a # policy is only a boundary where the CNI enforces one. enabled: true # Namespaces whose pods may reach this workload, in addition to the release's # own namespace, which is always allowed. Empty means: own namespace only. # # Matched on the "kubernetes.io/metadata.name" label Kubernetes sets on every # namespace itself, so no labelling work is needed on a namespace this chart # does not own - and no typo protection either: a name that does not exist # matches nothing and is silently ineffective. Note the granularity: an entry # opens the port to EVERY pod in that namespace. allowedNamespaces: [] # Source CIDRs allowed to reach port 8123, which is the port all three probes address. # # EMPTY MEANS OPEN, NOT BLOCKED. With no entry that rule is rendered WITHOUT a # "from" selector, so the port is reachable from anywhere the CNI routes to - # inside the cluster, since it carries no NodePort. # # That is the deliberate choice between two bad defaults. The kubelet's probes # originate from the IP of the WORKER NODE running the pod, an address this # chart cannot know and must not guess: a CIDR that excludes the real node # blocks every probe, the pod never becomes Ready, the rollout times out, and # the symptom reads as a broken application rather than as a policy. # # Narrow it by listing the worker node IPs (e.g. ["10.0.1.11/32"]) - and mind # the companion trap: once this list is non-empty the rule IS restricted, so # everything else that needs the port has to be in it. The chart adds the own # namespace and allowedNamespaces to the same rule for exactly that reason. probeSources: []