Docker ENTRYPOINT
Docker commands
containerization
Dockerfile
Linux containers

Multiple commands on docker ENTRYPOINT

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Running multiple startup commands in a container is common. You may need to wait for a dependency, run a migration, generate assets, and then launch the real service. The safest pattern is usually a small entrypoint script that performs setup and then uses exec to hand control to the main process. That keeps signal handling correct and prevents the container from being trapped behind a shell wrapper with awkward lifecycle behavior.

Know the Difference Between ENTRYPOINT and CMD

ENTRYPOINT defines the executable that always runs when the container starts. CMD provides default arguments that are appended unless the user overrides them.

dockerfile
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["gunicorn", "app.wsgi:application", "--bind", "0.0.0.0:8000"]

This is the cleanest structure when a startup script must prepare the environment and then hand off to the application.

A short shell script is usually better than cramming several commands into one ENTRYPOINT string.

bash
1#!/bin/sh
2set -eu
3
4echo "Running migrations"
5python manage.py migrate --noinput
6
7echo "Collecting static assets"
8python manage.py collectstatic --noinput
9
10echo "Starting server"
11exec "$@"

Dockerfile wiring:

dockerfile
1FROM python:3.12-slim
2WORKDIR /app
3COPY . /app
4COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
5RUN chmod +x /usr/local/bin/entrypoint.sh
6
7ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
8CMD ["gunicorn", "app.wsgi:application", "--bind", "0.0.0.0:8000"]

The critical line is exec "$@". Without it, the shell remains PID 1 and signal forwarding becomes much less reliable.

Why One-Line Shell Chains Get Fragile

It is possible to write an inline shell chain.

dockerfile
ENTRYPOINT ["sh", "-c", "python manage.py migrate && gunicorn app.wsgi:application"]

This works for simple cases, but it gets ugly quickly:

  • quoting becomes harder,
  • error handling becomes less clear,
  • signal behavior depends on how the shell remains in front,
  • and debugging or extension becomes painful.

As soon as startup logic is more than one short chain, a script is the better engineering choice.

Make Startup Steps Conditional When Needed

A script also makes it easy to control behavior by environment variables.

bash
1#!/bin/sh
2set -eu
3
4if [ "${WAIT_FOR_DB:-false}" = "true" ]; then
5  until nc -z "$DB_HOST" "$DB_PORT"; do
6    sleep 1
7  done
8fi
9
10if [ "${RUN_MIGRATIONS:-true}" = "true" ]; then
11  python manage.py migrate --noinput
12fi
13
14exec "$@"

This lets one image serve local, staging, and production use cases without duplicating Dockerfiles.

Avoid Treating One Container as a Full Process Supervisor

If you need several long-lived processes, the first question should be whether they should be separate containers instead. Containers are usually simpler to operate when they have one main process.

If you truly need multiple long-lived processes, use a minimal init system or real supervision carefully. But that should be an exception, not the default answer to “multiple commands”.

Test Shutdown Behavior, Not Just Startup Success

Many broken entrypoint setups appear fine during startup and only fail during stop or restart, because PID 1 behavior is wrong.

bash
1docker build -t myapp:local .
2cid=$(docker run -d myapp:local)
3sleep 3
4time docker stop "$cid"

If docker stop consistently hangs or times out, the entrypoint handoff is probably incorrect.

Idempotency Matters for Startup Logic

Migrations and initialization commands may run again when a container restarts. That means startup logic should be safe to repeat or at least fail in a clear, intentional way.

Treat entrypoint scripts as real production logic, not as disposable glue. The first time a restart loop happens under orchestration is the wrong time to discover that the script assumes it runs only once.

Common Pitfalls

  • Forgetting exec "$@" and breaking signal handling for the real application process.
  • Packing complex shell logic into a single ENTRYPOINT string.
  • Running several long-lived background processes without supervision.
  • Writing startup steps that are not safe under container restart.
  • Assuming startup success is enough without testing shutdown behavior.

Summary

  • Use an entrypoint script when a container must run several startup commands.
  • Keep ENTRYPOINT in exec form and hand off to the final process with exec.
  • Prefer scripts over long inline shell chains once logic becomes nontrivial.
  • Keep startup steps idempotent where possible.
  • Test both startup and shutdown behavior so PID 1 handling is correct.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.