Dockerfile ENTRYPOINT: How It Works, CMD vs ENTRYPOINT, and Best Practices

Dockerfile ENTRYPOINT: How It Works, CMD vs ENTRYPOINT, and Best Practices

A Dockerfile ENTRYPOINT defines the command that runs when a container starts. Use it when the image should behave like a specific executable: an API server, a CLI tool, a worker, or a small wrapper script that prepares the environment and then launches the real process.

Quick answer: ENTRYPOINT sets the container’s main executable. CMD usually supplies the default arguments. The safest default is exec form: ENTRYPOINT ["executable", "arg"]. Use shell form only when you intentionally need shell behavior.

If you are building broader Docker fundamentals, this fits naturally with the Docker containers guide, the multi-stage Dockerfile guide, and Compose workflows like docker compose up.

What Dockerfile ENTRYPOINT Does

ENTRYPOINT tells Docker what executable to start as PID 1 inside the container. When someone runs the image, Docker launches that command by default. Any arguments passed through docker run are appended to the entrypoint unless you override it.

FROM python:3.12-slim
WORKDIR /app
COPY app.py .
ENTRYPOINT ["python", "app.py"]Code language: JavaScript (javascript)

With that image, running the container starts python app.py. The image has a clear default behavior instead of acting like a generic shell environment.

ENTRYPOINT vs CMD vs RUN

Most confusion comes from mixing up three Dockerfile instructions that happen at different times.

InstructionWhen it runsWhat it is for
RUNDuring image buildInstall packages, compile code, create files, and bake changes into the image.
CMDWhen the container startsProvide the default command or default arguments. Can be replaced easily at runtime.
ENTRYPOINTWhen the container startsDefine the main executable the container is meant to run. Runtime arguments are appended to it.

CMD alone: easy to replace

CMD ["python", "app.py"]Code language: CSS (css)

If the image uses only CMD, a user can replace the whole command by passing another command to docker run. That is useful for flexible base images, but less precise for images that should always start one program.

ENTRYPOINT plus CMD: executable plus default arguments

ENTRYPOINT ["python", "app.py"]
CMD ["--host", "0.0.0.0", "--port", "8080"]Code language: CSS (css)

This pattern is often the most useful: ENTRYPOINT defines the executable and CMD defines default arguments. A user can override the arguments without replacing the executable.

docker run my-python-app --port 9000

That keeps the container behavior predictable while still allowing runtime configuration.

Exec Form vs Shell Form

Docker supports two forms. Prefer exec form unless you specifically need shell expansion, pipes, or multiple shell commands.

Exec form: recommended default

ENTRYPOINT ["node", "server.js"]Code language: CSS (css)

Exec form starts the executable directly, so your application can be PID 1 and receive stop signals more predictably. That matters during docker stop, rolling deployments, and orchestrator shutdowns, where a process that ignores SIGTERM may be killed after the grace period.

Shell form: convenient but easier to misuse

ENTRYPOINT node server.jsCode language: CSS (css)

Shell form runs through /bin/sh -c. That means PID 1 is the shell, not your application. In practice, the shell may not forward SIGTERM cleanly to the child process, so containers can hang until Docker or the orchestrator sends a forced kill. If your application needs clean shutdown behavior, prefer exec form or make sure your shell script uses exec to hand control to the real process.

Using an entrypoint.sh Wrapper Safely

A wrapper script is useful when the container needs to run setup before starting the main process: template config, run a lightweight migration check, wait for a dependency, or validate environment variables. The key is to end the script with exec "$@".

#!/bin/sh
set -e

: "${APP_ENV:=production}"
echo "Starting app in $APP_ENV mode"

exec "$@"Code language: PHP (php)
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
CMD ["node", "server.js"]Code language: JavaScript (javascript)

exec "$@" replaces the shell with the real application process. Without it, the shell stays as PID 1, signal handling can be worse, and the application may not stop cleanly.

How to Override ENTRYPOINT at Runtime

Sometimes you need to debug an image or run a one-off command. Use --entrypoint to replace the image entrypoint temporarily.

