What Your Container Runs As
Your container runs as root, with a read-only root filesystem, and with most Linux capabilities dropped. That last one is the surprising part: your container runs as root, but root is not root for file permissions here.
Reproduce it locally
If your image works on your machine but fails on HostStack, run it with these flags first. This is the sandbox, in one command:
docker run --rm -it \
--read-only --tmpfs /tmp --tmpfs /var/tmp \
--cap-drop ALL \
--cap-add CHOWN --cap-add SETGID --cap-add SETUID --cap-add NET_BIND_SERVICE \
--security-opt no-new-privileges \
-e PORT=3000 -p 3000:3000 \
your-imageAnything that fails under that command will fail on HostStack, and anything that passes will run. Debugging it locally is far faster than debugging it a deploy at a time.
The capability set
All capabilities are dropped, then four are added back — enough for the standard entrypoint patterns (a chown of the app dir, a gosu/su-exec privilege drop, binding :80/:443):
granted: CHOWN SETGID SETUID NET_BIND_SERVICE
dropped: everything else — notably DAC_OVERRIDE and FOWNERDAC_OVERRIDE is what normally lets root ignore file permission bits, and FOWNER is what lets root chmod a file it doesn't own. Without them, root can only write where it owns the directory and can only chmod files it owns. id -u still prints 0, which is why this is so easy to misread.
Errors this produces
None of these messages mention capabilities. All of them are the missing DAC_OVERRIDE/FOWNER:
- A
chownsucceeds and thechmodon the very next line fails withOperation not permitted— underset -eyour entrypoint dies one line after doing the work correctly. file_put_contents(): Failed to open stream: Permission deniedon a file the same script just wrote.cp -ainto a directory the script itself created, denied.tailon the process's own log file, denied.- Writes anywhere outside
/tmp,/var/tmp, and your mounted volumes failing withRead-only file system.
The fix is almost always the same: create files as the user that will read them rather than fixing up permissions afterwards, and write only to /tmp or a persistent volume.
Where you can write
/tmpand/var/tmp— tmpfs, writable,noexec, and wiped on every restart. Free; no provisioning needed.- Any persistent volume you attach — writable and durable across restarts and deploys.
- Nothing else. The rest of the filesystem is read-only, including your application directory.
Other limits
no-new-privileges setuid binaries cannot escalate
seccomp a restricted syscall profile is applied
pids limit capped per container
nofile / nproc ulimits are capped per container
network your service reaches the internet and its own
project's services; inbound arrives via the edge onlyBinding the port
Bind the port in $PORT, on 0.0.0.0 — not 127.0.0.1. A container listening only on loopback is unreachable from the edge and will fail its readiness probe with the container never answered. See Health Checks.