DEV Community

Cover image for Docker for Absolute Beginners — Part 2
Md Enayetur Rahman
Md Enayetur Rahman

Posted on

Docker for Absolute Beginners — Part 2

Containerising a Tiny Express “/health” Service

In Part 1 we demystified Docker’s basics—what containers and images are, how to build and run them, and why they streamline development. Today we’ll build a micro‑service, turn it into an image, run it, and publish it to Docker Hub — step by gentle step.


Learning Aims

By the end of this post you will be able to:

  1. Write a minimal Dockerfile for a Node.js project.
  2. Build and run a container from that Dockerfile locally.
  3. Tag and push your image to Docker Hub so anyone can pull it.

1 The Project in 20 Lines

We keep the code tiny so we can focus on Docker itself:

simple-docker-express-backend/
├─ Dockerfile
├─ package.json
└─ index.js
Enter fullscreen mode Exit fullscreen mode
// index.js — ES‑module syntax
import express from "express";

const app  = express();
const port = 3000;

app.get("/health", (_req, res) => {
  res.json({ status: "OK" });
});

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});
Enter fullscreen mode Exit fullscreen mode

Everything this service does is reply { "status": "OK" } when you GET /health.


2 Dockerfile — Word by Word

Below is the complete file we’ll drop alongside index.js and package.json.

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Enter fullscreen mode Exit fullscreen mode

Let’s decode every single token in plain English:
| Line | Token | What it means |
|--------------------------|-------|---------------|
| FROM node:18-alpine | FROM | “Start my image from another one.” |
| | node:18-alpine | An official Node.js image running version 18 on the super‑small Alpine Linux distro. |
| WORKDIR /app | WORKDIR | “From now on, treat /app as my working directory in the image.” Docker will cd there before each subsequent instruction. |
| COPY package*.json ./ | COPY | “Copy files from my laptop → into the image.” |
| | package*.json | Both package.json and (if present) package-lock.json. The wildcard keeps the line short. |
| | ./ | Destination folder — here that working directory /app. |
| RUN npm install | RUN | “Execute a command while building the image.” |
| | npm install | Fetches and caches your project’s dependencies. |
| COPY . . | First . | Source: “everything in the current build‑context folder.” |
| | Second . | Destination: the current image folder (/app). |
| EXPOSE 3000 | EXPOSE | A hint saying “the app inside listens on port 3000.” (It does not publish the port — that happens at docker run time.) |
| CMD ["npm", "start"] | CMD | The default command Docker will run when you start a container from this image. |
| | [...] (JSON array) | Exec‑form — avoids an extra shell layer; here it runs the script defined in package.json. |


3 Building the Image Locally

Run this from the folder that contains the Dockerfile:

docker build -t simple-docker-express-backend .
Enter fullscreen mode Exit fullscreen mode

Word‑by‑word breakdown:

  • docker – the CLI binary that talks to the Docker Engine.
  • build – tell the engine to create an image from a Dockerfile.
  • -t – short for tag: give the result a human‑friendly name.
  • simple-docker-express-backend – our chosen image name (no spaces!).
  • . – “Use the current folder as the build context” (i.e. send its contents to the engine so the COPY steps work).

When it finishes you can list the image:

docker images | head -n 2
REPOSITORY                       TAG       IMAGE ID       CREATED          SIZE
simple-docker-express-backend    latest    abcdef123456   10 seconds ago   55MB
Enter fullscreen mode Exit fullscreen mode

4 Running the Container

docker run -p 3000:3000 simple-docker-express-backend
Enter fullscreen mode Exit fullscreen mode
  • docker run – create a new container from an image and start it.
  • -p 3000:3000 – “Publish container‑port 3000 on host‑port 3000.” The format is HOST:CONTAINER.
  • simple-docker-express-backend – which image to boot.

Open http://localhost:3000/health in your browser — you should see { "status": "OK" }.

Stop the container any time with Ctrl + C (running in foreground) or docker stop <container‑id> if you ran detached (-d).


5 Tagging & Pushing to Docker Hub

5.1 Log In

docker login
Enter fullscreen mode Exit fullscreen mode

Docker will store a token so you only type your credentials once.

5.2 Tag the Image

docker tag simple-docker-express-backend:latest   enayetsyl/simple-docker-express-backend:latest
Enter fullscreen mode Exit fullscreen mode
  • docker tag – create an alias for an existing image ID.
  • simple-docker-express-backend:latestsource image (latest is the default tag).
  • enayetsyl/simple-docker-express-backend:latesttarget name in the format USERNAME/REPO:TAG.

5.3 Push

docker push enayetsyl/simple-docker-express-backend:latest
Enter fullscreen mode Exit fullscreen mode
  • docker push – upload an image to a registry.
  • enayetsyl/simple-docker-express-backend:latest – what to upload. Docker CLI looks up the image locally, zips the layers, and streams them to Docker Hub. After a short wait you’ll find the repo at https://hub.docker.com/r/enayetsyl/simple-docker-express-backend.

6 Conclusion — What We Learned

✅ Writing a minimal Dockerfile for Node.js, line by line.

✅ Building an image with docker build.

✅ Running a container interactively with docker run -p HOST:CONTAINER.

✅ Tagging and pushing that image to Docker Hub so it’s shareable.

Happy container‑hacking! 🚀

Top comments (0)