Dockerfile check · medium

chmod 777 (world-writable files) in a Dockerfile

chmod 777 (or a+rwx) is the usual quick fix for permission errors after adding a non-root USER, but it makes the files writable by every user in the container. A compromised process can then rewrite your application code or config, which is a common way to persist a backdoor. Give the runtime user ownership of only the directory it needs to write, and leave the code read-only.

$ explain Dockerfile
! RUN makes files world-writable medium
line 6
fix: see below
$ 

What the free Dockerfile explainer reports on the risky example below.

Why it matters

The point of running as a non-root user is that the process can't modify the image's code. World-writable files remove that protection.

chmod -R 777 on an application directory usually covers far more than the one cache or upload folder that actually needed write access.

The explainer flags chmod with 777, 0777, a+rwx or ugo+rwx in a RUN step.

Risky and safer Dockerfile

RiskyDockerfile
FROM node:22.11-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY src ./src
RUN mkdir -p /app/uploads && chmod -R 777 /app
USER 10001
HEALTHCHECK CMD ["node", "src/healthcheck.js"]
CMD ["node", "src/server.js"]
SaferDockerfile
FROM node:22.11-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY src ./src
RUN mkdir -p /app/uploads && chown 10001:10001 /app/uploads
USER 10001
HEALTHCHECK CMD ["node", "src/healthcheck.js"]
CMD ["node", "src/server.js"]

How to fix it

  • Find the directories the app really writes to (uploads, cache, temp files).
  • Make the runtime user own just those: RUN mkdir -p /app/uploads && chown 10001:10001 /app/uploads.
  • Leave application code owned by root and not writable by the runtime user. For data that must survive restarts, mount a volume there instead.

When it's acceptable

  • Scratch directories that are already world-writable by design, such as /tmp, which uses the sticky bit (1777) so users can't delete each other's files. The rule matches 777 and 0777, not 1777.

Check your own Dockerfile

The risky example is loaded below. Paste your own file to check it for this and every other Dockerfile rule. It runs in your browser; nothing is uploaded.

Dockerfile

Parsed in your browser. Nothing is uploaded.

Explanation

1 stage9 instructions1 medium1 low

Stage 0node:22.11-slim

  • Docs for FROM (opens in a new tab)

    Starts from the base image node, tag 22.11-slim.

    A version tag can be re-pushed to different content; only a digest is immutable. This applies to every tagged FROM in the file.

    Safer: Pin the digest as well (image:tag@sha256:<digest>) and let a bot open pull requests when it changes.

  • Docs for WORKDIR (opens in a new tab)

    Sets the working directory to /app for later instructions and the container. It is created if it does not exist.

  • Docs for COPY (opens in a new tab)

    Copies package.json, package-lock.json from your build context to ./ in the image.

  • Docs for RUN (opens in a new tab)

    Runs npm ci --omit=dev while building the image and saves the result as a new layer.

  • Docs for COPY (opens in a new tab)

    Copies src from your build context to ./src in the image.

  • Docs for RUN (opens in a new tab)

    Runs mkdir -p /app/uploads && chmod -R 777 /app while building the image and saves the result as a new layer.

    A 777 permission lets any process in the container rewrite your application, a common way to plant a backdoor.

    Safer: Give ownership of the one directory that needs writes to the runtime user (chown) and leave the rest read-only.

  • Docs for USER (opens in a new tab)

    Runs later instructions and the container's process as user 10001.

  • Docs for HEALTHCHECK (opens in a new tab)

    Runs node src/healthcheck.js to check the container is healthy.

  • Docs for CMD (opens in a new tab)

    Sets the default command to node src/server.js. It runs when the container starts, and anything passed to docker run replaces it.

Questions

Is chmod 777 bad in a Dockerfile?

Yes, for application files. It lets any process in the container modify them, which undoes the benefit of running as a non-root user.

How do I fix permission denied after adding USER to a Dockerfile?

Before the USER line, chown only the directories the app writes to, e.g. chown 10001:10001 /app/uploads, or use COPY --chown for files the user must own.

Does the explainer catch chmod a+rwx too?

Yes. It flags 777, 0777, a+rwx and ugo+rwx in RUN steps.