Docker for DevOps: What to Learn and What to Skip
By Branko Petric - Updated - About 3 weeks at 10 hours a week
The short answer
For DevOps, you need enough Docker to package an app and debug it when it crashes. Learn to write a multi-stage Dockerfile, run as a non-root user, use Compose with a database and a volume, and read container logs. Skip Swarm and the DCA certification. Plan on about 3 weeks at 10 hours a week.
Why Docker comes after running an app by hand
At step 5, you installed a language runtime, packages and config files on a server, one command at a time. Then you rebuilt it from your notes and saw how easily the steps go wrong. A new server may also have a different version of something, and the app breaks.
Docker solves that problem. Your runbook turns into a Dockerfile, which builds the same image every time. Because you know what each line replaces, the Dockerfile will make sense. It also sets up CI/CD, which will build this image for you.
What to learn, in order
- Images and containers. Start with the Docker overview. Run an existing image with
docker run, and usedocker psto see what is running. - Publishing ports. Containers have their own network.
-p 8080:8000sends port 8080 on your machine to port 8000 in the container. You will use your networking skills to debug this. - Writing a Dockerfile. Learn
FROM,COPY,RUN,CMDandWORKDIR. Turn your app's install steps into these lines. - Layers and the build cache. Each step creates a layer, and Docker reuses layers that did not change. Copy your dependency list and install it before you copy your code. Otherwise every code edit reinstalls everything.
.dockerignore. This file keeps things like.gitand.envout of the build. It makes builds faster and helps keep secrets out of the image.- Multi-stage builds. A multi-stage build uses one stage to build the app and another to run it. Build tools stay out of the final image, so it is smaller and exposes less.
- Running as non-root. Add a
USERline to your Dockerfile. If someone breaks into the app, they then have fewer rights inside the container. - Volumes. A container's own files are lost when you remove it. A volume keeps data outside the container, which your database needs.
- Compose. Docker Compose starts several containers from one file. Your app can reach the database by its service name, such as
db, instead of an IP address. - Debugging. Use
docker logsfirst, thendocker exec -it <container> shto look around inside.docker inspectshows settings such as the exit code. Very small images may not include a shell. - Registries. A registry stores images, so servers can pull them. Docker Hub and GitHub Container Registry are common choices.
What to skip for now
- Docker Swarm. Kubernetes is the orchestration tool on this roadmap.
- Comparing Docker with Podman and other tools. The ideas carry over.
- Writing your own container runtime, or studying how namespaces work in depth.
- The Docker Certified Associate (DCA) exam.
- Advanced custom network setups. The default Compose network is enough for your project.
Common mistakes
- Putting secrets in the image. Anyone who can pull the image can read its layers, and deleting a file in a later layer does not remove it from an earlier one. Pass secrets in when the container starts.
- Copying your code before installing dependencies. One code edit then invalidates the cache, and every build starts over.
- Keeping database data only inside the container. Removing the container then removes the data. Use a volume, and take care with
docker compose down -v, because-vdeletes volumes too. - Using the
latesttag in production. It does not tell you what version is running, and it can change without warning. Tag images with a version or a Git commit. - Publishing the database port. On Linux, Docker's published ports can bypass ufw rules. In Compose, your app can reach the database without publishing its port at all.
- Using
--privilegedor mounting the Docker socket to fix a permission error. Either one can give the container control over the host. Find out which permission is missing, and fix that.
Your project
Package the app from step 5:
- Write a multi-stage Dockerfile that runs as non-root. Note the image size before and after you make it multi-stage.
- Write a Compose file with the app, Postgres and a named volume. Use a
.envfile for the password, and keep it out of Git. - Prove that the data survives
docker compose downanddocker compose up. - Restore your
pg_dumpbackup into the Postgres container, to show that your backup still works here. - Push the image to a registry, and run it on your server from there.
Then look back at your runbook and mark each step Docker now handles. The steps left, such as building and pushing the image on each change, are what CI/CD automates next.
You are done when
You can do all of this with no notes. If you already can, skip this step.
- You write a multi-stage Dockerfile for your app, and
docker imagesshows that the final image is smaller than a single-stage build of the same app. docker run --rm <image> id -uprints a number other than 0, which proves that the container does not run as root.docker compose upstarts your app and Postgres together. You add data, rundocker compose down, start it again, and the data is still there.- You make a container crash on purpose, then find the reason with
docker logs. You also inspect a running container withdocker exec. - You push your image to a registry, delete it locally, and run it again by pulling it.
Learn it and test it
Free guides
- Docker Fundamentals for DevOps
- Writing Dockerfiles: Best Practices for Small, Fast Images
- Docker Volumes and Persistent Data
- Docker Compose: Running Multi-Container Apps Locally
- Debugging Docker Containers
- Optimizing Docker Image Size and Build Speed
Practice scenarios
- 1.6GB Docker Image, 8-Minute Builds. Add Runners?
Your image is huge and every code change rebuilds from scratch. A teammate wants to just add more CI runners. Defend how you actually fix it.
- The Build Is Slow, So Let's Just Add --no-cache to CI?
CI Docker builds crept to 11 minutes and once shipped a stale dependency. A teammate wants --no-cache on every build. Defend what you actually do first.
- The Container Needs Docker, So Mount the Socket and Run --privileged?
A CI job needs to build images and a teammate wants to bind-mount the Docker socket and run --privileged to fix permission errors. Defend the safe approach.
Common questions
Should I learn Docker before Kubernetes?
Yes. Kubernetes runs containers across many machines, so you need to understand what a container is first. When a Kubernetes app fails to start, the cause is often in the image or the app's startup. Kubernetes comes much later on this roadmap.
What is the difference between a Docker image and a container?
An image is the packaged app, stored as a read-only template. A container is a running copy of that image. You can start many containers from one image. Anything a container writes is lost when you remove it, unless you store it in a volume.
Is a container the same as a virtual machine?
No. A virtual machine simulates a whole computer and runs its own operating system. A container shares the kernel (the core of the operating system) of the machine it runs on. That makes containers smaller and quicker to start, but they are less isolated from the host.
Practice defending your decisions
DevOps Ledger is a scenario-based DevOps and SRE interview practice platform: you pick a real production incident, defend your decision in a chat against a senior-engineer AI that pushes back, and get scored against an answer key written before you started.
One payment. 12 months of access. No subscription, nothing renews.
Get full access - $19