Multi-Container App: Build & Push (Practical)
July 7, 2025
A worked example: a frontend (HTML/JS) plus a backend (Node.js/Express on port 5000), containerised and pushed to Docker Hub.
The full flow (in order)
- Docker Hub: create a public repository (hub.docker.com → Repositories → Create Repository).
- Clone the repo and
cdinto it. - Write a Dockerfile in each service folder (
backend,frontend). - Write docker-compose.yml in the repo root.
- Push the code to the repo:
git add .
git commit -m "Add Dockerfiles and docker-compose"
git push origin main
- Build & push the images:
docker login
docker compose build
docker compose push
To run/demo locally: docker compose up, then open the frontend at
http://localhost:8080.
The files
backend/Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5000
CMD ["npm", "start"]
frontend/Dockerfile (static site via nginx)
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80
docker-compose.yml (repo root)
version: "3.8"
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile
image: <dockerhub-username>/<name>-backend:latest
ports:
- "5000:5000"
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
image: <dockerhub-username>/<name>-frontend:latest
ports:
- "8080:80"
depends_on:
- backend
Gotchas (remember these!)
- If the file is named
DOCKERFILE(all caps), Docker won't auto-find it — it only looks forDockerfile. Either rename it, use-f DOCKERFILE, or setdockerfile: DOCKERFILEin Compose. - Port mapping is HOST:CONTAINER (e.g.
8080:80→ openlocalhost:8080). fetch("http://backend:5000")from the frontend only resolves inside the Docker network — the browser needshttp://localhost:5000.- Run
docker loginbeforedocker compose push, or you get access denied.
Related: [[docker-compose]], [[docker-hub-push-images]], [[dockerfile-instructions]].