Containers often fail to shut down cleanly because a shell wrapper became PID 1 instead of the application.
ENTRYPOINT ["node", "server.js"]
CMD ["--port", "3000"]
The division of labour is simple: ENTRYPOINT names the fixed executable, CMD supplies defaults a caller may replace. Prefer JSON exec form over shell form, so SIGTERM reaches the process that must handle it.
| Instruction | Role |
|---|---|
| ENTRYPOINT | What the image runs |
| CMD | Default arguments/command |
Shell-form
CMD node server.jsinserts a shell and changes signal handling. It is rarely what a service container wants.
Make runtime overrides intentional, then test docker stop rather than assuming the Dockerfile tells the truth.
Compose the final command deliberately
docker run image --port 8080 replaces CMD arguments while retaining an exec-form ENTRYPOINT. That is ideal for an image whose executable is fixed but flags vary. If callers need to replace the executable itself—an image used for a shell, migrations, and a server—use CMD alone or document an entrypoint override.
| Image purpose | Sensible pattern |
|---|---|
| One fixed service executable | ENTRYPOINT plus default CMD args |
| General runtime base image | CMD only |
| Wrapper that must initialise state | Small executable entrypoint that ends with exec |
ENTRYPOINT ["sh", "-c", "…"]brings the signal problem back unless the script ends withexec. Inspect the process tree, not just the Dockerfile.
Run docker inspect --format '{{json .Config}}' image and test an override before publishing the image contract. Container arguments are part of your public API.
Cover photo by Stanislav Kondratiev on Pexels.
