kadet-hub logokadet-hub

Home Assistant on Kubernetes with Kapitan

Home Assistant Core, the home automation server, with its configuration and database on a claim, designed in kadet-hub on the project’s image and rendered by the kadet-hub kubernetes generator 1.14.0.

What it deploys

Required parameters

No parameter has to be set.

Include

A target includes the recipe and its options as classes and can override anything the recipe sets:

classes:
  - recipes.home-assistant
parameters:
  namespace: home-assistant
  kapitan:
    vars:
      target: home-assistant

Fetching it

The quickstart goes through these steps on a kind cluster with the uptime-kuma recipe, from installing Kapitan to the running app.

A separate fetch inventory pins the recipe by version and digest. This is fetch/targets/recipes.yml for version 1.0.0:

parameters:
  kapitan:
    dependencies:
      - type: oci
        source: ghcr.io/kadet-hub/recipes/home-assistant:1.0.0@sha256:88a2ac9aecaf2097e6fb446aede66c2ccf56df0da16bcff82a00f215671b00f4
        output_path: inventory/classes/recipes/home-assistant

The first command fetches the recipe into inventory/classes/recipes/home-assistant, the second the generators and compiles:

kapitan compile --inventory-path fetch --fetch --output-path .
kapitan compile --fetch

The first command exits with status 1 because the fetch target compiles nothing. Kapitan's --fetch checks neither the digest nor the attestation. This verifies the attestation of the recipe:

gh attestation verify \
  oci://ghcr.io/kadet-hub/recipes/home-assistant@sha256:88a2ac9aecaf2097e6fb446aede66c2ccf56df0da16bcff82a00f215671b00f4 \
  --owner kadet-hub \
  --cert-identity https://github.com/kadet-hub/registry/.github/workflows/release.yml@refs/heads/main

The attested index must also list each digest under its recipe with the pinned version, and not as yanked. Verifying by hand shows how to read it.

Settings

Setting Inventory
Home Assistant version components.home-assistant.image, tag and digest
time zone of the container’s processes, UTC components.home-assistant.env.TZ, a tz database name such as Europe/Amsterdam
size of the claim config, 1Gi components.home-assistant.volume_claims.config.spec.resources.requests.storage
exposure components.home-assistant.service.type, or a route or Ingress of the target to the Service home-assistant on port 8123
resources components.home-assistant.resources

Home Assistant keeps its state in /config on the claim: configuration.yaml, the users and tokens in .storage, and the recorder’s database home-assistant_v2.db. The database grows with the number of entities and the days of history the recorder keeps, ten by default. Home Assistant’s own time zone is set in the onboarding and stored in .storage, apart from TZ.

The recipe creates no account (DEC-133). The first visitor of the web UI creates the owner in the onboarding, without a login. Create the owner before the target exposes the Service. Until then only the cluster reaches it.

The pod has no probe, because neither the image nor its documentation names a check. It is ready once the container runs, before Home Assistant listens on port 8123. Its termination grace period is 60 seconds, the stop timeout of the container guide, so that Home Assistant closes its database. Until Home Assistant has started, a SIGTERM ends it before it writes the changes it holds back, such as a new token. A preStop hook holds the stop until Home Assistant catches SIGTERM (DEC-333).

Behind a route or Ingress, Home Assistant refuses forwarded requests until its http integration names the proxy’s addresses in trusted_proxies with use_x_forwarded_for: true in configuration.yaml.

Left out

Without the option host-network, Home Assistant has no multicast or broadcast of the LAN. These do not work:

With or without the option, the recipe renders no privileged, no device and no host path. That leaves out:

Home Assistant Container has no apps (add-ons), with or without the recipe.

Proof

check home-assistant --level deploys with tests/target.yml, on kind for each Kubernetes minor CI tests. The image has no Kubernetes objects to compare with, so the recipe is proven by deploying it (RCP-44).

tests/smoke checks that the pod passes Pod Security baseline. It then starts a pod of Home Assistant’s image, which carries curl. From there, through the Service home-assistant on port 8123, it

The option host-network is proven by its render only: compiled with tests/target.yml, it changes nothing but hostNetwork and dnsPolicy, and check home-assistant --level renders passes with it. CI deploys the recipe without it.

Not tested: the web UI in a browser, an integration, a device, and a route or Ingress in front of the Service.

Versions

The recipe's files are in kadet-hub/recipes. All recipes