Docker rarely breaks on a Tuesday morning when you have time to think. It breaks on Friday evening, halfway through a deploy, in a way that makes no sense until you spot the one small decision that caused it. Most of those decisions are boring, common, and easy to check for once you know the pattern. Here are five that keep coming up, and the quickest way to catch each one before it eats your weekend.
1. Running everything as root
If your Dockerfile never mentions a user, your application runs as root inside the container. That works fine right up until it doesn't: files written to mounted volumes end up owned by root on your host, your editor can't save changes without sudo, and any process that escapes the container starts with far more privilege than it ever needed.
The spot-check takes seconds:
docker exec mycontainer whoami
If that prints root, add a USER instruction to the Dockerfile and create the account explicitly:
RUN useradd --create-home appuser
USER appuser
Some images make this fiddly, and a few tools genuinely need root, but for a typical web service or worker this is a ten-minute fix that removes a whole category of permission weirdness.
2. Having no .dockerignore file
Your build context is everything in the directory where you run docker build — unless a .dockerignore says otherwise. Without one, Docker cheerfully ships your .git folder, node_modules, local logs, and that 400 MB test dataset into the build, every single time. Builds get slower, caches get invalidated by files that have nothing to do with the app, and secrets sitting in a local .env can end up baked into an image layer.
Spot it by watching the build output: Docker prints the size of the build context it sends to the daemon. If that number surprises you, you have found the problem. A minimal .dockerignore covers most projects:
.git
node_modules
*.log
.env
3. Trusting the latest tag
FROM node:latest feels convenient until a rebuild three months later pulls a different base image than the one you tested. The build that worked in March fails in June, and nothing in your code changed — because latest is a moving target, not a version.
The fix is unglamorous: pin your base images. Use a specific version tag, and for anything you ship further afield, pin by digest so the image is bit-for-bit the one you tested. When you do upgrade, upgrade deliberately — bump the tag in the Dockerfile, rebuild, run your tests, and commit the change like any other dependency update. Surprises belong in birthday parties, not in CI.
4. Writing data to the container filesystem
Containers are disposable, and their writable layer goes away with them. Applications that write uploads, caches, or database files into a container's own filesystem seem fine in development — until someone recreates the container and everything vanishes. Worse, heavy writes to the overlay filesystem are slow, and the layer keeps growing until disk usage becomes the next incident.
Check where your app actually writes, and move anything that must survive into a named volume or a bind mount:
docker run -v appdata:/var/lib/myapp myimage
A good rule of thumb: if losing it would ruin your day, it does not belong in the container layer.
5. Letting orphaned volumes and networks pile up
Every experiment leaves debris: stopped containers, dangling images, anonymous volumes from one-off runs, and networks from Compose projects you deleted weeks ago. None of it errors. It just accumulates quietly until the disk fills up and every service on the machine starts failing in unrelated-looking ways.
See what you are holding with:
docker system df
Then reclaim the safe stuff with docker system prune, and add --volumes once you have checked nothing important lives in an anonymous volume. Run both commands monthly and this mistake mostly takes care of itself.
The pattern underneath
None of these five are advanced topics. They are defaults nobody revisited — the default user, the default context, the default tag, the default storage, the default cleanup habit of never. Spending an hour auditing a working setup against this list is dull, but it is much less dull than debugging the same setup at midnight. For more step-by-step troubleshooting in this style, the guides at techexample org work through real commands and real fixes, and there is a plain-language walkthrough of the Docker checks worth running before any deploy if you want the longer version of this list.
Top comments (0)