What you need to do to build a Docker image

Building a Docker image means creating a file that contains your application, its dependencies, and the instructions Docker needs to run it. You do this by writing a Dockerfile — a text file with a list of commands — and then running the docker build command. Docker reads the Dockerfile line by line, executes each instruction, and saves the result as an image you can run anywhere Docker is installed.

The process has three main steps: write a Dockerfile that describes your application and what it needs, build the image from that file, and then run a container from that image to test it. Most of the work is in the Dockerfile itself — the build command is usually just one line.

Key Takeaways

  • A Dockerfile is a text file with commands that tell Docker how to package your application, starting with a base image like Python or Node.js.
  • The docker build command reads your Dockerfile and creates an image, which you name with a tag like myapp:1.0.
  • Common Dockerfile commands include FROM to set the base image, COPY to add your code, RUN to install dependencies, and CMD to set what runs when the container starts.
  • You test the image by running docker run with the image name, which starts a container and shows you whether your application works inside it.
  • Image size matters — using a smaller base image and removing unnecessary files keeps your image fast to download and run.

Creating your first Dockerfile

Start by creating a file named Dockerfile (no extension) in the root directory of your project. Open it in a text editor and begin with a FROM line that specifies a base image. The base image is a pre-built image that already has an operating system and often a programming language installed. For a Python application, you might use FROM python:3.11. For Node.js, use FROM node:18. Docker Hub hosts thousands of base images — search there for the language and version you need.

After the FROM line, add a WORKDIR command to set the directory where your application will live inside the container. For example, WORKDIR /app creates an /app folder and makes it the working directory. Then use COPY to copy your application files from your computer into the image. The command COPY . . copies everything from your current directory into the container's working directory.

Next, add a RUN command to install any dependencies your application needs. For Python, this is usually RUN pip install -r requirements.txt, which installs the packages listed in your requirements.txt file. For Node.js, use RUN npm install. Finally, add a CMD line that tells Docker what command to run when the container starts. For a Python script, this might be CMD ["python", "app.py"]. For Node.js, it might be CMD ["node", "server.js"].

A complete Dockerfile example

Here is a working Dockerfile for a simple Python application:

FROM python:3.11 WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD ["python", "app.py"]

This Dockerfile starts with Python 3.11, sets the working directory to /app, copies your requirements.txt file, installs the packages it lists, copies the rest of your application code, and then runs python app.py when the container starts. The order matters — Docker caches each layer, so putting COPY requirements.txt before COPY . . means Docker only re-installs dependencies if the requirements file changes, not every time you change your code.

For a Node.js application, the Dockerfile looks similar but uses Node commands:

FROM node:18 WORKDIR /app COPY package.json . RUN npm install COPY . . CMD ["node", "server.js"]

Building the image with docker build

Once your Dockerfile is written, open a terminal or command prompt in the directory where the Dockerfile sits. Run the command docker build -t myapp:1.0 .. The -t flag sets the image name and tag — myapp is the name, and 1.0 is the version. The period at the end tells Docker to look for the Dockerfile in the current directory.

Docker will then read your Dockerfile line by line. For each instruction, it creates a layer — a snapshot of the filesystem at that point. You will see output showing each step: downloading the base image, running commands, copying files. This can take a few minutes the first time, especially if the base image is large. Subsequent builds are faster because Docker reuses layers that have not changed.

If the build fails, Docker will show an error message telling you which line caused the problem. Common issues include a missing file (check that requirements.txt or package.json exists), a typo in a command, or a base image that does not exist. Fix the Dockerfile and run docker build again.

Testing your image by running a container

After the build completes, test the image by running a container from it. Use the command docker run myapp:1.0. Docker will start a container based on your image and execute the CMD instruction you specified in the Dockerfile. If your application is a web server, you will need to add a port mapping so you can reach it from your computer. Use docker run -p 8000:5000 myapp:1.0 to map port 8000 on your computer to port 5000 inside the container.

If the container starts and your application runs without errors, the image is working. If something goes wrong, you can run the container in interactive mode to debug. Use docker run -it myapp:1.0 /bin/bash to start a shell inside the container so you can explore the filesystem and run commands manually. This helps you understand what is happening inside the image.

Keeping your image small and efficient

Docker images can grow large quickly, which makes them slow to download and run. To keep your image lean, use a smaller base image when possible. Instead of FROM python:3.11, consider FROM python:3.11-slim, which removes unnecessary packages and is much smaller. For Node.js, FROM node:18-alpine uses Alpine Linux, a tiny base image that cuts size dramatically.

Another way to reduce size is to clean up after installing dependencies. In a Python Dockerfile, add RUN pip install -r requirements.txt && rm -rf /root/.cache/pip to remove the pip cache after installation. For Node.js, use RUN npm install && npm cache clean --force. You can also use a multi-stage build — write two Dockerfiles in one file, use the first to build your application, and copy only the final result into the second image. This leaves behind all the build tools and intermediate files.

Understanding Dockerfile commands you will see often

FROM specifies the base image and must be the first line. WORKDIR sets the working directory inside the container. COPY copies files from your computer into the image. RUN executes a command during the build, like installing packages. CMD sets the default command to run when the container starts — only one CMD per Dockerfile, and if you specify a command when running the container, it overrides this.

EXPOSE documents which ports your application listens on, though it does not actually open them — you still need the -p flag in docker run. ENV sets environment variables inside the container, useful for configuration. ARG lets you pass values into the build process, so you can build different versions of the image without editing the Dockerfile. ENTRYPOINT is similar to CMD but harder to override — use it when you want the container to always run a specific program.

Frequently Asked Questions

What is the difference between COPY and ADD in a Dockerfile?

COPY copies files from your computer into the image. ADD does the same thing but also handles URLs and automatically extracts compressed files. For most cases, use COPY — it is simpler and more predictable. Use ADD only when you specifically need to download a file or extract a tar archive.

Why does my docker build fail with "file not found"?

Docker builds from the directory you specify (usually the current directory, marked by the period in docker build -t myapp:1.0 .). Make sure the files you are copying actually exist in that directory. If you use COPY requirements.txt ., the file must be in the same folder as your Dockerfile. Use docker build --verbose to see exactly what Docker is looking for.

Can I build a Docker image without a Dockerfile?

No, Docker needs a Dockerfile to build an image. However, you can create an image from a running container using docker commit, though this is not recommended for production — it makes your image hard to reproduce and update. Always use a Dockerfile so others can see exactly how your image is built.

How do I push my image to Docker Hub so others can use it?

First, create an account on Docker Hub and log in with docker login. Then tag your image with your Docker Hub username: docker tag myapp:1.0 yourusername/myapp:1.0. Finally, push it with docker push yourusername/myapp:1.0. Others can then pull and run your image with docker run yourusername/myapp:1.0.

What does "layer" mean in Docker?

Each instruction in your Dockerfile creates a layer — a snapshot of the filesystem at that point. Docker stacks these layers on top of each other to build the final image. Layers are cached, so if you run docker build twice and nothing has changed, Docker reuses the cached layers and the build is instant. This is why the order of commands in your Dockerfile matters — putting commands that change often near the end keeps builds fast.