Skip to content
Docker
Lab 11 of 11·40mChallengeIntermediate

Challenge: works on my machine

An image runs on your laptop and fails in a clean environment. Four faults, all of them things this track already showed you.

You need

  • Docker Engine 24+ on Linux (or a Linux VM)

Do first

Everything you need is in the ten labs before this. No new commands.

Set up the scenario

Run this exactly as written.

mkdir -p ~/labs/challenge-wonmm && cd ~/labs/challenge-wonmm
mkdir -p appdata
cat > app.sh <<'SH'
#!/bin/sh
STATE=/var/lib/app/state.log
CONFIG=/etc/app/app.conf
echo "reading $CONFIG"
VALUE=$(grep '^greeting=' "$CONFIG" | cut -d= -f2)
echo "starting with greeting=$VALUE"
while true; do
  echo "$(date -Is) $VALUE" >> "$STATE"
  sleep 2
done
SH
cat > app.conf <<'CONF'
greeting=hello
CONF
cat > Dockerfile <<'DOCKER'
FROM alpine:3.20
RUN adduser -S -u 10001 app
COPY app.sh /app.sh
COPY app.conf /etc/app/app.conf
USER app
CMD ["/app.sh"]
DOCKER
docker build -q -t wonmm . >/dev/null 2>&1 || docker build -t wonmm .
echo "built"

The situation

The developer says it works for them, because they run it like this:

docker run --rm --user 0:0 -v "$PWD/appdata:/var/lib/app" wonmm

Root, a bind mount, no limits. In the environment it has to run in, it is started like this — and this is the command you must make work:

docker run --rm \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=8m \
  --cap-drop=ALL \
  --security-opt=no-new-privileges \
  --memory=64m --memory-swap=64m \
  -v "$PWD/appdata:/var/lib/app" \
  wonmm

Your goal

Make the second command run the application successfully, writing lines to appdata/state.log on the host, and keep it running for at least thirty seconds.

Then state, one sentence each, what all four faults were.

Constraints

  • Do not add --user 0:0, and do not remove USER app from the Dockerfile. Running as root makes two faults vanish and is the thing being avoided.
  • Do not remove --read-only, --cap-drop=ALL, or the memory limit from the run command. Those are the target environment, not your choice.
  • Do not chmod 777 anything.
  • Two faults are in the Dockerfile. One is on the host. One is in how the container is invoked.

They surface one at a time

Fix one, and the next appears. Re-read the output after every change rather than making four at once — that is the actual skill being tested.

Hints, in increasing order of spoiler

  • Start with the routine: docker ps -a for the exit code, then docker logs. The exit code names the first fault before you read anything. Compare it to the table in lab 22.
  • COPY preserves the host file's mode, and a freshly created file has no execute bit. Lab 22, failure two.
  • The app writes to /var/lib/app, which is a bind mount. Lab 11 was entirely about who owns a bind-mounted directory and what happens when the container's uid does not match. What uid does USER app resolve to, and who owns ./appdata on the host?
  • --read-only means the rootfs is immutable. Read app.sh again and ask what it needs to write, and where. One of those two paths is not covered by the --tmpfs or the volume — and the fix is a flag on the run command, not a change to the app.
  • Fault four is subtle and specific: look at the COPY app.conf /etc/app/app.conf line and at what USER app can read. Then check the file's mode on the host with ls -l app.conf.

What success looks like

Verify

docker run -d --name wonmm-ok --read-only \ --tmpfs /tmp:rw,noexec,nosuid,size=8m --cap-drop=ALL \ --security-opt=no-new-privileges --memory=64m --memory-swap=64m \ -v "$PWD/appdata:/var/lib/app" wonmm >/dev/null sleep 12 docker inspect wonmm-ok --format 'status={{.State.Status}} exit={{.State.ExitCode}}' # status=running exit=0

Verify

wc -l < appdata/state.log # 5 or more lines, written by uid 10001 through the read-only container

Verify

docker logs wonmm-ok | head -n 2 # reading /etc/app/app.conf # starting with greeting=hello

Clean up

docker rm -f wonmm-ok 2>/dev/null
docker image rm wonmm 2>/dev/null
cd ~ && sudo rm -rf ~/labs/challenge-wonmm

Where this goes next

That is the Docker track. The CI/CD track builds these images in a pipeline; Observability instruments the service inside one; Cloud Engineering runs them somewhere other than your laptop.