World map

 

BM: Baca dalam Bahasa Melayu

World D2: Recipes

Run as a non-root USER

You will learn: to run your app as a normal user, so a break-in can do less.

Checked with Docker Engine 29 on 31 Oct 2026

The problem

whoami inside the api image prints root. Docker transcript: $ docker run --rm api:1.0 whoami root

Your laptop~/api

  • No open hatches

Lunchboxes (containers)

  • No containers yet

Frozen packs (images)

  • api:1.06 layers · 244MB
  • node:24-alpine4 layers · 244MB

0 containers (0 running), 2 images, 0 lockers.

Your API runs as root inside the box. A bug lets a stranger in. Now the stranger is root too, with every key in the kitchen.

Watch

Tap the line that picks who runs the app.

0 of 1 found

A non-root user: A normal user with few rights. If someone breaks in, they can do less. has few rights, like a kitchen helper. USER node switches to one. The node images already have a user called node.

Try it

Add USER node to the end of the recipe. Build api:1.1. Then ask whoami.

Your laptop~/api

  • No open hatches

Lunchboxes (containers)

  • No containers yet

Frozen packs (images)

  • api:1.06 layers · 244MB
  • node:24-alpine4 layers · 244MB

0 containers (0 running), 2 images, 0 lockers.

Predict

A recipe says USER node, and after it RUN apk add --no-cache curl. What happens?

Pick one answer.

Check

The oops recipe sets USER node before RUN apk add. The build fails: ERROR: Unable to open log: Permission denied, exit code 99. Docker transcript: $ cat oops/Dockerfile FROM node:24-alpine USER node RUN apk add --no-cache curl $ docker build -t api:oops oops [+] Building 0.1s (5/5) FINISHED docker:default => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 95B 0.0s => [internal] load metadata for docker.io/library/node:24-alpine 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [1/2] FROM docker.io/library/node:24-alpine@sha256:8a32c8cf649a49915a6 0.0s => => resolve docker.io/library/node:24-alpine@sha256:8a32c8cf649a49915a6 0.0s => ERROR [2/2] RUN apk add --no-cache curl 0.1s ------ > [2/2] RUN apk add --no-cache curl: 0.215 ERROR: Unable to open log: Permission denied ------ Dockerfile:3 -------------------- 1 | FROM node:24-alpine 2 | USER node 3 | >>> RUN apk add --no-cache curl 4 | -------------------- ERROR: failed to build: failed to solve: process "/bin/sh -c apk add --no-cache curl" did not complete successfully: exit code: 99

Your laptop~/api

  • No open hatches

Lunchboxes (containers)

  • No containers yet

Frozen packs (images)

  • api:1.06 layers · 244MB
  • node:24-alpine4 layers · 244MB

0 containers (0 running), 2 images, 0 lockers.

Real error: Permission denied, exit code 99. Plain words: only root may install. Install first, then switch with USER.

Check

Good habit? Swipe right for yes, left for “needs work”.

Swipe right for good, left for needs work. Or use the buttons.

1 / 4

Install packages first, then add USER near the end

Install packages first, then add USER near the end

You did it!

You can now run your app as a non-root user.

Go deeper (optional)

docker run -u 1000 picks a user for one run. A recipe USER is the safe default. Must the app write a folder? Give it to the user: COPY --chown=node:node . .. In World D5 you add more locks.

Real mission

  1. docker run --rm hello:1.0 whoami

0 of 3 steps done · Your ticks are saved.