Docker Compose check · medium

user: root in Docker Compose

user: root (or 0, 0:0, root:root) makes the service's process run as UID 0. Unless Docker uses user namespaces or runs rootless, that is the same UID as root on the host, limited only by capabilities and security profiles. The explainer flags an explicit root user as medium. It does not flag a missing user key, where the image's own USER applies.

$ explain docker-compose.yml
! app runs as root medium
line 4
fix: see below
$ 

What the free Docker Compose explainer reports on the risky example below.

Why it matters

If the application is compromised, the attacker gets the process's user. As root they can modify any file in the container, install tools, and write to any mounted volume regardless of its file permissions.

Root is also what most container escape techniques start from. Each other loosened setting, such as an added capability or a sensitive mount, is more dangerous when the process is root.

An explicit `user: root` often overrides an image that was built to run as a non-root user, undoing its maintainers' choice.

Risky and safer docker-compose.yml

Riskydocker-compose.yml
services:
  app:
    image: example/app:1.4.2
    user: root
    restart: unless-stopped
    mem_limit: 512m
Saferdocker-compose.yml
services:
  app:
    image: example/app:1.4.2
    user: "1000:1000"
    restart: unless-stopped
    mem_limit: 512m

How to fix it

  • Set user to an unprivileged UID:GID, e.g. "1000:1000", or remove it if the image already declares a non-root USER.
  • Make sure that user owns the volumes and directories the service writes to, for example with a one-time chown on the host.
  • If the service needs root only to bind a port below 1024, listen on a higher port and map it with ports, or add just NET_BIND_SERVICE.

When it's acceptable

  • Images whose entrypoint starts as root only to fix permissions and then drops to an unprivileged user, as several official database images do. Check the image's documentation before overriding it.
  • Hosts that run Docker rootless or with user namespace remapping, where container root maps to an unprivileged host user.

Check your own docker-compose.yml

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

docker-compose.yml

Parsed in your browser. Nothing is uploaded.

Explanation

1 service0 networks0 volumes1 medium

Top level

appexample/app:1.4.2

  • Docs for image (opens in a new tab)

    Runs the image example/app:1.4.2.

  • Docs for user (opens in a new tab)

    Runs as user root, which is root inside the container.

    If the app is compromised, the attacker has root inside the container, which makes escapes and file tampering easier.

    Safer: Set user to an unprivileged UID:GID that owns the files the service needs.

  • Docs for restart (opens in a new tab)

    Restarts if it stops or after a daemon restart (unless-stopped), but stays down if you stopped it manually.

  • Docs for mem_limit (opens in a new tab)

    Limits memory to 512m. The container is killed if it exceeds this.

Questions

Is root in a Docker container the same as root on the host?

By default it is the same UID 0, limited by Docker's reduced capabilities, seccomp and AppArmor. With rootless Docker or user namespace remapping, container root maps to an unprivileged host user.

How do I run a docker-compose service as a non-root user?

Add user: "1000:1000" (any unprivileged UID:GID) to the service and make sure that user can write the paths it needs.

Does the explainer flag services with no user key?

No. Without a user key the image's USER applies, which the compose file alone cannot show. It flags only an explicit root, 0, root:… or 0:… value.