NeuroAgent

CMD vs ENTRYPOINT: Key Differences Explained

Learn the crucial difference between CMD and ENTRYPOINT in Dockerfiles. Understand when to use each command, how they interact, and best practices for creating effective Docker containers.

Question

What is the difference between CMD and ENTRYPOINT in a Dockerfile?

In Dockerfiles, there are two commands that appear similar: CMD and ENTRYPOINT. I understand there must be a difference between them, otherwise it wouldn’t make sense to have two separate commands for the same purpose.

According to the documentation:

  • CMD: The main purpose of a CMD is to provide defaults for an executing container.
  • ENTRYPOINT: An ENTRYPOINT helps you to configure a container that you can run as an executable.

What is the key difference between these two Docker commands, and when should each be used?

NeuroAgent

The key difference between CMD and ENTRYPOINT in Dockerfiles lies in their behavior when overriding container commands at runtime: CMD provides default arguments that can be easily overridden, while ENTRYPOINT defines the main executable that runs consistently, with CMD supplying default arguments to that executable. This fundamental distinction makes ENTRYPOINT ideal for containers that need to run a specific program consistently, while CMD is perfect for providing flexible default parameters that users can customize.

Contents


Understanding CMD

The CMD instruction in a Dockerfile provides the default command and/or parameters for an executing container. There are three forms of CMD:

dockerfile
# Exec form (recommended)
CMD ["executable", "param1", "param2"]

# Shell form
CMD command param1 param2

# As default parameters for ENTRYPOINT
CMD ["param1", "param2"]

Key Characteristics of CMD:

  • Overrideable: You can easily override CMD when running the container
  • Multiple forms: Supports both exec and shell forms
  • Default arguments: Serves as default parameters when used with ENTRYPOINT
  • Only one CMD: A Dockerfile can only have one CMD instruction (the last one takes effect)

When you run a container like docker run my-image, Docker executes the CMD. However, if you provide additional arguments like docker run my-image ls -l, the CMD is completely replaced by your command.

dockerfile
# Example showing CMD override
FROM ubuntu:latest
CMD ["echo", "Hello World"]

Running this container:

bash
# Uses default CMD
docker run my-image
# Output: Hello World

# Overrides CMD
docker run my-image echo "Custom message"
# Output: Custom message

Understanding ENTRYPOINT

The ENTRYPOINT instruction configures a container that will run as an executable. It has the same syntax forms as CMD:

dockerfile
# Exec form (recommended)
ENTRYPOINT ["executable", "param1", "param2"]

# Shell form
ENTRYPOINT command param1 param2

Key Characteristics of ENTRYPOINT:

  • Consistent execution: The main executable defined in ENTRYPOINT always runs
  • Parameters via CMD: When used with CMD, CMD provides default parameters
  • Cannot be easily overridden: Requires special flags to override
  • Main purpose: Defines the container’s main executable

ENTRYPOINT is designed for containers that should always run the same executable, with the ability to modify command-line arguments.

dockerfile
# Example showing ENTRYPOINT behavior
FROM ubuntu:latest
ENTRYPOINT ["echo"]
CMD ["Hello World"]

Running this container:

bash
# Uses ENTRYPOINT with CMD parameters
docker run my-image
# Output: Hello World

# Appends to CMD parameters
docker run my-image "Custom message"
# Output: Custom message

# Overrides with --entrypoint
docker run --entrypoint cat my-image /etc/os-release
# Output: Ubuntu version info (since we overrode the ENTRYPOINT)

Key Differences

1. Override Behavior

Aspect CMD ENTRYPOINT
Default behavior Replaced when new command provided Executed with CMD as arguments
Override method Direct command replacement --entrypoint flag or complete command override
Flexibility High - easy to change entire command Lower - main executable stays the same

2. Usage Patterns

  • CMD: Best for providing default behavior that users might want to change entirely
  • ENTRYPOINT: Best for containers that should always run the same program

3. Execution Order

When both are used together:

  1. ENTRYPOINT defines the main executable
  2. CMD provides default arguments to that executable
  3. Runtime arguments are appended to CMD
dockerfile
# Combined usage example
FROM python:3.9-slim
ENTRYPOINT ["python", "-m"]
CMD ["http.server", "8000"]

Running this:

bash
# Uses defaults
docker run my-image
# Runs: python -m http.server 8000

# With custom port
docker run my-image http.server 9000
# Runs: python -m http.server 9000

Using CMD and ENTRYPOINT Together

When combining CMD and ENTRYPOINT, the recommended pattern is:

