Your Dockerfile Is Broken. You Just Don’t Know It Yet

Kaan YagciKaan Yagci
5 min read

Most developers only ever use FROM, COPY, RUN, EXPOSE and CMD

If they’re fancy, maybe they throw in an ENV or two.

But that’s it. That’s their entire Dockerfile vocabulary.

And that’s a problem.

You’re not writing shell scripts. You’re defining a production system. And if you’re shipping containers with the bare minimum, you’re not just making life harder for your Ops team.

You’re also exposing yourself to security issues, portability nightmares, and invisible production bugs.

The Full List: All 17 Dockerfile Instructions

Here’s every instruction available in Dockerfiles today:

  1. FROM

  2. ARG

  3. ENV

  4. LABEL

  5. COPY

  6. ADD

  7. RUN

  8. CMD

  9. ENTRYPOINT

  10. EXPOSE

  11. VOLUME

  12. WORKDIR

  13. USER

  14. SHELL

  15. STOPSIGNAL

  16. HEALTHCHECK

  17. ONBUILD

No, they’re not all interchangeable. And yes, several of them are misunderstood, misused, or just ignored.

Notable Instructions You Probably Ignore

1. HEALTHCHECK

Your container isn’t healthy just because it’s running.

Let’s be clear: Kubernetes ignores the HEALTHCHECK instruction in your Dockerfile.

If you want health checks in Kubernetes, you need to define livenessProbe and readinessProbe in your Kubernetes manifests. Dockerfile HEALTHCHECK is useful only in local development, Docker Compose setups, or CI pipelines where Docker is directly used.

Still, it’s valuable:

HEALTHCHECK CMD curl --fail http://localhost:3000/health || exit 1

Use it to catch issues early during image build testing. With Kubernetes, use it to document health semantics so your Ops team understands how to check if the app is actually alive, without reverse engineering your container or pinging you at 3 AM.

2. SHELL

Tired of escaping nightmares in your RUN commands?

SHELL ["bash", "-c"]

This lets you avoid quoting madness when your image has Bash. It’s also key for Windows containers where PowerShell is the default.

3. STOPSIGNAL

How does your app shut down?

If Docker sends SIGTERM but your app only responds to SIGINT, guess what?

It never shuts down cleanly. You get zombie processes or data loss.

STOPSIGNAL SIGINT

Use the signal your app actually handles.

4. ONBUILD

This is powerful and dangerous.

Want a base image that auto-runs instructions when used as a parent?

ONBUILD COPY . /app

Used carefully, it’s magic. Used carelessly, it’s a pipeline grenade.

5. ARG vs ENV

Understand the difference:

  • ARG = Build-time only. Not in the final image.

  • ENV = Runtime. Baked into the image.

Confuse the two, and your production environment won’t behave like dev. Debugging this will waste hours.

6. LABEL

Metadata matters.

LABEL maintainer="kaan.yagci@makepad.fr"
LABEL version="1.0"

This helps with:

  • Image scanning tools

  • CI/CD pipelines

  • Audit compliance

It’s not cosmetic. It’s documentation that your tooling can read.

Quick Recap of the Basics

Even the common ones have depth:

  • FROM defines the base image. Think of it as your OS.

  • COPY and ADD differ: ADD can unpack archives and fetch URLs. Use COPY unless you need ADD.

  • RUN executes a shell command during build.

  • CMD sets arguments to ENTRYPOINT (if present).

  • ENTRYPOINT defines the main command, and can combine with CMD.

  • USER, WORKDIR, VOLUME, and EXPOSE all contribute to better security and usability.

If you want to learn more about CMD vs ENTRYPOINT I have covered it in this post

Stop using only 5 of them. You’re leaving performance, security, and clarity on the table.

What is OCI and Why Should I Care

Most developers have never heard of the OCI spec. But it defines the foundation for container portability.

OCI (Open Container Initiative) is a vendor-neutral industry standard. It defines:

  • What configuration fields a container runtime must support?

  • How should container images be structured and executed across any compliant runtime (e.g., containerd, CRI-O, Podman)?

Docker helped define this standard, but the spec is more minimal than what Docker allows in a Dockerfile.

OCI Fields (from config.json)

The OCI runtime spec cares only about fields that affect how a container runs. These include:

  • Env

  • Entrypoint

  • Cmd

  • WorkingDir

  • ExposedPorts

  • Volumes

  • User

  • Labels

  • StopSignal

This is why Dockerfile instructions like:

  • HEALTHCHECK

  • ONBUILD

  • ARG

  • SHELL

…are ignored by the runtime. They don’t appear in the final image’s OCI config.

And remember: Kubernetes isn’t a runtime. It delegates to runtimes like containerd. So if the runtime doesn’t honor those fields, neither will Kubernetes.

Why You Should Still Use the Non-OCI Instructions

  1. Local Development
    HEALTHCHECK and ONBUILD can massively improve DX for teams using Docker directly.

  2. CI/CD Pipelines
    Use LABEL, ARG, and ENV to pass metadata, build versioning, and secrets securely.

  3. Tooling Compatibility
    Tools like Hadolint, Docker Scout, or Snyk rely on metadata and health declarations to give useful feedback.

  4. Future Proofing
    OCI is evolving. These features may become standard—or at least better supported—by newer build tools (e.g., BuildKit).

Bottom line:

Dockerfile isn’t a bash script. It’s an immutable blueprint for your system.

Write it like it matters. Because it does.

Final thoughts

Your Dockerfile is a reflection of your engineering maturity.

A sloppy Dockerfile leaks into production. It creates tech debt, frustrates teams, and bloats your cloud bill.

Start treating it like code. Use the full power Docker gives you.

And next time someone brags their Dockerfile is “simple”, ask them if it’s also complete.


I help engineering teams build smarter systems, from resilient containerization to scalable software architecture.

If you care about shipping clean, production-ready software (and avoiding silent failures at scale), you’ll want to stick around.

Follow me on LinkedIn for brutally practical insights
Subscribe to this blog for deep dives, not developer fluff
Reach out if your stack needs clarity, performance, or a no-bullshit architecture review

I don’t write theory. I ship battle-tested systems.

0
Subscribe to my newsletter

Read articles from Kaan Yagci directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kaan Yagci
Kaan Yagci

Senior Platform Engineer. Infra and programming languages nerd. I write about the stuff nobody teaches: how things really work under the hood, containers, orchestration, authentication, scaling, debugging, and what actually matters when you’re building and running real systems. I share what I wish more real seniors did: the brutal, unfiltered truth about building secure and reliable systems in production.