Skip to content

Build1 publisher3 min readPublished

Docker 29.8 extends the container umask to the execs the entrypoint trick never reached

Engine 29.8.0's --umask flag sets one mask for the main process, execs and healthchecks. A side-by-side test on a second daemon also shows it accepting a four-digit value and applying only the last three.

The Engineer · Build desk

Illustration accompanying Docker 29.8 extends the container umask to the execs the entrypoint trick never reached

What happened

  • Docker Engine 29.8.0, released 3 September 2026, adds a --umask flag to docker create and docker run plus a HostConfig.Umask field, documented to cover the main process, execs and healthchecks.
  • In a dev.to test, an entrypoint setting umask 027 produced a 640 file from the main process, while a file touched through docker exec in the same container came out 644.
  • The daemon truncated silently: it accepted --umask 1777 without complaint and applied 0777, and 2027 and 4027 both landed as 0027, with no client error and no daemon log entry.
  • Non-numeric values are refused before the container is created, with strconv.ParseUint errors for 099, for -1, for symbolic notation such as u=rwx, and for an empty string.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision A team whose file permission policy lives in an entrypoint umask now picks between upgrading the engine and leaving every exec and healthcheck writing at the runtime default.
  • exposure Files that deploy scripts and monitoring sidecars wrote through docker exec have been readable by other accounts in the container under a policy that forbade it.
  • constraint A configuration pipeline that copies a umask value carrying a special-bit prefix will set a mask Docker never applied, and nothing in the logs or the client output flags the difference in review.
  • cost The fix lives in the daemon, not the image, so it is paid for by whoever owns the hosts: an engine upgrade past 29.3.1 everywhere the containers run.

Exec escapes the entrypoint script because of how processes inherit the mask. `umask 027` in an entrypoint sets the mask on PID 1, and PID 1's children inherit it [4]. A process started by `docker exec` is attached by the runtime straight into the container's namespaces, so it is not a child of PID 1 and inherits nothing from it. It begins with the runtime's own default, which on the test host was `022` [4].

One bit separates the two answers. 0644 minus 0640 is 0004, the read bit for other, so files written through exec came out readable by every account in the container while the entrypoint's stated policy was 640 [1].

The main process was correct under both approaches, which is why this is easy to miss: the person who writes the entrypoint script tests the process running in the foreground [12]. According to the dev.to writeup, exec and the healthcheck are the two that quietly get the wrong permissions, and the usual way to find out is a deploy script or a monitoring sidecar failing to read a file it should have had access to [12].

The flag itself behaves like a umask. Alpine's unmasked default of `022` gives 644 files and 755 directories, `--umask 027` gives 640 and 750, and `--umask 000` gives 666 [6].

The four-digit case is a mask down to the low three octal digits: 1777 lands as 0777, and 2027 and 4027 both land as 0027 [3]. A 0666 creation mode under a 0777 mask leaves no permission bits at all, which is the `0` that `stat` printed [4]. Docker's own help text for it says "Set umask for the container" [10].

The comparison ran on one machine, twice. The installed engine was 29.3.1, whose `docker run --help` has no `--umask` line, so the 29.8.1 static build from download.docker.com was started as a second `containerd` and `dockerd` on their own socket and data directory, with a second client pointed at it [5]. Running a second daemon avoids touching the one everything else depends on.

The healthcheck figure was taken across two consecutive checks at `--health-interval 2s`, and both gave 644 under the entrypoint script and 640 under the flag [7]. The first attempt used `--health-retries 1` with no `--health-start-period` and read the first health log entry as the result [11].

Whether the 644 transfers to your hosts depends on your runtime's default, and 022 is what this daemon started exec processes with [4]. Docker documents the flag as covering "a container's main process, execs, and healthchecks" without stating a range for the value [2][10]. If your entrypoint sets a mask your runtime already uses, the upgrade changes nothing you can observe. If it sets 027 or 077, everything exec and the healthcheck write today is looser than the script says [3][7].

What to watch

  • Whether Docker documents a range for --umask or starts rejecting four-digit values with setuid, setgid or sticky prefixes instead of truncating them.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories