Difference Between RUN and CMD in a Dockerfile

TLDR
In a Dockerfile, RUN is used to execute commands during the image build process, creating layers in the final image. CMD specifies the default command to run when a container starts. Use RUN for building the image and CMD for defining the container's runtime behavior.
When writing a Dockerfile, understanding the difference between RUN and CMD matters for building efficient and functional Docker images. While both are used to execute commands, they serve different purposes. This guide will explain the differences and provide examples to help you use them effectively.
What is RUN?
The RUN instruction is used to execute commands during the image build process. Each RUN command creates a new layer in the Docker image. It is typically used for installing software, setting up the environment, or performing other tasks required to build the image.
Example
Here is an example of using RUN in a Dockerfile:
# Dockerfile
FROM ubuntu:20.04
# Install curl
RUN apt-get update && apt-get install -y curl
In this example:
- The
RUNinstruction updates the package list and installscurl. - The resulting image includes
curlas part of its filesystem.
Why It Matters
- Build-Time Execution: Commands in
RUNare executed during the build process, not when the container starts. - Layer Creation: Each
RUNinstruction creates a new layer, which can be cached to speed up subsequent builds.
What is CMD?
The CMD instruction specifies the default command to run when a container starts. Unlike RUN, it does not execute during the build process. Instead, it defines the container's runtime behavior.
Example
Here is an example of using CMD in a Dockerfile:
# Dockerfile
FROM node:16
# Set the default command to run the application
CMD ["node", "app.js"]
In this example:
- The
CMDinstruction specifies that the container should runnode app.jswhen it starts. - If a different command is provided at runtime, it will override the
CMDinstruction.
Why It Matters
- Runtime Execution:
CMDdefines what the container does when it starts. - Overridable: The
CMDinstruction can be overridden by specifying a command when running the container.
Key Differences Between RUN and CMD
| Feature | RUN |
CMD |
|---|---|---|
| Purpose | Executes commands during build | Specifies default runtime command |
| Execution Time | Build time | Runtime |
| Layer Creation | Creates a new image layer | Does not create a new layer |
| Overridable | No | Yes |
Combining RUN and CMD
You can use both RUN and CMD in the same Dockerfile to build the image and define its runtime behavior.
Example
# Dockerfile
FROM python:3.9
# Install dependencies
RUN pip install flask
# Set the default command
CMD ["python", "app.py"]
In this example:
- The
RUNinstruction installs Flask during the build process. - The
CMDinstruction specifies that the container should runpython app.pywhen it starts.
How Do You Run More Than One Command When a Container Starts?
CMD starts one process. To run a setup step first, wrap the commands in a shell, and start the long-running process with exec:
FROM nginx:1.30
# exec replaces the shell, so nginx becomes PID 1 and gets the stop signal
CMD ["sh", "-c", "echo setup done && exec nginx -g 'daemon off;'"]
Without exec, the shell stays PID 1 and nginx runs as its child. docker stop sends SIGTERM to PID 1, the shell does not pass it on, and after 10 seconds Docker kills everything with SIGKILL. We built the image twice, as sh-exec with exec and sh-plain without it, and looked at PID 1 in each container:
Then we timed docker stop on each one. sh-plain took 10 seconds and exited with code 137, which means it was killed and nginx never shut down cleanly. sh-exec stopped at once with exit code 0. In Compose, command: replaces the image's CMD and follows the same rule: command: sh -c "echo warming cache && exec python -m http.server 8000".
Two cases need more than a shell line:
- One-off steps, such as database migrations. Put them in a separate service that runs and exits, and make the app wait for it with
depends_on: { migrate: { condition: service_completed_successfully } }. The app then starts only if the migration exited with code 0. Dockerfile vs Compose file shows the full file. - Two long-running processes, such as a web server and a queue worker. Run them as two containers from the same image with different commands. A process manager such as supervisord inside one container works, but then Docker sees one process and cannot restart or scale the two parts on their own.
For longer setup logic, copy a script into the image and use it as the ENTRYPOINT, ending with exec "$@" so the CMD still runs as PID 1. CMD and ENTRYPOINT explains how the two instructions combine.
Best Practices
- Minimize Layers: Combine multiple commands in a single
RUNinstruction to reduce the number of layers. - Use CMD for Defaults: Use
CMDto define the default behavior of the container, but allow it to be overridden if needed. - Avoid Hardcoding: Avoid hardcoding sensitive information like credentials in
RUNorCMDinstructions.
By understanding the differences between RUN and CMD, you can write more efficient and functional Dockerfiles, ensuring your containers behave as expected.
Related Resources
- How Do I Make a Comment in a Dockerfile?: write clearer Dockerfiles
- Advanced Docker Features: multi-stage builds, BuildKit, and more
- Docker Security Best Practices: harden your containers
- Docker Image Optimization: build smaller, faster images
- Introduction to Docker: Building Custom Images: full guide to Dockerfiles
- Docker Flashcards: review Docker concepts
Try it hands-on
Run the commands from this article in the browser. Nothing to install.
Docker Terminal Simulator
Practice Docker commands in an interactive browser terminal. Learn images, containers, logs, exec, Dockerfile builds, volumes, networks, and Docker Compose with a live Docker daemon visualization.
Container Security Challenge
Six real docker run commands, each carrying a flag that hands an attacker the host. Click the dangerous ones, then see the actual escape path and what to grant instead. Covers the Docker socket, --privileged, host PID namespace, SYS_ADMIN and unconfined AppArmor, plus one command with nothing wrong with it.
We earn commissions when you shop through the links below.
Bitrise
Managed runners for GitHub Actions
Bitrise Build Hub runs your GitHub Actions jobs on M4 Macs and AMD Linux machines. Change runs-on and keep your workflows, secrets and logs on GitHub.
Atomsized
AWS platform engineering and GitOps
Design and automation for reliable AWS and Kubernetes platforms, safer delivery workflows, and preview and UAT environments your engineers can understand and own.
DigitalOcean
Cloud infrastructure for developers
Simple, reliable cloud computing designed for developers
DevDojo
Developer community & tools
Join a community of developers sharing knowledge and tools
SMTPfast
Developer-first email API
Send transactional and marketing email through a clean REST API. Detailed logs, webhooks, and embeddable signup forms in one dashboard.
QuizAPI
Developer-first quiz platform
Build, generate, and embed quizzes with a powerful REST API. AI-powered question generation and live multiplayer.
Want to support DevOps Daily and reach thousands of developers?
Become a SponsorFound an issue?
Related Posts
Also worth your time on this topic
Docker: Name is Already in Use by Container
Encountering the 'name is already in use by container' error in Docker? Learn why it happens and how to resolve it effectively.
Dockerfile Best Practices
What is the difference between a Docker image and a container? What are some Dockerfile best practices?
junior
Docker Security Hardening Checklist
Comprehensive security checklist for hardening Docker containers, images, and runtime environments.
60-90 minutes