diff --git a/cookbooks/zwave-js-ui/README.md b/cookbooks/zwave-js-ui/README.md index 2cc9904..763c72a 100644 --- a/cookbooks/zwave-js-ui/README.md +++ b/cookbooks/zwave-js-ui/README.md @@ -27,7 +27,8 @@ This cookbook: - Supports automatic container image updates via Podman auto-update. The image is `ghcr.io/zwave-js/zwave-js-ui:11`, a multi-architecture manifest (amd64 + -arm64, verified 2026-09-26, version 11.24.1). +arm64, verified 2026-09-26, version 11.24.1). It is named in the container unit rather than in +a `.image` unit — see the deviations below, and the reason is boot resilience, not taste. ## What this cookbook does NOT do @@ -198,6 +199,17 @@ a restart loop fixes. artifact. What the external file buys is not secrecy: it is that the authoritative copy lives outside the application, is restorable from a vault, and cannot be edited from the web interface. +- **No `.image` unit: the image is named in the container.** A `.image` unit runs + `podman image pull`, which contacts the registry even when the image is already in local + storage. A machine that boots before its network does — a power cut, a switch slower than the + machine, a cable plugged in after power-on — fails that pull with + `Temporary failure in name resolution`, and the `Requires=` that quadlet generates on the + `.image` unit takes the container down with it. **A failed dependency is never retried**: + `Restart=always` only ever applies to a unit that started at least once, so the service stays + down until someone notices. Naming the image in the container instead lets podman resolve it + at start with its default `--pull=missing`: the local image starts with no registry involved, + and a genuinely missing image fails the container, which `Restart=` then retries until the + network is there. Auto-update is unaffected — it reads the container's label. - **Only `external-settings.json` is mounted into the container, never the configuration directory.** `:Z` relabels what it mounts to `container_file_t`, recursively and on disk, and `init.sh` lives in that directory. Mounting the directory therefore leaves a script systemd diff --git a/cookbooks/zwave-js-ui/zwave-js-ui.container b/cookbooks/zwave-js-ui/zwave-js-ui.container index 38e5e26..5879a0e 100644 --- a/cookbooks/zwave-js-ui/zwave-js-ui.container +++ b/cookbooks/zwave-js-ui/zwave-js-ui.container @@ -21,7 +21,21 @@ PartOf=zwave-js-ui.target [Container] ContainerName=zwave-js-ui -Image=zwave-js-ui.image +# The image reference, and NOT a zwave-js-ui.image unit -- the one deviation from +# the convention of this repository, for a reason this cookbook has already paid +# for. A .image unit runs `podman image pull`, which contacts the registry even +# when the image is already in local storage. A radio head that boots before its +# network does -- a power cut, a switch that is slower than it is, a cable +# plugged in after the machine -- therefore fails that pull, and the generated +# `Requires=` on it takes the container down with it. A failed dependency is +# never retried: Restart=always only ever applies to a unit that started once. +# +# Named here instead, podman resolves the image at container start with its +# default --pull=missing: the local image starts with no registry involved, and a +# genuinely missing image fails the container, which Restart= below then retries +# until the network is there. Auto-update still works -- it reads the label of +# the container, not the .image unit. +Image=ghcr.io/zwave-js/zwave-js-ui:11 AutoUpdate=registry # No need for root privileges: the image has no entrypoint script, it execs node diff --git a/cookbooks/zwave-js-ui/zwave-js-ui.image b/cookbooks/zwave-js-ui/zwave-js-ui.image deleted file mode 100644 index 0e57b7c..0000000 --- a/cookbooks/zwave-js-ui/zwave-js-ui.image +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=podman pull ghcr.io/zwave-js/zwave-js-ui -Documentation=https://zwave-js.github.io/zwave-js-ui/ - -[Image] -# Multi-architecture manifest (amd64 + arm64, verified 2026-09-26 on tag 11 -> -# 11.24.1), so the cookbook installs on an aarch64 host as well as on the x86_64 -# radio head. The tag is the major version: Z-Wave JS UI ships the zwave-js -# driver and its Z-Wave JS Server, whose schema version is what Home Assistant -# negotiates. A major bump is the one that can break that handshake, so it is -# the one an operator has to opt into. -Image=ghcr.io/zwave-js/zwave-js-ui:11