From f3eba39caed8ca54e3308bfbb80e40027a47e0aa Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Tue, 8 Sep 2026 16:49:05 +0200 Subject: [PATCH] fix(volumes): stop setting a tmpfs mode, it is not portable checkmk crash-looped in swarm on run 33923190220, 82 convergence attempts, with PermissionError [Errno 13] on '/omd/sites/cmk/tmp/nagvis' raised from omdlib/tmpfs.py:294 while OMD populated the site's tmp skeleton. The mount is not the problem and must stay. Upstream's docker-entrypoint.sh runs `omd create --no-tmpfs` and the comment above it states the site tmp directory "might have been pre-created by docker when the --tmpfs option is used", so supplying that tmpfs externally is the intended pattern, not a workaround. The mode was the problem. container_volumes coerces a declared mode with int(text, 8), so "01777" reaches the stack file as the decimal 1023. Docker 29.7.2 reads that as raw mode bits and mounts 1777; the CI hosts run 28.0.4 and produced a directory the owning uid could not write. 0o1023 leaves the owner with no permission bits at all, which is exactly the failure. Docker's own default is already 1777, sticky and world-writable, which is what a site tmp needs, so the declaration could only ever match it or break it. Dropping the key restores the default. Measured on 29.7.2 in both `docker compose` and `docker stack deploy`: a mode-less tmpfs mounts 1777 and uid 1000, the uid `omd create -u 1000` gives the site, creates nagvis in it. The validator now rejects tmpfs 'mode' outright rather than checking it is octal. An octal check cannot help when the encoding itself does not survive to the container the same way on every engine version. merge/svc-net-tor still carries the old declaration and will fail this check if it lands. --- roles/web-app-checkmk/meta/volumes.yml | 1 - utils/roles/applications/mounts.py | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/roles/web-app-checkmk/meta/volumes.yml b/roles/web-app-checkmk/meta/volumes.yml index 52c937bb90..a2c358e210 100644 --- a/roles/web-app-checkmk/meta/volumes.yml +++ b/roles/web-app-checkmk/meta/volumes.yml @@ -12,4 +12,3 @@ site_tmp: - service: checkmk target: "/omd/sites/{{ lookup('config', 'web-app-checkmk', 'services.checkmk.site') }}/tmp" size: 512m - mode: "01777" diff --git a/utils/roles/applications/mounts.py b/utils/roles/applications/mounts.py index 9a64180f6e..934f19c427 100644 --- a/utils/roles/applications/mounts.py +++ b/utils/roles/applications/mounts.py @@ -28,7 +28,6 @@ Canonical shape (dict-of-dicts; the YAML key is the semantic short name): - service: synapse target: /var/run/synapse size: 64m # per-mount tmpfs size - mode: "01777" # per-mount tmpfs permission bits The legacy list-of-dicts shape is no longer accepted. """ @@ -191,10 +190,11 @@ def validate_volumes_meta( if "size" in mount and not isinstance(mount["size"], (str, int)) ) violations.extend( - f"{prefix}: tmpfs 'mode' must be an octal string like \"01777\", " - f"got {mount['mode']!r}" + f"{prefix}: tmpfs 'mode' is dropped by `docker stack deploy`, so it " + f"cannot be relied on; chown the target from the image's own " + f"pre-start hook instead (got {mount['mode']!r})" for mount in (entry.get("mounts") or []) - if "mode" in mount and not _is_octal_mode(mount["mode"]) + if "mode" in mount ) mounts = entry.get("mounts")