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?
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
- Understanding ENTRYPOINT
- Key Differences
- Using CMD and ENTRYPOINT Together
- Practical Examples
- Best Practices
- Common Mistakes to Avoid
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:
# 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.
# Example showing CMD override
FROM ubuntu:latest
CMD ["echo", "Hello World"]
Running this container:
# 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:
# 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.
# Example showing ENTRYPOINT behavior
FROM ubuntu:latest
ENTRYPOINT ["echo"]
CMD ["Hello World"]
Running this container:
# 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:
- ENTRYPOINT defines the main executable
- CMD provides default arguments to that executable
- Runtime arguments are appended to CMD
# Combined usage example
FROM python:3.9-slim
ENTRYPOINT ["python", "-m"]
CMD ["http.server", "8000"]
Running this:
# 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:
# Best practice: Use exec form for both
ENTRYPOINT ["executable"]
CMD ["default", "arguments"]
Why This Combination Works Well:
- Consistent executable: ENTRYPOINT ensures the same program always runs
- Flexible parameters: CMD provides defaults that users can modify
- Clean override: Users can change arguments without rewriting the entire command
# 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:
# 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
# Node.js application
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ENTRYPOINT ["node", "app.js"]
CMD ["--port", "3000"]
Usage:
# 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
# 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
# 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
# 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:
# 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
# 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
# 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
# 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:
# 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:
- CMD provides flexible defaults that users can easily override entirely
- ENTRYPOINT defines the main executable that runs consistently
- Together they create powerful patterns where the main program stays the same but arguments can be customized
- Always prefer exec form for better process management and signal handling
- 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)?