Writing Your First Dockerfile: A Beginner’s Guide

Introduction

A Dockerfile is a script of instructions that tells Docker how to build an image for your application. In this tutorial, you’ll learn how to write a basic Dockerfile from scratch, understand what each line does, and build a working Docker image.

Step 1: Create a Project Directory

Start by creating a directory for your sample app. For this example, we’ll use a basic Node.js app.

mkdir hello-docker
cd hello-docker

Step 2: Create a Simple Node.js App

Create a file named index.js and add the following code:

const http = require('http');

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello from Docker!');
});

server.listen(3000, () => {
  console.log('Server running on port 3000');
});

Then, initialize the project and add a package.json file:

npm init -y

Step 3: Create the Dockerfile

Now create a file named Dockerfile (no file extension). Paste the following inside:

# Use an official Node runtime as the base image
FROM node:18

# Set the working directory
WORKDIR /usr/src/app

# Copy package.json and install dependencies
COPY package*.json ./
RUN npm install

# Copy the application code
COPY . .

# Expose the app's port
EXPOSE 3000

# Start the app
CMD ["node", "index.js"]

What each line does:

  • FROM: Sets the base image for Node.js 18.
  • WORKDIR: Creates and sets the working directory.
  • COPY: Moves files from your machine into the image.
  • RUN: Executes commands—in this case, installs dependencies.
  • EXPOSE: Declares the network port your app will use.
  • CMD: Sets the command to run the app.

Step 4: Build and Run Your Image

Now you’re ready to build the image:

docker build -t hello-docker .

And run it using:

docker run -p 3000:3000 hello-docker

You should see the message: Server running on port 3000. Visit http://localhost:3000 in your browser.

Conclusion

You’ve just written your first Dockerfile and successfully built and run a Docker container. With this foundation, you can now Dockerize more complex apps, use multi-stage builds, and prepare your containers for deployment in production environments.