DevOps

Docker: Override or Remove ENTRYPOINT From a Base Image

Distinguish a Dockerfile override from a runtime override, then check which command the final image and container actually execute.

Mahdi Ben RhoumaMahdi Ben Rhouma
Updated
7 min read

TL;DR#

A child Dockerfile's ENTRYPOINT replaces the parent's entrypoint. Declare the command you intend to run, set CMD explicitly when it supplies arguments, then inspect the image you actually built. The original Stack Overflow thread reports success with an env entrypoint and a script in CMD, but that indirection is optional. An exec-form entrypoint that invokes the script directly already overrides the parent.

The problem#

plaintext
FROM denvazh/gatling:2.2.3
 
RUN apk update \
  && apk add -U bash \
  && apk add nodejs=6.7.0-r0
 
COPY simulations /opt/gatling/user-files/simulations
COPY trigger-test-and-parser.sh /opt/gatling/
 
RUN chmod +x /opt/gatling/trigger-test-and-parser.sh
 
ENTRYPOINT ["bash", "/opt/gatling/trigger-test-and-parser.sh"]

This is the historical question's Dockerfile, including its old package pins. It already declares a new exec-form entrypoint invoking bash and the script. Therefore the parent's entrypoint cannot explain a failure of this particular image: it has been replaced. The report does not establish why that attempted script failed. Inspect the image tag, the script's contents and its runtime output rather than inventing an inheritance rule. For a current project, use its supported base image and dependencies instead of copying these historical package pins.

Why it happens#

Per the official Dockerfile reference on ENTRYPOINT, only the last ENTRYPOINT instruction that applies to the image has any effect. A child Dockerfile's FROM base-image followed by its own ENTRYPOINT line means your instruction is the one Docker keeps — the base image's ENTRYPOINT is not merged with yours, appended to it, or run first. It is discarded entirely once your image declares a new one.

The same reference calls out an easy-to-miss side effect: setting a new ENTRYPOINT resets any CMD inherited from the base image back to an empty value. So a base image that shipped both an ENTRYPOINT and a default CMD loses both the instant your Dockerfile overrides the entrypoint — you are not just replacing one instruction, you are clearing two.

The other half of the confusion is exec form versus shell form. In exec form (ENTRYPOINT ["executable", "param1"]), CMD values are appended as extra arguments to your ENTRYPOINT executable — this is the documented pairing for "CMD provides default arguments for ENTRYPOINT." In shell form (ENTRYPOINT command param1), Docker wraps the whole thing in /bin/sh -c and, per the same page, shell-form ENTRYPOINT ignores any CMD or docker run command-line arguments outright. That distinction is exactly what trips people up when they mix a shell-form ENTRYPOINT with a CMD they expect to be appended: it silently never runs.

None of this is Gatling-specific or Node-specific — it's how any layered image behaves once a second Dockerfile declares its own ENTRYPOINT on top of a base image that already had one.

Fix#

The pattern from the accepted answer works because /usr/bin/env is a real executable whose entire job is to exec whatever command line follows it — so it becomes a transparent stand-in for the base image's original entrypoint binary.

  1. Point ENTRYPOINT at /usr/bin/env in exec form, so CMD is appended to it as arguments:
dockerfile
FROM denvazh/gatling:2.2.3
 
RUN apk update \
  && apk add -U bash \
  && apk add nodejs=6.7.0-r0
 
COPY simulations /opt/gatling/user-files/simulations
COPY trigger-test-and-parser.sh /opt/gatling/
 
RUN chmod +x /opt/gatling/trigger-test-and-parser.sh
 
ENTRYPOINT ["/usr/bin/env"]
  1. Set CMD to the script that runs both commands in sequence, also in exec form so it combines correctly with the exec-form ENTRYPOINT:
dockerfile
CMD ["bash", "/opt/gatling/trigger-test-and-parser.sh"]
  1. The script itself just runs both commands one after another — nothing special is required inside it because the container's lifecycle now follows the script, not the base image's original binary:
bash
#!/bin/bash
gatling.sh -s MicroserviceHPSPubSubRatePerfTest.scala
node publish-rate-to-team-city.js
  1. Rebuild and run as usual:
bash
docker build --no-cache -t gatling-nodejs:v8 .
docker run -i -v "$PWD/results":/opt/gatling/results gatling-nodejs:v8

Alternative: skip the Dockerfile change entirely#

If you cannot rebuild the image right now, or you only need this for one run, override the entrypoint from the command line instead. The docker run CLI reference documents --entrypoint as overwriting the default ENTRYPOINT of the image for that container:

bash
docker run --entrypoint /opt/gatling/trigger-test-and-parser.sh gatling-nodejs:v8

--entrypoint takes a single executable path. It also clears the image's CMD, as the runtime entrypoint reference documents. Supply arguments after the image name when invoking an interpreter:

bash
docker run --rm --entrypoint bash gatling-nodejs:v8 /opt/gatling/trigger-test-and-parser.sh

The direct script form requires executable permissions and a valid interpreter line. The bash form requires bash to exist in the image; neither form silently preserves arguments from the image CMD.

Verify the fix#

Confirm which entrypoint actually shipped in the built image, rather than trusting the Dockerfile alone:

bash
docker inspect --format='{{.Config.Entrypoint}} {{.Config.Cmd}}' gatling-nodejs:v8

You should see [/usr/bin/env] as the entrypoint and your script command as the Cmd array — not the base image's original [gatling.sh]. Then run the container and confirm both processes execute in order, in the container logs:

bash
docker run -i gatling-nodejs:v8

If output stops after the first command, inspect the actual script and its exit status. Check the final image's configuration and the container's overrides before attributing the failure to Docker. An earlier entrypoint in the same stage does not override a later one. Also verify that you ran the newly built tag and selected the intended build stage.

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.

Variants and edge cases#

  • Base image CMD you actually wanted to keep. Because setting ENTRYPOINT clears the inherited CMD, if the base image's default CMD supplied arguments you relied on, you must re-declare them explicitly in your own CMD — they will not survive the override implicitly.
  • Compose services. A non-null entrypoint ignores the image's CMD. An explicit Compose command can still supply arguments. These are separate configuration levels; the Compose entrypoint reference documents their interaction.
  • Multi-stage builds. A stage based on an unrelated image does not inherit another stage's entrypoint merely because it copies its files. A final FROM earlierStage, however, inherits that stage's configuration. The multistage reference shows this distinction.
  • Signal handling. A script that starts child processes needs appropriate shutdown behaviour if early termination matters. This also applies to one-shot tasks stopped before completion; inspect how the shell forwards signals rather than assuming Docker delivers them directly to every child.

An override selects the new command; it does not arrange for the parent's executable to run first. If the replacement cannot start, inspect its interpreter, permissions and path. A wrong-architecture exec format error or an OCI runtime exec failure needs its own diagnosis. For a Dockerfile and Compose development setup to compare against, see the Docker Compose dev environment guide. Keep the image configuration, container overrides and script output together: those establish which command ran and where it stopped.

Frequently Asked Questions

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote