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.

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.