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.
This commit is contained in:
kevinveenbirkenbach committed 2026-09-08 16:49:05 +02:00
1 parent 13f5ca6d95
commit f3eba39cae
2 files changed
+4 -5

No files matched your search

-1
View File
@@ -12,4 +12,3 @@ site_tmp:
- service: checkmk
target: "/omd/sites/{{ lookup('config', 'web-app-checkmk', 'services.checkmk.site') }}/tmp"
size: 512m
mode: "01777"
+4 -4
View File
@@ -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")