docker run --rm --entrypoint sh my-image

For Compose, the equivalent is the service-level entrypoint: field. Use it carefully: overriding the entrypoint can bypass setup logic the image author expected to run.

services:
  app:
    image: my-image
    entrypoint: ["sh", "-lc", "echo debug && exec node server.js"]Code language: CSS (css)

If the image is distroless or built FROM scratch, it may not contain sh. In that case, debug with a purpose-built debug image or add temporary tooling only in a separate debug build, not in the production image.

Common ENTRYPOINT Mistakes

Mistake 1: combining ENTRYPOINT and CMD on one line

Keep Dockerfile instructions separate. Dockerfile instructions are line-based, so gluing ENTRYPOINT and CMD together creates invalid or misleading syntax. This is wrong:

ENTRYPOINT ["python3", "app.py"]CMD ["--help"]Code language: CSS (css)

Use separate lines:

ENTRYPOINT ["python3", "app.py"]
CMD ["--help"]Code language: CSS (css)

Mistake 2: putting complex logic directly in ENTRYPOINT

A long shell command inside ENTRYPOINT is hard to read, hard to test, and easy to break. Put real setup logic in a small script, test the script, and keep the Dockerfile obvious.

Mistake 3: forgetting signal handling

Containers are stopped with signals. If a shell script starts your app as a child process and never uses exec, the app may not receive shutdown signals properly. This can cause slow shutdowns, stuck deployments, or data loss in stateful services.

Mistake 4: using ENTRYPOINT when CMD is enough

Not every image needs ENTRYPOINT. For flexible developer images, CMD alone can be better because users can easily replace the command. Use ENTRYPOINT when the image has a clear primary behavior.

Practical Rules of Thumb

  • Use ENTRYPOINT for images that behave like one executable or service.
  • Use CMD for default arguments that users may override.
  • Prefer exec form: ENTRYPOINT ["app", "arg"].
  • Use wrapper scripts only when setup is genuinely needed.
  • End wrapper scripts with exec "$@".
  • Document expected runtime arguments in the README or image docs.

Troubleshooting ENTRYPOINT Problems

SymptomLikely causeFix
Container exits immediatelyEntrypoint command finishes quickly or wrapper script does not run the final commandCheck logs, verify the command, and use exec "$@" in wrapper scripts.
Arguments are ignoredENTRYPOINT and CMD are not combined as expectedUse exec form and test with docker run image arg.
Cannot open a shell for debuggingImage entrypoint always starts the appRun docker run --entrypoint sh image.
Slow or dirty shutdownShell wrapper does not forward signals cleanlyUse exec form or end the wrapper with exec "$@".

FAQ

What does ENTRYPOINT do in a Dockerfile?

ENTRYPOINT defines the executable that runs when a container starts. It is best used when the image should behave like a specific application, service, or CLI tool.

What is the difference between ENTRYPOINT and CMD?

ENTRYPOINT sets the main executable. CMD usually provides default arguments or a default command. A common pattern is ENTRYPOINT for the executable and CMD for arguments users can override.

Should I use exec form or shell form for ENTRYPOINT?

Use exec form by default, such as ENTRYPOINT [“node”, “server.js”]. Shell form is useful only when you intentionally need shell features like variable expansion, pipes, or command chaining.

How do I override ENTRYPOINT when running a container?

Use docker run –entrypoint to replace the image entrypoint temporarily. For example, docker run –rm –entrypoint sh my-image opens a shell if the image contains sh.

Why should an entrypoint script use exec “$@”?

exec “$@” replaces the shell script with the real application process. That helps the app receive signals directly and usually makes container shutdown behavior cleaner.

Bottom Line

Use ENTRYPOINT when your Docker image has one clear job. Pair it with CMD when you want sensible default arguments, prefer exec form for predictable runtime behavior, and keep wrapper scripts small enough that future you can debug them quickly.

For more Docker workflow context, continue with docker compose up, docker compose stop, and docker compose down -v.

Nathan Cole Avatar

Leave a Reply

Your email address will not be published. Required fields are marked *