← Back to Home
🐳

Writing a Dockerfile

Turn your own application into a portable image, one instruction at a time.

⏱11 min readπŸ“šDevOps Fundamentals

Running other people’s images is useful, but the real power is packaging your own app. A Dockerfile is a plain-text recipe that tells Docker how to build your image, step by step.


🎯 Learning Objectives

By the end of this lesson you will:


The Core Instructions

Instruction What it does
FROM The base image to build on
WORKDIR Set the working directory inside the image
COPY Copy files from your project into the image
RUN Execute a command at build time (install deps)
EXPOSE Document which port the app listens on
CMD The command that runs when the container starts

A Real Dockerfile

Here’s a complete Dockerfile for a small Node.js app:

dockerfile
# 1. Start from an official, slim base image
FROM node:20-slim

# 2. Set the working directory
WORKDIR /app

# 3. Copy dependency manifests first (better caching)
COPY package*.json ./

# 4. Install dependencies
RUN npm install --production

# 5. Copy the rest of the source code
COPY . .

# 6. Document the port the app uses
EXPOSE 3000

# 7. Command to start the app
CMD ["node", "server.js"]

πŸ’‘ Why copy package.json first?

Docker caches each instruction as a layer. By copying package.json and installing deps before copying your code, Docker only re-runs npm install when your dependencies change β€” not on every code edit. This makes rebuilds dramatically faster.


Build and Run Your Image

bash
docker build -t myapp:1.0 .        # build, tag it myapp:1.0
docker run -d -p 3000:3000 myapp:1.0
bash β€” 80Γ—24
student@devops:~$docker build -t myapp:1.0 .

The -t flag tags the image with a name and version, so you can run and share it easily.


Best Practices

⚠ Don't run as root

By default containers run as root, which is risky. Add a non-root user and switch to it with USER appuser before your CMD. It’s a small change that greatly reduces the blast radius of a compromise.

A few habits that separate good images from bad:

dockerfile
# Multi-stage build: build stage + tiny runtime stage
FROM node:20 AS build
WORKDIR /app
COPY . .
RUN npm ci && npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html

πŸ§ͺ Hands-on Lab

πŸ“

Containerise a Static Site

  1. Create an index.html with any content
  2. Write a Dockerfile based on nginx:alpine that copies it into the web root
  3. Build the image as mysite:1.0 and run it on port 8080

🧠 Knowledge Check

Knowledge Check

Why do experienced engineers copy package.json and install dependencies before copying the rest of the code?

Knowledge Check

What does a multi-stage build achieve?


πŸ’Ό Interview Preparation

Interview Q&A

How would you reduce the size of a Docker image?


Summary

You can now write a Dockerfile, build and tag your own image, and apply best practices for small, cached, secure builds. Next, you’ll run multiple containers together β€” app plus database β€” using Docker Compose.

Up Next

Multi-Container Apps with Docker Compose

You've mastered this lesson. Continue your journey to becoming a DevOps Engineer.

Start Next Lesson→