← All blogs
  • Dockerfiles
  • Docker
  • Cloud
  • CI/CD

Understanding Dockerfiles: FROM, RUN, COPY, ENV and CMD

Understand Dockerfile instructions FROM, RUN, COPY, ENV, and CMD, and how image construction differs from container startup.

Originally published on Medium ↗. Read the full article below.

If you’re learning Docker, one of the first concepts you’ll encounter is the Dockerfile.

Understanding Dockerfiles: FROM, RUN, COPY, ENV and CMD

A Dockerfile may initially look like a collection of unfamiliar instructions:

FROM
RUN
COPY
ENV
CMD

However, each instruction has a specific purpose. Once you understand how they work together, writing Dockerfiles becomes much easier.

What is a Dockerfile?

A Dockerfile is a set of instructions used to build a Docker image.

The basic workflow looks like this:

Dockerfile
    ↓
docker build
    ↓
Docker Image
    ↓
docker run
    ↓
Docker Container

You can think of a Dockerfile as a blueprint that defines the environment your application needs.

For example, a Dockerfile can tell Docker to:

  • Start with a Node.js environment
  • Create a working directory
  • Copy application files
  • Install dependencies
  • Define environment variables
  • Start the application

Let’s look at the most common instructions.

1. FROM — Choose the Base Image

A Dockerfile generally starts with the FROM instruction.

For a Node.js application:

FROM node:20

This tells Docker to use the Node.js 20 image as the starting point for our image.

Instead of starting with a minimal operating system and manually installing Node.js, we can use an existing image that already contains Node.js.

For example:

FROM node:20

provides a base environment in which Node.js is already installed.

In simple terms:

FROM defines the starting point for your image.

2. ENV — Define Environment Variables

The ENV instruction allows you to define environment variables:

ENV PORT=3000
ENV NODE_ENV=production

These variables are then available inside the container.

For example, a Node.js application can access the port using:

const port = process.env.PORT;

However, environment-specific configuration is often better provided when the container is run rather than permanently embedded in the image.

This is particularly useful for values such as:

  • Database connection strings
  • API configuration
  • Application settings
  • Development and production configuration

The important concept is:

ENV defines configuration that is available in the container environment.

3. RUN — Execute Commands During the Build

The RUN instruction executes commands while the Docker image is being built.

For example:

RUN mkdir /app

creates the /app directory inside the image.

Another common example is:

RUN npm install

which installs the application’s dependencies during the image build process.

You can have multiple RUN instructions:

RUN apt-get update
RUN npm install

The key point is that RUN happens during:

docker build

It is used primarily to prepare the image.

4. COPY — Copy Application Files into the Image

Your application files normally exist in your project directory on your machine.

For example:

my-app/
├── Dockerfile
├── package.json
├── package-lock.json
└── server.js

You can copy these files into the image using:

COPY . /app

This tells Docker to take files from the build context and place them in /app inside the image.

This is different from RUN.

For example:

RUN mkdir /app

executes a command as part of building the image.

Whereas:

COPY . /app

transfers files from the build context into the image.

A useful way to remember this is:

RUN executes a command; COPY transfers files into the image.

5. CMD — Define the Default Startup Command

The CMD instruction specifies the default command that should be executed when a container starts.

For a Node.js application:

CMD ["node", "server.js"]

This tells Docker:

When a container is started from this image, run node server.js.

The overall flow is:

docker build
     ↓
Docker Image
     ↓
docker run
     ↓
Container starts
     ↓
CMD executes
     ↓
node server.js

This is why CMD is commonly used to start the application's main process.

RUN vs CMD

One of the most important distinctions to understand when learning Docker is the difference between RUN and CMD.

RUN

RUN npm install

Executes during the image build process.

CMD

CMD ["node", "server.js"]

Executes when a container is started.

A simple way to remember the difference:

RUN prepares the image.

CMD starts the application.

For example:

docker build
     ↓
RUN npm install
     ↓
Docker Image
     ↓
docker run
     ↓
CMD ["node", "server.js"]
     ↓
Application starts

A Complete Example

Here is a simple Dockerfile for a Node.js application:

FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ENV PORT=3000
CMD ["node", "server.js"]

Let’s translate each instruction into plain English:

FROM node:20
→ Start with the Node.js 20 image.
WORKDIR /app
→ Set /app as the working directory.
COPY package*.json ./
→ Copy the package files into the image.
RUN npm install
→ Install the application's dependencies.
COPY . .
→ Copy the remaining application files.
ENV PORT=3000
→ Define the PORT environment variable.
CMD ["node", "server.js"]
→ Start the Node.js application when the container runs.

Dockerfile Cheat Sheet

InstructionPurposeFROMSelects the base imageENVDefines environment variablesRUNExecutes commands during image buildingCOPYCopies files into the imageCMDDefines the default command when the container startsWORKDIRSets the working directory

The Bigger Picture

A Dockerfile is essentially a way of describing the environment and instructions required to run an application.

Instead of manually setting up an environment every time, you define the setup once:

Choose a base environment
        ↓
Configure the environment
        ↓
Copy the application
        ↓
Install dependencies
        ↓
Define the startup command
        ↓
Build the Docker image
        ↓
Run the container

This makes application environments more consistent and reproducible across development, testing, and production.

Key Takeaways

If you’re just getting started with Docker, focus on these concepts first:

1. Image and container are different.

A Docker image is a packaged, immutable template. A container is a running instance of that image.

2. RUN and CMD serve different purposes.

RUN executes during image construction, while CMD defines what runs when the container starts.

3. A Dockerfile is a set of instructions.

It describes how to build an environment in which your application can run consistently.

Once you understand these fundamentals, concepts such as Docker Compose, volumes, networking, multi-stage builds, and container orchestration become much easier to learn.