Shrink the Image
A tiny Flask app ships in a container image of more than 1 GB that runs as root. Rewrite the Dockerfile so the image is under 200 MB, runs as a normal user and still works.
The situation
The slug service is tiny: one Flask file (app.py) and three dependencies. It turns any text into a URL-friendly slug, so GET /slugify?text=Hello%20World answers {"slug": "hello-world"}.
Its container image is over 1 GB. Every deploy pulls it again, the registry bill keeps growing, and the security scanner lists hundreds of packages the app never uses. It also runs as root. Nobody has touched the Dockerfile since the first prototype.
Your task
- The image builds.
- The image is at most 200 MB.
- The container runs as a user that is not root.
- It starts and answers GET /health on port 8000 within 20 seconds.
- It still runs the real app with gunicorn. Leave app.py and the requirements files as they are.
What the grader checks
| Check | Passes when |
|---|---|
| The image builds | docker build finishes within 10 minutes on a GitHub-hosted ubuntu-24.04 runner, with Docker's official Dockerfile frontend, pinned by the grader (no # syntax= line). |
| Under 200 MB | The image is at most 200 MB (200,000,000 bytes), as docker image inspect reports it. python:3.13-slim with the app's dependencies is about 125 MB; the full python:3.13 base alone is over 1 GB. |
| Runs as a non-root user | Docker starts the image as a UID other than 0 (the grader checks with its own static id binary, so a name that maps to UID 0 does not count), and no process in the running container has UID 0. |
| Starts and answers /health | The container, started with no extra arguments, answers GET /health on port 8000 with HTTP 200 within 20 seconds. |
| The app still works | The real app answers: GET /slugify returns the same answer as app.py for five random texts in Latin, Greek and Cyrillic letters, the error answers are Flask's, and gunicorn serves it, with no network access beyond the grader. |
When a check fails, the grader's comment says what it saw, not how to fix it.
Files you may change
challenges/shrink-the-image/Dockerfilechallenges/shrink-the-image/.dockerignore
A pull request that changes any other file, including the tests or the workflows, is not graded.
Hints
Where do the gigabytes come from?
Run docker history on the current image. Compare the size of the first layers with the layers your own instructions add.
What does the app need at run time?
Python and the packages in requirements.txt. Not a compiler, git, vim, the dev requirements, the tests or pip's download cache.
Who runs the process?
A container runs as root unless the image says otherwise. Create a user in the image and switch to it with USER before CMD.
Test it locally
From the challenge folder, with Docker installed:
docker build -t shrink-the-image .
docker image ls shrink-the-image
docker run --rm -d --name shrink -p 8000:8000 shrink-the-image
for i in $(seq 20); do curl -fs localhost:8000/health && break; sleep 1; done
curl -i localhost:8000/health
curl --get --data-urlencode 'text=Shrink This Image' localhost:8000/slugify
docker top shrink -eo uid,cmd
docker rm -f shrinkSubmit
- Fork The-DevOps-Daily/challenges and create a branch.
- Commit your changes to the files listed above, and nothing else.
- Open a pull request against main in The-DevOps-Daily/challenges.
- Read the grader's comment. If a check fails, push another commit to the same branch and it grades again.
- When every check passes, the PR gets the passed label and is closed. It is never merged, and your pass is usually counted on this page within a few minutes.
The workflow takes the tests and the grader from the repository's main branch and copies only the files above out of your PR. Your code runs in a container or a throwaway kind cluster, and some inputs are random at grading time. A second workflow checks the result against your PR's real commit before it comments.
Learn the basics first
Exercise
Docker Multi-Stage Build Optimization
Learn to create efficient Docker images using multi-stage builds to reduce image size and improve security.
60 minutes
Exercise
Running Docker Containers on Your Linux Server
Install Docker and Docker Compose on Ubuntu, run your first container, deploy a WordPress stack with docker-compose, and set up Nginx as a reverse proxy in front of your containers.
60 minutes
Guide
Introduction to Docker
Learn Docker step by step with Engine 29: install it, run containers, write Dockerfiles, use volumes, networks and Compose, then deploy to production.
More challenges
Bash · Starter
Script It: Summarize an Access Log
Finish a half-written Bash script that turns an nginx access log into a short report: requests, status classes, top clients and top paths. ShellCheck has to be clean and the edge cases right.
About 45 min
Kubernetes · Intermediate
Fix the Deployment
An inventory API never comes up on Kubernetes. Find and fix the bugs in its Deployment, Service and ConfigMap until the rollout finishes and the Service answers.
About 45 min