Quick Answer: What are the most common mistakes when using docker in production?
The most common Docker production mistakes are running containers as root, using unpinned `latest` tags, baking secrets into images, skipping CPU and memory limits, and shipping containers with no health checks. Most of these issues live in the Dockerfile, the run command, or the orchestration config, so they can be fixed without rewriting the application itself.
Plain Docker and orchestrators differ in how they enforce some of these fixes. Kubernetes handles resource limits and liveness probes through pod specs, while standalone Docker relies on `HEALTHCHECK` instructions and runtime flags. The underlying principle stays the same: assume the container will fail and design for it.
Before deploying, check the base image user, tag pinning, secret handling, resource limits, health checks, image size, volume usage, log destination, and image scanning cadence.
A container image scanned by a security team found an average of dozens of known vulnerabilities, and a large share of production images still run as root by default. That combination is why Docker security guidance keeps repeating the same warnings year after year. The good news: most production Docker failures trace back to a short list of repeatable mistakes, and every one of them is fixable without rewriting the application. (Docker (software)))
Mistake 1: Running Containers as Root

Most base images default to the root user. That means a compromised process inside the container may have broad privileges, and any misconfigured mount can expose the host. The fix is a single line in the Dockerfile: create a non-root user and switch to it with `USER`.
In practice, teams forget this because their local builds work fine as root. The problem only appears in production, where file permissions and mounted volumes suddenly matter. A common symptom is an application that cannot write to its own data directory after the `USER` switch, which pushes developers to revert to root instead of fixing ownership with `chown` at build time.
A related trap: adding engineers to the host’s `docker` group. Membership there is effectively root on the machine, because anyone who can talk to the Docker daemon can mount the host filesystem. Treat that group like sudo access. (Docker Engine security overview)
Mistake 2: Using `latest` Tags and Unpinned Base Images
The `latest` tag is not a version. It is a moving pointer. A rebuild next Tuesday can pull a different base image than the one tested on Monday, and the change may be a major OS upgrade with a different libc.
Pin images by specific version, and ideally by digest. A digest is immutable, so the exact bytes tested in staging are the exact bytes that run in production. This single habit removes an entire category of “it worked yesterday” incidents.
| Approach | Reproducible? | Risk Level |
|---|---|---|
| `myapp:latest` | No | High |
| `myapp:1.4.2` | Mostly | Medium |
| `myapp@sha256:…` | Yes | Low |
The same rule applies to base images in the Dockerfile. `FROM node:latest` is a time bomb; `FROM node:20.11.1-alpine` is a decision.
Mistake 3: Baking Secrets Into Images
Environment variables set with `ENV` and files copied with `COPY` both persist in image layers and image history. Anyone who can pull the image can read them. Rotating the secret later does not help, because the old value is still in the layer.
The correct pattern is runtime injection: Docker secrets in Swarm, Kubernetes Secrets mounted as volumes, or a dedicated secrets manager. Build arguments are not a safe alternative either, since `–build-arg` values can leak into history and build caches.
A practical check before every release: run `docker history` on the image and read it line by line. If anything sensitive appears, the build is wrong.
Mistake 4: Skipping Resource Limits
A container with no CPU or memory limit can consume everything on the host. One memory leak in one service becomes an outage for every service on that machine. Docker exposes `–memory` and `–cpus` flags; orchestrators expose requests and limits in the pod spec.
The subtle mistake here is setting limits without requests, or setting them far too tight. A container capped at 256 MB that needs 400 MB will be killed repeatedly, and the logs will show only restarts. Load test with realistic traffic before choosing numbers, then leave headroom.
Mistake 5: Shipping Without Health Checks
Without a health check, the platform has no way to know a container is broken. Traffic keeps flowing to a process that is deadlocked or out of database connections. The container looks “up” because the PID exists.
Standalone Docker uses the `HEALTHCHECK` instruction. Kubernetes uses liveness and readiness probes, which serve different purposes: readiness controls traffic, liveness triggers restarts. Confusing the two is its own classic error, because a liveness probe pointed at a dependency-heavy endpoint can restart healthy containers during a brief database blip.
Mistake 6: Bloated Images and Broken Layer Caching
Copying the entire source tree before running the dependency install busts the build cache on every code change. The build takes minutes instead of seconds, and the image carries build tools into production.
Multi-stage builds solve both problems: compile in one stage, copy only the artifacts into a slim runtime stage. Order Dockerfile instructions from least to most frequently changing, so dependency layers cache properly. Alpine and distroless bases cut size further, though Alpine’s musl libc occasionally breaks native modules, so test before switching.
If the team needs fast visual checks of the running UI during QA, capturing a screenshot is quicker with the right shortcut guide, such as this walkthrough on how to take a screenshot on a mac or the equivalent for Windows screenshot capture.
Mistake 7: Storing Data Inside the Container
Container filesystems are ephemeral. Anything written to them disappears on restart, reschedule, or image update. Databases, uploads, and generated reports belong in volumes or external services.
The sneaky version of this mistake is the “temporary” SQLite file that quietly becomes the system of record. Another is log files written inside the container, which grow until the disk fills and then vanish on restart. Send logs to stdout and stderr so the platform collects them; that is the whole contract.
Mistake 8: Ignoring Image Scanning and Patching
Base images age. CVEs accumulate. A team that built a clean image eighteen months ago is now running known vulnerabilities in production, often without knowing it.
Scanning tools integrated into CI catch this automatically. The workflow that actually works: scan on every build, fail the pipeline on critical findings, and rebuild base images on a schedule rather than only when the application changes. Rebuilding a working image with a patched base is low-risk and high-value.
Mistake 9: Treating Local Success as Production Readiness
A container that runs on a laptop has proven almost nothing. Production adds orchestration, network policies, persistent storage, secret rotation, and real traffic. Teams that skip staging parity discover these gaps during incidents.
The practical remedy is a staging environment that mirrors production topology, plus a checklist that covers the items above before the first deploy. If the team also produces marketing visuals for the product, an AI mockup generator for product design can speed up that side of the work, but it will not fix a container that runs as root.
What Actually Works in Practice
Experienced teams converge on a few habits that prevent most of these mistakes. None of them are exotic.
- A hardened base image. Build one internal base image with a non-root user, pinned packages, and a health check. Every service inherits it.
- A Dockerfile linter in CI. Tools like Hadolint catch unpinned tags, root users, and missing health checks before review.
- A pre-deploy checklist. Root user, pinned digests, secrets source, resource limits, probes, volume mounts, log destination, scan results. Eight boxes, two minutes.
- Rebuild cadence. Weekly base image rebuilds keep patched images flowing without waiting for feature work.
- Read-only root filesystem where possible. It turns accidental writes into loud failures instead of silent drift.
One more habit pays off: keep images small and single-purpose. A container that runs one process is easier to limit, probe, and scan than one running five.
When teams need polished product imagery for the storefront that runs on this infrastructure, an AI product photography generator for ecommerce handles the visual side, while the container checklist handles the reliability side.
Why These Mistakes Persist
The root cause is usually not ignorance. It is the gap between the developer’s machine and the production cluster. Local Docker rewards speed: `latest` is convenient, root never complains, and no one sets memory limits on a laptop.
Production punishes exactly those shortcuts. The fix is cultural as much as technical: make the secure path the easy path by templating Dockerfiles, automating scans, and reviewing infrastructure changes with the same rigor as application code.
Conclusion
Docker in production fails in predictable ways. Root users, floating tags, embedded secrets, missing limits, absent health checks, bloated images, data in the wrong place, unpatched bases, and untested deployments cover the overwhelming majority of incidents. Each has a known fix, and most fixes are a few lines of configuration.
The teams that ship reliably are not the ones with the cleverest orchestration. They are the ones who turned these nine checks into defaults, automated them, and stopped treating a working laptop build as evidence of anything. Start with the Dockerfile, add the checklist, and the rest follows.
Frequently Asked Questions
Is it safe to run Docker in production without an orchestrator?
Yes, for small deployments. Plain Docker with restart policies, health checks, and resource limits handles modest workloads well. Orchestrators add scheduling, scaling, and self-healing, which matter as service count grows.
How often should base images be rebuilt?
A weekly or biweekly cadence works for most teams. The goal is to pick up patched base layers without waiting for a feature release, and to keep the rebuild itself boring and low-risk.
Do resource limits slow applications down?
Memory limits do not slow anything; they cap usage. CPU limits can throttle under burst load, which is why requests and limits are set separately in orchestrators so bursty services keep headroom.
What is the difference between a liveness and a readiness probe?
Readiness decides whether traffic reaches the container. Liveness decides whether the container gets restarted. Pointing liveness at an endpoint that depends on a database can cause restart loops during brief outages.
Can secrets be passed safely as environment variables?
They can be injected at runtime by the orchestrator, which is acceptable for many teams. They must never be baked into the image with `ENV` or `COPY`, because image layers and history preserve them permanently.
How large should a production image be?
Smaller is better, but there is no magic number. A typical compiled service lands well under 100 MB with multi-stage builds and a slim base. If an image carries compilers and build caches, it is too large.
The checklist is the artifact that outlives any individual engineer. Write it down, keep it in the repository next to the Dockerfile, and require it in the pull request template so it cannot be skipped under deadline pressure. Review it whenever an incident occurs, and add the missing line that would have caught it. Over a year, the checklist becomes a precise record of everything the team has learned the hard way. Pair it with a rollback plan: know the previous image digest, know how to redeploy it, and practice the rollback once on staging so it is not improvised at 3 a.m. Reliability in production is rarely the result of one brilliant decision. It is the accumulation of small, boring defaults applied consistently, release after release, until the safe path requires no thought at all.


