The Complete Docker Guide
Beginner to Advanced — For Students & Developers
Run Any Project on Any Machine — Without Installing Dependencies
By Abhishek Rathor [Ig :code.abhii07] | SYNTAX ERROR
1 Introduction to Docker
What is Docker?
Docker is an open-source platform that lets you package your application and all its
dependencies into a single unit called a Container. Think of it like a lunchbox — you put your
food (code), plate (runtime), and utensils (dependencies) all inside one box. Wherever you carry
that box, you can eat your lunch exactly the same way.
Analogy: Imagine you cooked a perfect biryani at home. But when you try to cook it at a
friend's house, the gas pressure is different, some spices are missing, and the result is totally
different. Docker is like bringing your entire kitchen along — so the biryani always tastes the
same!
Why is Docker Used?
Docker solves some of the most frustrating problems in software development:
• Ship applications faster with consistent environments
• Eliminate environment-related bugs
• Scale applications easily using containers
• Simplify collaboration between team members
• Works identically on development, testing, and production servers
The Real-World Problem Docker Solves
Every developer has heard this phrase at least once in their career:
"It works on my machine!"
Here is a common scenario without Docker:
1. You build a [Link] app using Node v18 on your laptop
2. Your friend tries to run it, but has Node v14 — app crashes
3. Your production server has Ubuntu, but you developed on macOS — different behavior
4. You need MongoDB v6, but the server has v4 installed — data issues
✅ With Docker, you define EVERYTHING your app needs inside a Dockerfile. When anyone
runs your Docker container, they get the exact same environment — same OS, same Node
version, same MongoDB version, same everything.
Common Problems Without Docker
Problem What Happens
Dependency version mismatch App crashes or behaves differently on another machine
Missing environment variables Features silently fail or throw cryptic errors
OS differences (Mac vs Linux) File paths, permissions, and commands differ
Manual setup for every developer Hours wasted just to get the project running
"Works on my machine" bug Impossible to reproduce — defeats the purpose of
testing
2 Core Concepts
Docker vs Virtual Machine (VM)
Both Docker and VMs allow you to run isolated environments, but they work very differently:
Feature Docker Container
Technology Uses your host OS kernel directly
Startup time Seconds (very fast)
Size MBs — very lightweight
Performance Near-native performance
Isolation Process-level isolation
Portability Extremely portable
Best for Microservices, apps, CI/CD pipelines
Feature Virtual Machine
Technology Runs its own full OS on a hypervisor
Startup time Minutes (slow)
Size GBs — very heavy
Performance Slower due to overhead
Isolation Full OS-level isolation
Portability Less portable, large disk images
Best for Running different OS, legacy systems
Simple Analogy: VM is like renting a full house. Docker container is like renting just a room in
a shared flat. You get your own private space, but share the building infrastructure.
Docker Image
A Docker Image is a read-only blueprint or template used to create containers. Think of it like a
recipe or a class in programming — you define it once, and you can create multiple instances
from it.
• Read-only: Images never change once built
• Layered: Built in layers for caching efficiency
• Shareable: Can be pushed to Docker Hub for others to use
• Versioned: Multiple versions (tags) can exist side by side
Analogy: Docker Image = A recipe card. You read the recipe (image), then cook the dish (run
the container). Multiple people can cook from the same recipe card!
Docker Container
A Docker Container is a running instance of a Docker Image. When you run an image, a
container is created. You can run multiple containers from the same image, and each one is
isolated from the others.
• Ephemeral: Containers can be started, stopped, deleted anytime
• Isolated: Each container has its own filesystem, network, and processes
• Lightweight: Shares the host OS kernel — very efficient
Analogy: Docker Image = Class definition in code. Docker Container = An object (instance)
created from that class. You can create many objects from one class!
Dockerfile
A Dockerfile is a text file containing a set of instructions to build a Docker Image. It tells Docker
exactly how to set up the environment for your application — what OS to use, what to install,
how to start the app.
# Example Dockerfile structure
FROM node:18-alpine # Base image
WORKDIR /app # Set working directory
COPY package*.json ./ # Copy package files
RUN npm install # Install dependencies
COPY . . # Copy all source code
EXPOSE 3000 # Document the port
CMD ["node", "[Link]"] # Start command
Docker Hub
Docker Hub is the official public registry (like GitHub, but for Docker Images). You can:
• Pull official images like node, python, mysql, nginx, redis
• Push your own images so others can use them
• Use private registries for company/team images
• Explore thousands of community-maintained images
Docker Hub URL: [Link] — Browse official images for Node, Python, Go,
MySQL, PostgreSQL, Redis, Nginx and more!
3 Step-by-Step Practical Guide
Step 1: Install Docker
Windows Installation
5. Go to [Link]
6. Download Docker Desktop for Windows
7. Run the installer and follow the wizard
8. Enable WSL 2 (Windows Subsystem for Linux) when prompted
9. Restart your computer
10. Open terminal and verify: docker --version
macOS Installation
11. Go to [Link]
12. Download Docker Desktop for Mac (choose Intel or Apple Silicon chip)
13. Open the .dmg file and drag Docker to Applications
14. Open Docker from Applications and follow setup
15. Open terminal and verify: docker --version
Linux (Ubuntu) Installation
# Update package list
sudo apt-get update
# Install required packages
sudo apt-get install ca-certificates curl gnupg lsb-release
# Add Docker's official GPG key
sudo mkdir -p /etc/apt/keyrings
curl -fsSL [Link] | sudo gpg \
--dearmor -o /etc/apt/keyrings/[Link]
# Set up the repository
echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/[Link]] \
[Link] \
$(lsb_release -cs) stable" | sudo tee /etc/apt/[Link].d/[Link] >
/dev/null
# Install Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli [Link] docker-compose-plugin
# Start Docker and enable auto-start
sudo systemctl start docker
sudo systemctl enable docker
# Run without sudo (optional but recommended)
sudo usermod -aG docker $USER
newgrp docker
# Verify installation
docker --version
docker run hello-world
Step 2: Create a Sample [Link] Project
We will build a simple [Link] web server that displays a message. Create a new project
folder:
# Create and enter project directory
mkdir my-docker-app
cd my-docker-app
Create [Link]
# Create [Link]
cat > [Link] << 'EOF'
{
"name": "my-docker-app",
"version": "1.0.0",
"description": "A simple [Link] app for Docker demo",
"main": "[Link]",
"scripts": {
"start": "node [Link]"
},
"dependencies": {
"express": "^4.18.2"
}
}
EOF
Create [Link] (Main App File)
// [Link]
const express = require('express');
const app = express();
const PORT = [Link] || 3000;
[Link]('/', (req, res) => {
[Link](`
<html>
<body style='font-family:Arial; text-align:center; margin-top:100px;'>
<h1> Hello from Docker!</h1>
<p>This app is running inside a Docker Container</p>
<p>Server Time: ${new Date().toISOString()}</p>
</body>
</html>
`);
});
[Link](PORT, () => {
[Link](`Server running on port ${PORT}`);
});
Step 3: Write a Dockerfile
Create a file named exactly 'Dockerfile' (no extension) in your project root:
# Dockerfile
# Step 1: Choose the base image
# alpine = lightweight Linux, node:18 = [Link] version 18
FROM node:18-alpine
# Step 2: Set the working directory inside the container
# All future commands will run from this path
WORKDIR /app
# Step 3: Copy package files FIRST (for Docker layer caching)
# This way, npm install only re-runs when [Link] changes
COPY package*.json ./
# Step 4: Install [Link] dependencies
RUN npm install --production
# Step 5: Copy the rest of your application code
COPY . .
# Step 6: Tell Docker which port the app listens on
# This is documentation — it doesn't actually open the port
EXPOSE 3000
# Step 7: Define the command to run when container starts
CMD ["node", "[Link]"]
Dockerfile Commands Explained
Command What It Does
FROM Specifies the base image to start from (like choosing
your OS + runtime)
WORKDIR Sets the working directory inside the container for all
commands
COPY Copies files from your local machine into the container
filesystem
RUN Executes a command during the image BUILD phase
(e.g., npm install)
EXPOSE Documents which port the container listens on
(informational)
CMD Specifies the default command to run when the
container STARTS
ENV Sets environment variables inside the container
ARG Defines build-time variables (only during build, not at
runtime)
VOLUME Creates a mount point for persistent data
ENTRYPOINT Sets the main executable (CMD provides default
arguments)
Create .dockerignore
Similar to .gitignore — tells Docker which files to EXCLUDE from the image:
# .dockerignore
node_modules
[Link]
.git
.gitignore
[Link]
.env
*.log
dist
.DS_Store
Why .dockerignore? Without it, COPY . . would copy node_modules (thousands of files) into
the image, making it huge. Always exclude node_modules and .git!
Step 4: Build the Docker Image
# Build the image
# -t = tag (name:version), . = use current directory as build context
docker build -t my-docker-app:1.0 .
# You will see output like:
# [1/5] FROM node:18-alpine
# [2/5] WORKDIR /app
# [3/5] COPY package*.json ./
# [4/5] RUN npm install
# [5/5] COPY . .
# Successfully built abc123def456
# Successfully tagged my-docker-app:1.0
# Verify the image was created
docker images
Step 5: Run the Docker Container
# Run the container
# -d = run in background (detached mode)
# -p 8080:3000 = map host port 8080 to container port 3000
# --name = give the container a friendly name
docker run -d -p 8080:3000 --name myapp my-docker-app:1.0
# Verify container is running
docker ps
# Expected output:
# CONTAINER ID IMAGE COMMAND STATUS PORTS
# a1b2c3d4e5f6 my-docker-app:1.0 'node [Link]' Up 30 seconds [Link]:8080-
>3000/tcp
Step 6: Port Mapping Explained
Port mapping is one of the most important Docker concepts. It connects your computer's port to
the container's port:
-p HOST_PORT:CONTAINER_PORT → -p 8080:3000 Host Port (8080) = The port on
YOUR machine (your laptop/server) Container Port (3000) = The port the app listens on INSIDE
the container Result: Opening localhost:8080 in your browser talks to the container's port 3000
Port mapping examples:
# Map same port numbers
docker run -p 3000:3000 my-app
# Map different port numbers
docker run -p 8080:3000 my-app
# Run multiple instances on different host ports
docker run -p 8080:3000 --name app1 my-app
docker run -p 8081:3000 --name app2 my-app
docker run -p 8082:3000 --name app3 my-app
Step 7: Access the Project in Browser
Open your web browser and navigate to:
[Link]
You should see: " Hello from Docker!" — your app is running inside a container!
# Useful commands while your container is running:
# View container logs (live)
docker logs -f myapp
# Execute a command inside the container
docker exec -it myapp sh
# Stop the container
docker stop myapp
# Start it again
docker start myapp
# Remove the container
docker rm myapp
4 Real-World Use Case
How to Share Your Project With a Friend
Now for the magic! There are two main ways to share your Docker project:
Option A: Share via Docker Hub (Recommended)
# Step 1: Create a free account at [Link]
# Step 2: Login to Docker Hub from terminal
docker login
# Enter your Docker Hub username and password
# Step 3: Tag your image with your Docker Hub username
# Format: docker tag LOCAL_IMAGE USERNAME/REPO_NAME:TAG
docker tag my-docker-app:1.0 yourUsername/my-docker-app:1.0
# Step 4: Push the image to Docker Hub
docker push yourUsername/my-docker-app:1.0
# Your image is now publicly available!
# Share this with your friend: yourUsername/my-docker-app:1.0
Option B: Share via Docker Archive File
# Save the image as a .tar file
docker save -o [Link] my-docker-app:1.0
# Send the .tar file to your friend (via USB, Google Drive, etc.)
# Your friend loads it on their machine:
docker load -i [Link]
# Then runs it:
docker run -d -p 8080:3000 my-docker-app:1.0
How Your Friend Runs It (Without Installing ANYTHING)
Your friend only needs to have Docker installed. That's it. Here is their complete setup:
# Your friend does ONLY these steps:
# Step 1: Pull your image from Docker Hub
docker pull yourUsername/my-docker-app:1.0
# Step 2: Run it
docker run -d -p 8080:3000 yourUsername/my-docker-app:1.0
# Step 3: Open browser
# [Link]
# That's it! No [Link] installation.
# No npm install. No dependency conflicts.
# It just works — same as on your machine!
This is the power of Docker! Your friend did NOT need to install [Link], npm, express, or
any other dependency. Everything is baked into the Docker image.
5 Important Docker Commands
Essential Commands Reference
docker build — Build an Image
Reads the Dockerfile and creates a new Docker Image:
# Basic build (uses Dockerfile in current directory)
docker build -t my-app .
# Build with specific tag/version
docker build -t my-app:2.0 .
# Build from a different directory
docker build -t my-app -f /path/to/Dockerfile .
# Build without cache (fresh build)
docker build --no-cache -t my-app .
# Build and see detailed output
docker build --progress=plain -t my-app .
docker run — Run a Container
Creates and starts a container from an image:
# Basic run
docker run my-app
# Run in background (detached)
docker run -d my-app
# Run with port mapping
docker run -d -p 8080:3000 my-app
# Run with a custom name
docker run -d --name mycontainer my-app
# Run with environment variables
docker run -d -e NODE_ENV=production -e PORT=5000 my-app
# Run with a volume (persistent storage)
docker run -d -v /host/path:/container/path my-app
# Run interactively (great for debugging)
docker run -it my-app sh
# Automatically remove container when it stops
docker run --rm my-app
docker ps — List Running Containers
# Show running containers only
docker ps
# Show ALL containers (including stopped)
docker ps -a
# Show only container IDs
docker ps -q
# Show containers with specific format
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
docker stop / start / restart — Control Containers
# Stop a container gracefully
docker stop mycontainer
# Stop multiple containers
docker stop container1 container2 container3
# Start a stopped container
docker start mycontainer
# Restart a container
docker restart mycontainer
# Force kill a container immediately
docker kill mycontainer
# Remove a stopped container
docker rm mycontainer
# Remove a running container (force)
docker rm -f mycontainer
# Remove ALL stopped containers
docker container prune
docker images — Manage Images
# List all local images
docker images
# Pull an image from Docker Hub
docker pull node:18-alpine
# Push image to Docker Hub
docker push yourUsername/my-app:1.0
# Remove an image
docker rmi my-app:1.0
# Remove ALL unused images
docker image prune -a
# Inspect image details
docker inspect my-app
# View image build history/layers
docker history my-app
Other Useful Commands
# View real-time logs
docker logs -f mycontainer
# Execute command in running container
docker exec -it mycontainer sh
# Copy files between container and host
docker cp mycontainer:/app/[Link] ./[Link]
docker cp ./[Link] mycontainer:/app/[Link]
# View resource usage (CPU, memory)
docker stats
# View all Docker system info
docker info
# Clean up everything unused
docker system prune -a
# View Docker networks
docker network ls
6 Advanced Concepts (Beginner Friendly)
Volumes — Persistent Data Storage
By default, data inside a container is lost when the container stops. Volumes solve this by
mapping a folder inside the container to a folder on your host machine.
Analogy: Think of the container as a hotel room and the volume as your personal locker. Even
when you check out (container stops), your belongings (data) remain in the locker.
# Mount a volume: -v HOST_PATH:CONTAINER_PATH
docker run -d \
-p 8080:3000 \
-v $(pwd)/data:/app/data \
--name myapp \
my-docker-app
# Named volumes (Docker manages the location)
docker volume create mydata
docker run -d -v mydata:/app/data my-docker-app
# List all volumes
docker volume ls
# Inspect a volume
docker volume inspect mydata
# Remove unused volumes
docker volume prune
Common Volume Use Cases
• Database data (MongoDB, MySQL, PostgreSQL) — so data persists after container
restart
• Application logs — access log files from your host machine
• Development hot-reload — mount source code so code changes reflect instantly
• Config files — inject configuration without rebuilding the image
Environment Variables
Environment variables let you configure your app without changing the code. This is essential
for secrets, API keys, database URLs, and configuration that differs between environments.
# Pass env variables with -e flag
docker run -d \
-e NODE_ENV=production \
-e DB_HOST=localhost \
-e DB_PASSWORD=secret123 \
-e API_KEY=your-api-key \
-p 3000:3000 \
my-app
# Use a .env file (recommended!)
docker run -d --env-file .env -p 3000:3000 my-app
# Sample .env file:
# NODE_ENV=production
# DB_HOST=localhost
# DB_PASSWORD=secret123
# Access in your [Link] code:
# const dbHost = [Link].DB_HOST;
# const port = [Link] || 3000;
Security Tip: Never hardcode secrets in your Dockerfile or image! Always pass them as
environment variables at runtime. Add .env to your .dockerignore and .gitignore files.
Docker Compose — Managing Multiple Containers
Real applications usually have multiple services — a web server, a database, a cache, maybe a
message queue. Docker Compose lets you define and run all of them with a single YAML file.
Example: [Link] App + MongoDB + Redis
# [Link]
version: '3.8'
services:
# Your [Link] application
app:
build: .
ports:
- '8080:3000'
environment:
- NODE_ENV=production
- MONGO_URI=mongodb://mongodb:27017/mydb
- REDIS_URL=redis://redis:6379
depends_on:
- mongodb
- redis
volumes:
- ./logs:/app/logs
# MongoDB database
mongodb:
image: mongo:6
ports:
- '27017:27017'
volumes:
- mongo_data:/data/db
environment:
- MONGO_INITDB_DATABASE=mydb
# Redis cache
redis:
image: redis:7-alpine
ports:
- '6379:6379'
# Named volumes for persistence
volumes:
mongo_data:
Docker Compose Commands
# Start all services (build images if needed)
docker compose up -d
# Stop all services
docker compose down
# Stop and remove volumes (WARNING: deletes data!)
docker compose down -v
# Rebuild images and restart
docker compose up -d --build
# View logs for all services
docker compose logs -f
# View logs for a specific service
docker compose logs -f app
# Scale a service (run 3 instances of app)
docker compose up -d --scale app=3
# Run a one-off command
docker compose exec app sh
Docker Compose is the #1 tool for local development! Define your entire stack (app +
database + cache) in one file. Share it with your team — everyone gets the same environment
with one command: docker compose up
7 Common Mistakes
Mistakes Every Docker Beginner Makes
Common Mistake How to Fix It
Using latest tag everywhere Always use specific versions: node:18-alpine not
node:latest
Copying node_modules into image Add node_modules to .dockerignore file
Port conflict error Check if port is in use: lsof -i :8080 — use a different
host port
Running as root user Add USER node in Dockerfile to run as non-root for
security
Not using .dockerignore Create .dockerignore to exclude .git,
node_modules, .env files
Putting RUN commands last Put rarely-changing commands early for better layer
caching
Forgetting -d flag Without -d the container runs in foreground, blocking
your terminal
Image vs Container confusion Image = recipe (static). Container = cooked dish
(running instance)
Storing secrets in Dockerfile Use environment variables or Docker secrets, never
hardcode in Dockerfile
Large image sizes Use alpine base images and multi-stage builds to reduce
size
Port Conflict Error — How to Debug
# Error: Bind for [Link]:8080 failed: port is already allocated
# Find what's using the port (Linux/Mac)
lsof -i :8080
sudo netstat -tulpn | grep :8080
# Find what's using the port (Windows)
netstat -ano | findstr :8080
# Solution 1: Use a different host port
docker run -p 9090:3000 my-app
# Solution 2: Stop the container using the port
docker stop container_using_8080
8 Best Practices
1. Use Lightweight Base Images
# ❌ Heavy — full OS with many unused packages
FROM node:18
# ✅ Lightweight — Alpine Linux, much smaller
FROM node:18-alpine
# Image size comparison:
# node:18 = ~1 GB
# node:18-alpine = ~180 MB
# Much faster to download and push!
2. Use Multi-Stage Builds (Advanced)
Multi-stage builds let you use multiple FROM statements to create smaller final images. Perfect
for compiled languages or when you need build tools that aren't needed at runtime.
# Multi-stage Dockerfile example
# Stage 1: Build stage (has all dev dependencies)
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci # Clean install
COPY . .
RUN npm run build # Build the app
# Stage 2: Production stage (only what's needed to run)
FROM node:18-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY [Link] .
CMD ["node", "dist/[Link]"]
# Result: Final image has no dev tools, much smaller!
3. Optimize Layer Caching
Docker caches each Dockerfile step as a layer. If a layer changes, all layers after it are rebuilt.
Structure your Dockerfile to maximize caching:
# ❌ Bad — code changes invalidate npm install cache
FROM node:18-alpine
WORKDIR /app
COPY . . # Copies everything — cache busted on ANY change!
RUN npm install
CMD ["node", "[Link]"]
# ✅ Good — npm install is cached unless [Link] changes
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./ # Only copy package files first
RUN npm install # This layer is cached!
COPY . . # Source changes don't bust npm cache
CMD ["node", "[Link]"]
4. Security Best Practices
# Run as non-root user
FROM node:18-alpine
WORKDIR /app
COPY --chown=node:node package*.json ./
RUN npm ci --production
COPY --chown=node:node . .
USER node # Switch to non-root user
CMD ["node", "[Link]"]
# Never store secrets in images
# ❌ Bad
ENV API_KEY=mysecretkey123
# ✅ Good — pass at runtime
docker run -e API_KEY=mysecretkey123 my-app
# Use Docker secrets for production
docker secret create api_key [Link]
5. Tag Images Properly
# ❌ Avoid using 'latest' in production
docker pull my-app:latest
# ✅ Always use specific versions
docker pull my-app:1.2.3
docker pull my-app:2024-01-15
# Good tagging strategy:
docker build -t myapp:1.0.0 .
docker build -t myapp:1.0 .
docker build -t myapp:latest .
# Using git commit hash for traceability:
GIT_HASH=$(git rev-parse --short HEAD)
docker build -t myapp:${GIT_HASH} .
Complete Real Project — [Link] REST API
Project: A Simple REST API with Docker
Let's build a complete, production-ready [Link] REST API with Docker. This project is suitable
for portfolios and real-world use.
Project Structure
my-api/
├── Dockerfile
├── [Link]
├── .dockerignore
├── .[Link]
├── [Link]
├── [Link]
└── src/
├── [Link]
├── routes/
│ └── [Link]
└── data/
└── [Link]
[Link]
{
"name": "my-api",
"version": "1.0.0",
"description": "REST API with Docker",
"main": "src/[Link]",
"scripts": {
"start": "node src/[Link]",
"dev": "nodemon src/[Link]"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5"
},
"devDependencies": {
"nodemon": "^3.0.2"
}
}
src/[Link]
const express = require('express');
const cors = require('cors');
const usersRouter = require('./routes/users');
const app = express();
const PORT = [Link] || 3000;
[Link](cors());
[Link]([Link]());
// Health check endpoint
[Link]('/health', (req, res) => {
[Link]({
status: 'healthy',
environment: [Link].NODE_ENV || 'development',
uptime: [Link](),
timestamp: new Date().toISOString()
});
});
// Routes
[Link]('/api/users', usersRouter);
// Root endpoint
[Link]('/', (req, res) => {
[Link]({
message: ' Welcome to Dockerized REST API!',
endpoints: {
health: 'GET /health',
users: 'GET /api/users',
user: 'GET /api/users/:id',
create: 'POST /api/users',
}
});
});
[Link](PORT, '[Link]', () => {
[Link](` Server running on port ${PORT}`);
[Link](` Environment: ${[Link].NODE_ENV || 'development'}`);
});
src/routes/[Link]
const express = require('express');
const router = [Link]();
// In-memory data (use a database in production!)
let users = [
{ id: 1, name: 'Alice Johnson', email: 'alice@[Link]', role: 'admin' },
{ id: 2, name: 'Bob Smith', email: 'bob@[Link]', role: 'user' },
{ id: 3, name: 'Charlie Dev', email: 'charlie@[Link]', role: 'user' },
];
// GET all users
[Link]('/', (req, res) => {
[Link]({ success: true, count: [Link], data: users });
});
// GET single user
[Link]('/:id', (req, res) => {
const user = [Link](u => [Link] === parseInt([Link]));
if (!user) return [Link](404).json({ success: false, message: 'User not
found' });
[Link]({ success: true, data: user });
});
// POST create user
[Link]('/', (req, res) => {
const { name, email, role = 'user' } = [Link];
if (!name || !email) {
return [Link](400).json({ success: false, message: 'Name and email
required' });
}
const newUser = { id: [Link] + 1, name, email, role };
[Link](newUser);
[Link](201).json({ success: true, data: newUser });
});
[Link] = router;
Dockerfile (Production-Ready)
# Production-ready Dockerfile
FROM node:18-alpine
# Install dumb-init for proper signal handling
RUN apk add --no-cache dumb-init
# Set environment
ENV NODE_ENV=production
# Set working directory
WORKDIR /app
# Copy and install dependencies
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# Copy source code
COPY --chown=node:node . .
# Switch to non-root user
USER node
# Expose port
EXPOSE 3000
# Use dumb-init to handle signals properly
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["node", "src/[Link]"]
[Link]
version: '3.8'
services:
api:
build:
context: .
dockerfile: Dockerfile
ports:
- '8080:3000'
environment:
- NODE_ENV=production
- PORT=3000
restart: unless-stopped
healthcheck:
test: ['CMD', 'wget', '--quiet', '--tries=1', '--spider',
'[Link]
interval: 30s
timeout: 10s
retries: 3
volumes:
- ./logs:/app/logs
# Uncomment to add MongoDB:
# mongodb:
# image: mongo:6-jammy
# ports:
# - '27017:27017'
# volumes:
# - mongo_data:/data/db
# volumes:
# mongo_data:
.dockerignore
node_modules
[Link]
.git
.gitignore
[Link]
.env
*.env
logs
*.log
.DS_Store
coverage
.nyc_output
dist
*.md
Build and Run Commands
# === Using Docker directly ===
# Build the image
docker build -t my-api:1.0 .
# Run the container
docker run -d \
-p 8080:3000 \
-e NODE_ENV=production \
--name my-api \
my-api:1.0
# Test the API
curl [Link]
curl [Link]
curl [Link]
curl [Link]
# Create a new user
curl -X POST [Link] \
-H 'Content-Type: application/json' \
-d '{"name":"Dave","email":"dave@[Link]"}'
# === Using Docker Compose ===
# Start all services
docker compose up -d
# View logs
docker compose logs -f api
# Stop everything
docker compose down
Expected API Responses
# GET /
{
"message": " Welcome to Dockerized REST API!",
"endpoints": { ... }
}
# GET /health
{
"status": "healthy",
"environment": "production",
"uptime": 42.5
}
# GET /api/users
{
"success": true,
"count": 3,
"data": [
{ "id": 1, "name": "Alice Johnson", "email": "alice@[Link]" },
...
]
}
9 Summary & Interview Guide
Step-by-Step Recap
Step What You Did
1. Install Docker Set up Docker Desktop on Windows/Mac or Docker
Engine on Linux
2. Create project Built a [Link] Express app with a proper project
structure
3. Write Dockerfile Defined the environment: base image, working dir,
dependencies, start command
4. Create .dockerignore Excluded unnecessary files from the image
(node_modules, .git, .env)
5. Build image docker build -t my-app . — created a portable Docker
image
6. Run container docker run -d -p 8080:3000 my-app — started the
containerized app
7. Access in browser Opened localhost:8080 — saw the app running from
inside Docker
8. Push to Docker Hub docker push username/my-app — shared the image with
the world
9. Friend pulls & runs docker pull + docker run — friend runs with zero setup!
10. Docker Compose docker compose up — orchestrated multi-service
applications
Interview-Ready Explanation
If an interviewer asks 'Explain Docker in simple terms', here is a perfect answer:
"Docker is a containerization platform that packages an application and all its dependencies
— runtime, libraries, configuration — into a single, self-contained unit called a container. Unlike
virtual machines, containers share the host OS kernel, making them lightweight and fast. A
Dockerfile defines the recipe to build an image, and containers are running instances of those
images. Docker solves the classic 'works on my machine' problem by ensuring consistent
environments across development, testing, and production."
Key Terms Cheat Sheet
Term One-Line Definition
Docker Platform to build, ship, and run containers
Container A running isolated instance of a Docker image
Image A read-only blueprint/template to create containers
Dockerfile Script of instructions to build a Docker image
Docker Hub Public registry to store and share Docker images
Volume Persistent storage that survives container restarts
Port Mapping Connecting host port to container port (-p host:container)
Docker Compose Tool to define and run multi-container applications
Registry Storage location for Docker images (Hub, ECR, GCR,
etc.)
Layer Each Dockerfile instruction creates a cached layer in the
image
Happy Dockerizing!
You now have everything you need to containerize any project
By Abhishek Rathor [Ig :code.abhii07] | SYNTAX ERROR.