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" wonmmRoot, 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" \
wonmmYour 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 removeUSER appfrom 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 777anything. - 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 -afor the exit code, thendocker logs. The exit code names the first fault before you read anything. Compare it to the table in lab 22. COPYpreserves 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 doesUSER appresolve to, and who owns./appdataon the host? --read-onlymeans the rootfs is immutable. Readapp.shagain and ask what it needs to write, and where. One of those two paths is not covered by the--tmpfsor 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.confline and at whatUSER appcan read. Then check the file's mode on the host withls -l app.conf.
What success looks like
Verify
Verify
Verify
Clean up
docker rm -f wonmm-ok 2>/dev/null
docker image rm wonmm 2>/dev/null
cd ~ && sudo rm -rf ~/labs/challenge-wonmmWhere 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.