dockerfile
# Best practice: Use exec form for both
ENTRYPOINT ["executable"]
CMD ["default", "arguments"]

Why This Combination Works Well:

  1. Consistent executable: ENTRYPOINT ensures the same program always runs
  2. Flexible parameters: CMD provides defaults that users can modify
  3. Clean override: Users can change arguments without rewriting the entire command
dockerfile
# Practical example: A web server
FROM nginx:alpine
ENTRYPOINT ["nginx", "-g", "daemon off;"]
CMD ["-c", "/etc/nginx/nginx.conf"]

This setup:

  • Always runs nginx with daemon mode off
  • Uses a default configuration file
  • Allows users to specify different configs: docker run my-image -c /custom.conf

Shell Form Considerations:

The shell form has important differences:

dockerfile
# Shell form examples
ENTRYPOINT command param1 param2  # Always runs with shell
CMD command param1 param2       # Also runs with shell

Important: Shell form automatically executes with /bin/sh -c, which can cause issues with signal handling and process management.


Practical Examples

Example 1: Simple Web Application

dockerfile
# Node.js application
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ENTRYPOINT ["node", "app.js"]
CMD ["--port", "3000"]

Usage:

bash
# Default
docker run my-app

# Custom port
docker run my-app --port 8080

# Different command (overrides entirely)
docker run my-app node server.js

Example 2: Database Container

dockerfile
# PostgreSQL container
FROM postgres:13
ENV POSTGRES_USER=myuser
ENV POSTGRES_PASSWORD=mypassword
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["postgres"]

This follows the official PostgreSQL pattern where:

  • ENTRYPOINT runs the initialization script
  • CMD specifies the default database to start

Example 3: Development vs Production

dockerfile
# Multi-purpose container
FROM python:3.9
WORKDIR /app

# Development mode
CMD ["python", "app.py"]

# Production mode (build with different ARG)
ARG MODE=dev
RUN if [ "$MODE" = "prod" ]; then \
        pip install gunicorn; \
    else \
        pip install -e .; \
    fi

ENTRYPOINT ["python"]

Best Practices

1. Always Use Exec Form

dockerfile
# Good
CMD ["executable", "param"]

# Bad (shell form)
CMD executable param

2. Prefer ENTRYPOINT for Main Executable

Use ENTRYPOINT when your container should always run the same program.

3. Use CMD for Default Parameters

Supply default arguments via CMD when using ENTRYPOINT.

4. Handle Signal Properly

For long-running processes, ensure proper signal handling:

dockerfile
# Example with proper signal handling
FROM python:3.9
ENTRYPOINT ["tini", "--", "python", "app.py"]
CMD ["--debug"]

Where tini is a proper init process that forwards signals.

5. Document Override Behavior

Clearly document how users can override commands in your Dockerfile comments.


Common Mistakes to Avoid

1. Using Shell Form for Long-Running Processes

dockerfile
# Problem: Poor signal handling
ENTRYPOINT python app.py
CMD ["--debug"]

This can cause the container to not respond properly to SIGTERM.

2. Forgetting to Use Both Forms

dockerfile
# Problem: Inconsistent behavior
CMD ["python", "app.py"]
# No ENTRYPOINT defined

This makes it too easy for users to completely replace the intended command.

3. Mixing Forms Inconsistently

dockerfile
# Problem: Unexpected behavior
ENTRYPOINT python app.py --debug
CMD ["--production"]

This might not work as expected due to shell vs exec form mixing.

4. Not Testing Override Scenarios

Always test:

bash
# Test different override patterns
docker run my-image
docker run my-image --flag
docker run my-image --entrypoint cat

Conclusion

Understanding the difference between CMD and ENTRYPOINT is crucial for creating effective Docker containers. The key takeaways are:

  1. CMD provides flexible defaults that users can easily override entirely
  2. ENTRYPOINT defines the main executable that runs consistently
  3. Together they create powerful patterns where the main program stays the same but arguments can be customized
  4. Always prefer exec form for better process management and signal handling
  5. Test override scenarios to ensure your container behaves as expected

When designing your Docker images, consider: Should users be able to completely change what runs (use CMD alone), or should they only be able to modify parameters while keeping the main program consistent (use ENTRYPOINT + CMD)?

Sources

  1. Official Docker Documentation - CMD
  2. Official Docker Documentation - ENTRYPOINT
  3. Docker Best Practices - CMD vs ENTRYPOINT
  4. Stack Overflow - Understanding CMD and ENTRYPOINT