🐳 The Container That Never Heard a Word You Typed After the Image Name
ENTRYPOINT and CMD are meant to work together – ENTRYPOINT defines the fixed program that always runs, and CMD supplies default arguments to it that anyone can override by appending their own at the end of docker run. That handoff only works, though, when ENTRYPOINT is written in exec form (a JSON array). Written in shell form instead, Docker wraps it in /bin/sh -c, and anything you pass after the image name on the command line gets appended as a completely separate argument to the shell, not to your program – so it’s silently accepted by Docker and silently ignored by the application inside the container.
🔎 The Problem
# Shell form - this is the trap ENTRYPOINT "myapp --config=/etc/myapp/default.yaml" # docker run myimage --config=/etc/myapp/override.yaml # # Docker actually executes: # /bin/sh -c "myapp --config=/etc/myapp/default.yaml" --config=/etc/myapp/override.yaml # # The extra argument gets passed to /bin/sh itself, not to myapp - # the shell ignores it (or errors, depending on the shell), and myapp # runs with ONLY its hardcoded default config, no error reported.
✅ Fix: Use Exec Form for ENTRYPOINT So CMD Arguments Actually Reach the Program
- Write ENTRYPOINT using the JSON array exec form – [“myapp”] instead of “myapp” – which runs the program directly (no intermediate shell) and correctly appends anything passed at docker run as real arguments to it.
- Use CMD, also in exec form, to supply default arguments that complement the ENTRYPOINT – [“–config=/etc/myapp/default.yaml”] – so the combination behaves as intended: overridable defaults, not a fixed, unchangeable shell string.
- If you genuinely need shell features (environment variable expansion, piping) in your entrypoint, use an explicit entrypoint script file instead of shell-form ENTRYPOINT, so you keep both the shell logic and correct argument handling.
⚠️ Why This Is Easy to Miss
- docker run exits cleanly with no warning about the ignored arguments – the container starts, the application runs, just with configuration nobody intended, which looks like an application-level config bug rather than a Dockerfile syntax choice.
- The Dockerfile reads perfectly naturally to anyone unfamiliar with the shell-form-versus-exec-form distinction, since both forms are valid Docker syntax and neither one is flagged as deprecated or incorrect by any tooling.
Shell-form ENTRYPOINT doesn’t run your program – it runs a shell that runs your program, and anything you hand to docker run afterward is arguing with the wrong listener.
