DEV Community

MilkyWay008
MilkyWay008

Posted on

Docker EACCES after an image upgrade: check the container's uid against the mount

A container that worked yesterday doesn't start today. The image tag moved by one patch version, nothing on your side changed, and the logs end with something like:

EACCES: permission denied, mkdir '/root/.flowise'
Enter fullscreen mode Exit fullscreen mode

Or you get the friendlier version. The web UI comes up, the API answers 502, and further up the log there's a Python PermissionError from the entrypoint plus sqlite3.OperationalError: unable to open database file.

Same failure, different outfit. The image changed which user it runs as (or which HOME it uses, or which subfolder of the mounted directory it writes into), and the directory on your host is still owned by somebody else. I've run into this class a few times now...... it's worth knowing the 2-minute diagnosis instead of rolling the tag back and hoping. Rolling back does work, by the way, and it's a fine stopgap. It just comes back on the next upgrade.

Why the uid matters

A bind mount is your host directory, handed to the container as is. No translation layer, no magic.

When the container process runs as uid X and tries to mkdir inside a directory owned by uid Y, the kernel does the normal thing: it checks X against the owner, group and "other" bits of that host inode. Not the owner, not in the group, and no write bit for others, and you get EACCES. That's the entire bug.

Two details that bite people.

Root inside the container is a special case. Linux lets uid 0 bypass a lot of permission checks through capabilities like CAP_DAC_OVERRIDE, so running a container as root often hides this problem instead of fixing it. Then a new image switches to a non-root USER, or you move to rootless Docker or Podman where container root maps to a host subuid, and the bypass that was covering for you is gone.

A mode 700 directory is the ugly case. Only the host owner has any access at all, so every other uid in the container is locked out completely and you get a hard crash instead of a partial one.

Why does an upgrade trigger it? Because the new image changed USER (root to 1000, or 1000 to 10001), changed HOME, or started writing to a path inside the mount that the old image never touched. Nothing on your side moved.

Diagnose it in a couple of minutes

All of these are read-only, safe on a running box.

docker logs <container> --tail 50
Enter fullscreen mode Exit fullscreen mode

Look for the failing path, not just the error. That path is the directory that has to be writable.

docker inspect --format '{{.Config.User}}' flowiseai/flowise:3.1.4
Enter fullscreen mode Exit fullscreen mode

Empty output means the image runs as root. 1000 or 10001:10001 means it doesn't. Run the same command against the old tag and compare. This is usually the moment it clicks.

docker run --rm --entrypoint sh flowiseai/flowise:3.1.4 -c 'id; echo $HOME; ls -ln /root'
Enter fullscreen mode Exit fullscreen mode

That prints the uid/gid, the HOME the image sets, and who owns the target directory. Worth knowing an image can set USER and forget HOME; then tools write somewhere odd and the error points at the wrong place.

Then the host side:

id -u
ls -ln /path/to/host/mount
Enter fullscreen mode Exit fullscreen mode

If the image runs as 1000 and the mount is owned by 1000, permissions are not your problem. Go look at SELinux (more below) or at the app's own config. If the numbers differ, you found it.

The fix ladder

1st, and usually right: make the host directory owned by the uid the container runs as.

sudo chown -R 1000:1000 /path/to/host/mount
Enter fullscreen mode Exit fullscreen mode

Works everywhere, takes seconds. The catch is you've now hardcoded one uid, so the next image that changes its uid breaks you again. Which is the trap you're standing in right now.

2nd, and more durable: use a named volume instead of a bind mount.

docker volume create flowise_data
Enter fullscreen mode Exit fullscreen mode
# docker-compose.yml
services:
  flowise:
    volumes:
      - flowise_data:/root/.flowise
Enter fullscreen mode Exit fullscreen mode

When Docker first creates a named volume and the image has content at that path, it copies the image's own directory into the volume, ownership included. So the volume starts out owned by whatever uid the image expects, and the container can write to it. The costs: your data isn't at a host path you can scp from, and if the volume already exists with a different uid you may have to remove and recreate it (back it up first, please).

3rd: override the user, if you want the container to match your host user.

services:
  flowise:
    user: "1000:1000"
Enter fullscreen mode Exit fullscreen mode

Handy for local dev where you want files to land as you. It backfires when the image's own files belong to a different uid (a home directory owned by node is still not writable by uid 1000), and on Docker Desktop the uid model isn't your host's anyway.

4th: PUID/PGID, when the image implements it. linuxserver-style images read PUID and PGID from the environment and chown their data directories at startup. Only works where it's supported, so check the docs for the image you're actually running.

5th: pin the previous tag. image: flowiseai/flowise:3.1.3. Two minutes of work, unblocks you, fixes nothing, and shows up again on the next upgrade. Fine as a stopgap while an upstream issue gets attention. Not a plan.

Gotchas that will cost you an afternoon

SELinux on Fedora and RHEL. If EACCES survives a correct chown, the directory is missing the container file label. Add :z or :Z to the mount. A correct chown that changes nothing is the SELinux tell.

Docker Desktop on Windows and macOS. Containers there run in a Linux VM with its own uid space, so everything above is for Linux hosts. Desktop layers its own file-sharing quirks on top.

It isn't always the mount root that needs fixing. If the app writes to $HOME/.appname, that subdirectory is the one that has to be writable, and it may be the one that was just created and failed.

chown -R on a big or shared directory is slow, and it re-owns data belonging to other users and apps without asking. On a 200 GB media mount, don't.

One honest caveat

I went and read two of these threads. The one with a proper A/B proof is an agent-canvas image running as uid 10001 against a 700 mount, which booted fine at 777...... that one nails the mechanism. The other, Flowise 3.1.4 failing on mkdir '/root/.flowise', has the exact symptom and a confirmed "revert to 3.1.3 fixes it", but the issue itself has zero comments, so the uid theory there is plausible rather than confirmed. If chown doesn't do it for you on that build, you're probably looking at a different startup bug wearing the same error message.

Either way: next time a container throws EACCES after an upgrade, run the inspect commands before you touch anything. Comparing {{.Config.User}} between two tags takes ten seconds, and it tells you whether you have a permissions problem at all.

Top comments (0)