広告が嫌いですか? 行く 広告なし 今日

Docker ENTRYPOINT vs CMD — Your Container Lied to You

更新日

You combined ENTRYPOINT and CMD in your Dockerfile, the container started the wrong thing, and now you're here. Here's the full breakdown — every combination, the shell vs exec form trap, and the patterns that actually work.

Docker ENTRYPOINT vs CMD — Your Container Lied to You 1

The error happens at 2am. You start your container, and instead of your API server, you get a shell prompt. Or nothing at all. Or your process runs wrapped in a ghost sh that eats SIGTERM like candy — so graceful shutdown takes 10 seconds of Docker waiting before it gives up and sends SIGKILL.

The culprit, nearly every time: you confused ENTRYPOINTCMD. Or combined them in ways Docker silently accepts — just not how you expected.

CMD: The Default You Can Replace

CMD sets what runs when you start a container — but it’s a suggestion, not a rule. Pass anything after the image name and it gets replaced entirely:

FROM ubuntu
CMD ["echo", "hello from CMD"]
$ docker run myimage
hello from CMD

$ docker run myimage echo goodbye
goodbye

それは人々を混乱させます。2つの選択肢があります:キーワードまたは度数の角度です。 echo goodbye didn’t append — it replaced. Your entire CMD is gone. This is by design: CMD is the default behavior, not the enforced behavior. Any runtime argument wins.

ENTRYPOINT: The Part That Always Runs

ENTRYPOINT sets the executable that runs no matter what. Runtime arguments don’t replace it — they get passed to it instead:

FROM ubuntu
ENTRYPOINT ["echo"]
CMD ["hello"]
$ docker run myimage
hello

$ docker run myimage goodbye
goodbye

$ docker run --entrypoint cat myimage /etc/hostname
mycontainer-abc123

When both are set, ENTRYPOINT is the executable and CMD becomes its default arguments. Override CMD freely. Override ENTRYPOINT only if you explicitly pass --entrypoint.

Every ENTRYPOINT + CMD Combination, Explained

The Docker docs include this table but don’t dwell on the rows that will wreck your day:

オプション。CMDがのみ引数を提供する場合に使用します。コンテナが開始されるときのデフォルトプロセス。What actually runs
HTTPSでのみ送信HTTPSでのみ送信Error — container needs a command from somewhere
HTTPSでのみ送信["cmd", "arg"] exec formcmd arg
HTTPSでのみ送信cmd arg shell form/bin/sh -c "cmd arg"
["entry"] exec formHTTPSでのみ送信entry
["entry"] exec form["arg1", "arg2"] exec formentry arg1 arg2
["entry"] exec formcmd arg shell formentry /bin/sh -c "cmd arg" — almost certainly wrong
entry shell form["arg1"] exec form/bin/sh -c "entry"CMD silently ignored
entry shell formcmd arg shell form/bin/sh -c "entry"CMD silently ignored

The two rows marked “CMD silently ignored” are responsible for a disproportionate amount of Docker debugging sessions. Shell form ENTRYPOINT does not merge with CMD — it ignores it entirely. Docker won’t warn you about this.

Shell Form vs Exec Form: The Signal Handling Trap

Both instructions accept two forms, and the choice matters more than most Dockerfile tutorials admit.

Exec form (array syntax):

ENTRYPOINT ["nginx", "-g", "daemon off;"]

Your binary runs directly. It becomes PID 1. When Docker sends SIGTERM to stop the container, your process receives it. Graceful shutdown works. Logs flush. Connections close cleanly.

CMD (plain string):

ENTRYPOINT nginx -g "daemon off;"

Docker runs this as /bin/sh -c "nginx -g daemon off;". The shell is PID 1. When SIGTERM arrives, sh gets it — and sh doesn’t forward signals to child processes. Your container hangs for 10 seconds, receives SIGKILL, and dies without cleanup. Every single time.

Use exec form. Always. For both ENTRYPOINTCMD.

Three Patterns That Actually Work

Pattern 1: Fixed executable, overridable defaults

The right pattern for most production containers. The binary is fixed; the flags are swappable at runtime:

ENTRYPOINT ["/app/server"]
CMD ["--port", "8080", "--env", "production"]
# Use defaults
docker run myimage

# Override at deploy time
docker run myimage --port 9090 --env staging

Pattern 2: Wrapper script with exec

When you need init logic before your main process (migrations, secret injection, signal trapping), use a wrapper script. The critical line is exec "$@" at the end — it replaces the shell process with your CMD, so your binary becomes PID 1:

#!/bin/sh
set -e

echo "Running migrations..."
/app/migrate

# Hand off to CMD — exec replaces shell, so /app/server becomes PID 1
exec "$@"
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/app/server", "--port", "8080"]

If you skip exec "$@", the shell stays as PID 1 and you’re back to signal handling problems.

Pattern 3: CMD only, no ENTRYPOINT

Fine for development images or tooling containers where you want to run arbitrary commands against the same environment:

FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

For production, Pattern 1 or 2 is safer — you don’t want a misconfigured deploy script accidentally running docker run myimage bash and replacing your server with a shell session.

What docker exec Has to Do With This

Nothing. docker exec runs a command in an already-running container. It bypasses ENTRYPOINT entirely. You don’t need to think about ENTRYPOINT at all when you run docker exec mycontainer bash to poke around — you’re talking to the live container environment, not the startup config.

The confusion usually comes from people who debug with docker exec and confirm things work, then wonder why docker run behaves differently. They’re completely separate code paths.

Pre-Ship Checklist

  • どちらも ENTRYPOINTCMD use exec form (array syntax, not plain strings)
  • If you have a wrapper script, it ends with exec "$@"
  • You’ve tested docker run myimagedocker run myimage --your-flag to confirm both paths work
  • docker stop mycontainer completes in under 2 seconds (not 10 — if it’s 10, you have a signal problem)

If you want automated feedback before your Dockerfile goes anywhere near a registry, IO Tools’ Dockerfile Linter catches shell form usage, missing exec in entrypoint scripts, and other patterns that cause silent misbehavior at runtime.

広告なしで楽しみたいですか? 今すぐ広告なしで

拡張機能をインストールする

お気に入りのブラウザにIOツールを追加して、すぐにアクセスし、検索を高速化します。

に追加 Chrome拡張機能 に追加 エッジ拡張 に追加 Firefox 拡張機能 に追加 Opera 拡張機能

スコアボードが到着しました!

スコアボード ゲームを追跡する楽しい方法です。すべてのデータはブラウザに保存されます。さらに多くの機能がまもなく登場します!

ニュースコーナー 技術ハイライト付き

参加する

価値ある無料ツールの提供を継続するためにご協力ください

コーヒーを買って