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:
- Write a minimal Dockerfile for a Node.js project.
- Build and run a container from that Dockerfile locally.
- 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
// 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}`);
});
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"]
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 .
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
4 Running the Container
docker run -p 3000:3000 simple-docker-express-backend
- 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
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
- docker tag – create an alias for an existing image ID.
-
simple-docker-express-backend:latest – source image (
latest
is the default tag). - enayetsyl/simple-docker-express-backend:latest – target name in the format USERNAME/REPO:TAG.
5.3 Push
docker push enayetsyl/simple-docker-express-backend:latest
- 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)