🚫 RESTRICTED — DO NOT COPY 🚫
This document is for personal study purposes only.
Copying, reproducing, sharing, or distributing any content from this material is strictly prohibited.
⚖️ Any unauthorized use of this content will result in immediate legal action.
The author reserves all rights and will pursue copyright infringement claims to the fullest extent of the law.
Detailed study notes on Docker: what it is, Dockerfiles, container management, image handling, lifecycle, restart policies, volumes, networks, environment variables, and registry operations.
- What is Docker?
- Docker Architecture
- Dockerfile — Build Your Own Image
- docker run — All Important Flags
- What Could Go Wrong With Your App?
- Docker Container Commands
- Docker Container Lifecycle
- Docker Container Restart Policies
- Docker Image Commands
- Image Layers — How Docker Images Work
- Environment Variables in Docker
- Docker Volumes — Persistent Data
- Docker Networks
- Dangling Images
- Push Image to Docker Hub
- Pull Image from Docker Hub
- Quick Reference Cheat Sheet
Docker is a platform that lets you package your application and all its dependencies into a single unit called a container, which can run identically on any machine that has Docker installed.
Without Docker:
"It works on my machine!" ← classic problem
Dev machine has Node 18, production has Node 16 → app breaks
With Docker:
You package Node 18 + your code + dependencies inside the container
The container runs the same everywhere: dev, staging, production
| Container | Virtual Machine | |
|---|---|---|
| Includes | App + dependencies + OS libraries | App + dependencies + full OS |
| Size | Megabytes | Gigabytes |
| Startup time | Seconds (or milliseconds) | Minutes |
| Resource usage | Shares host OS kernel | Needs its own kernel |
| Isolation | Process-level | Full OS-level |
Key insight: Containers share the host OS kernel — they are not full virtual machines. This makes them lightweight and fast.
| Term | Meaning |
|---|---|
| Image | A read-only blueprint/template. Like a class in OOP. |
| Container | A running instance of an image. Like an object created from a class. |
| Dockerfile | A script of instructions that tells Docker how to build an image. |
| Docker Hub | A public registry where images are stored and shared (like GitHub for images). |
| Registry | A server that stores Docker images (Docker Hub, AWS ECR, GitHub Container Registry). |
| Docker Daemon | The background service that manages images, containers, volumes, and networks. |
| Docker CLI | The command-line tool (docker) you use to talk to the daemon. |
You (terminal)
│
│ docker build / docker run / docker pull
▼
Docker CLI ──────────────────► Docker Daemon (dockerd)
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
[ Images ] [ Containers ] [ Volumes ]
│
▼
Docker Hub / Registry
(docker pull/push)
- CLI sends commands to the Daemon via a REST API
- Daemon does the actual work: pulling images, starting containers, managing networks
- Images are pulled from registries (Docker Hub by default)
- Containers are created from images by the daemon
A Dockerfile is a plain text file with step-by-step instructions for building a Docker image. Docker reads it top to bottom and executes each instruction as a layer.
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 5000
CMD ["node", "index.js"]FROM node:20-alpine- Every Dockerfile must start with
FROM - Sets the base image — your image is built on top of this
node:20-alpine= Node.js v20 on Alpine Linux (a tiny 5MB Linux distro)- Alpine is preferred over the default Debian because it's much smaller
node:20 → ~1.1 GB (full Debian)
node:20-slim → ~240 MB (stripped Debian)
node:20-alpine → ~130 MB (Alpine Linux) ← best for production
WORKDIR /app- Sets the working directory inside the container for all following commands
- If
/appdoesn't exist, Docker creates it automatically - All
RUN,COPY,CMDinstructions after this work relative to/app - Without
WORKDIR, files would land in/(root) which is messy
ENV NODE_ENV=production- Sets an environment variable inside the image
- This variable is available to the app at runtime:
process.env.NODE_ENV === 'production' - Setting
NODE_ENV=productiontells Express and many npm packages to use production optimizations (less logging, no dev error pages, etc.)
COPY package*.json ./- Copies files from your local machine into the image
package*.jsonmatches bothpackage.jsonandpackage-lock.json./means the current directory inside the container (/appbecause of WORKDIR)- Why copy package files before the rest of the code? → Layer caching (explained in Section 10)
RUN npm ci --only=production- Executes a command during the image build (not at runtime)
npm ci= clean install — usespackage-lock.jsonexactly (more reliable thannpm install)--only=production= skipdevDependencies(testing tools, linters) → smaller image- This creates a layer with all the installed
node_modules
npm ci vs npm install:
npm install |
npm ci |
|
|---|---|---|
| Uses lock file | Can modify it | Strictly follows it |
| Deletes node_modules first | No | Yes |
| Consistency | Less predictable | 100% reproducible |
| Use in | Development | CI/CD, Docker |
COPY . .- Copies everything else from your project directory into
/appin the image - This comes AFTER
npm cion purpose — if you change your code, only this layer rebuilds (notnpm ci). See Section 10 for why.
What gets copied? Everything in the build context (
.). Use a.dockerignorefile to excludenode_modules,.git,.env, etc.
EXPOSE 5000- Documents that the container listens on port 5000
- This is informational only — it does NOT actually publish the port to your host machine
- To actually access the port, you still need
-p 5000:5000indocker run - Think of it as documentation: "this container serves traffic on 5000"
CMD ["node", "index.js"]- Specifies the default command to run when the container starts
- Uses JSON array format (exec form) — preferred because it doesn't spawn a shell
- Runs
node index.jswhich starts the Express server - There can only be one CMD per Dockerfile (last one wins)
CMD vs ENTRYPOINT:
CMD |
ENTRYPOINT |
|
|---|---|---|
| Purpose | Default command — can be overridden | Fixed executable — cannot be overridden easily |
| Override with | docker run image other-command |
docker run --entrypoint |
| Use for | Apps where you might want to swap the command | Containers that always run one specific program |
# CMD — can override:
CMD ["node", "index.js"]
# docker run my-app node other-script.js ← overrides CMD
# ENTRYPOINT — always runs:
ENTRYPOINT ["node"]
CMD ["index.js"] # ← this becomes the default argument to node
# docker run my-app other-script.js ← runs "node other-script.js"# Build the image from the Dockerfile in the current directory
docker build -t my-express-app .
# Run it, mapping port 5000 on host to 5000 in container
docker run -d -p 5000:5000 my-express-app
# Visit http://localhost:5000 → "Hello World from docker!"| Instruction | Purpose | Runs At |
|---|---|---|
FROM |
Set base image | Build time |
WORKDIR |
Set working directory | Build time |
ENV |
Set environment variable | Build time + Runtime |
COPY |
Copy files from host to image | Build time |
ADD |
Like COPY but supports URLs and tar extraction | Build time |
RUN |
Execute a shell command | Build time |
EXPOSE |
Document which port the app uses | Documentation only |
CMD |
Default command when container starts | Runtime |
ENTRYPOINT |
Fixed executable for the container | Runtime |
ARG |
Build-time variable (not available at runtime) | Build time only |
VOLUME |
Declare a mount point for external volumes | Runtime |
USER |
Set the user to run commands as | Build time + Runtime |
Create a .dockerignore file alongside your Dockerfile:
node_modules
.git
.env
*.log
.DS_Store
Without this, COPY . . would copy node_modules (heavy) and .env (sensitive) into the image — bad for both size and security.
docker run is the most used command. Here are all the key flags with examples:
docker run [OPTIONS] IMAGE [COMMAND]| Flag | Example | What It Does |
|---|---|---|
-d |
docker run -d nginx |
Detached mode — run in background |
-p |
docker run -p 8080:80 nginx |
Map host port to container port |
--name |
docker run --name web nginx |
Give the container a custom name |
-e |
docker run -e PORT=3000 nginx |
Set an environment variable |
-v |
docker run -v /host/path:/container/path nginx |
Mount a volume |
--rm |
docker run --rm nginx |
Auto-remove container when it stops |
--restart |
docker run --restart always nginx |
Set restart policy |
--network |
docker run --network my-net nginx |
Connect to a Docker network |
-it |
docker run -it node sh |
Interactive terminal (for exploration) |
--memory |
docker run --memory 512m nginx |
Limit memory to 512 MB |
--cpus |
docker run --cpus 0.5 nginx |
Limit to half a CPU |
--env-file |
docker run --env-file .env nginx |
Load env vars from a file |
docker run \
-d \
-p 5000:5000 \
--name my-express-app \
-e NODE_ENV=production \
-e PORT=5000 \
--restart unless-stopped \
--memory 256m \
--cpus 0.5 \
my-express-appdocker run --rm node:20-alpine node -e "console.log('hello')"The container is deleted the moment it exits. Perfect for one-off tasks where you don't need the container to stick around.
This is the core reason Docker has restart policies and container lifecycle management. A production app can go down for many unexpected reasons:
| # | Failure | What Happens |
|---|---|---|
| 1 | Resource Spike | Sudden surge in CPU or memory causes the process to crash or the OS to kill it (OOM killer) |
| 2 | Dependency Unavailable | A database, external API, or another microservice goes down — your app can't connect and exits |
| 3 | Application Bug | An unhandled exception, null reference, or uncaught error crashes the process |
| 4 | Manual Stop | Someone accidentally or intentionally runs docker stop or shuts the server down |
| 5 | System Reboot | The host machine restarts (OS update, power cut) — all running containers are killed |
Without restart policies, every one of these events means manual intervention to bring the app back. With the right policy, Docker handles recovery automatically.
The image above shows the 13 essential container commands. Here is the full reference with notes:
| Command | Example | What It Does |
|---|---|---|
docker run |
docker run nginx |
Create & start a container in one step |
docker run -d |
docker run -d nginx |
Run container in the background (detached) |
docker run -p |
docker run -p 8080:80 nginx |
Map a host port to a container port |
docker ps |
docker ps |
List running containers |
docker ps -a |
docker ps -a |
List all containers (running + stopped) |
docker stop |
docker stop container_id |
Stop a running container |
docker start |
docker start container_id |
Start a stopped container |
docker restart |
docker restart container_id |
Restart a container |
docker rm |
docker rm container_id |
Remove a container |
docker logs |
docker logs container_id |
View container logs |
docker exec -it |
docker exec -it container_id sh |
Access container terminal |
docker container prune |
docker container prune |
Remove stopped containers |
docker system prune |
docker system prune |
Remove all unused data |
docker run nginx # Create + start (foreground, blocks terminal)
docker run -d nginx # Detached mode — runs in background, prints container ID
docker run -p 8080:80 nginx # Port mapping: host:containerPort mapping breakdown:
docker run -p 8080:80 nginx
↑ ↑
host port container port
→ You access http://localhost:8080
→ Docker forwards it to port 80 inside the container
docker ps # only RUNNING containers
docker ps -a # ALL containers (including stopped/exited ones)Output columns explained:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 nginx ... 1 min Up 1 minute 0.0.0.0:8080->80/tcp my-nginx
f6e5d4c3b2a1 nginx ... 5 min Exited (0) old-nginx
STATUS: Up= runningSTATUS: Exited (0)= stopped cleanlySTATUS: Exited (1)= stopped with an error
docker stop container_id # Sends SIGTERM → waits 10 sec → sends SIGKILL (graceful)
docker start container_id # Starts a stopped container (keeps previous config)
docker restart container_id # Equivalent to docker stop + docker start
docker stopvsdocker kill:
stop= graceful shutdown (app can clean up, close connections)kill= immediate force kill (SIGKILL, no cleanup time)
docker rm container_id # Remove one stopped container
docker container prune # Remove ALL stopped containers
docker system prune # Remove stopped containers + dangling images + unused networks + build cacheYou cannot
docker rma running container. Stop it first, or usedocker rm -fto force-remove.
docker logs container_id # Print all logs since container started
docker logs -f container_id # Follow logs in real time (like tail -f)
docker logs --tail 100 container_id # Show last 100 lines only
docker logs --since 1h container_id # Show logs from the last 1 hourdocker exec -it container_id sh # Open shell (sh is available in most images)
docker exec -it container_id bash # Bash (available in Debian/Ubuntu-based images)-i= interactive (keeps stdin open)-t= allocate a terminal (TTY)- You can also run single commands:
docker exec container_id ls /app
The diagram shows 4 states a container moves through and the commands that trigger each transition.
| State | Color in Diagram | Description |
|---|---|---|
| Created | Purple | Container created (filesystem allocated, config set) but process NOT yet started |
| Running | Teal | The container's main process is actively running |
| Stopped | Yellow/Orange | Process has exited. Container still exists with its data, can be restarted |
| Deleted | Red | Container permanently removed. Data is gone (unless in a volume) |
┌─────────────── Run ───────────────────┐
│ ▼
Create ──► [ Created ] ──── Start ────────────► [ Running ]
│ │
│ rm Stop
│ │
▼ ▼
[ Deleted ] ◄──────── rm ──────────── [ Stopped ]
│
Start
│
└──────────────► [ Running ]
| Trigger | From | To | Notes |
|---|---|---|---|
docker create |
— | Created | Prepares container, does not start it |
docker run |
— | Running | Skips Created — creates AND starts in one command |
docker start |
Created / Stopped | Running | Starts existing container |
docker stop |
Running | Stopped | Graceful shutdown |
docker restart |
Running | Running | Internally: stop → start |
docker rm |
Created / Stopped | Deleted | Can't remove while running |
docker rm -f |
Running | Deleted | Force removes (skips stop) |
Key insight from the diagram:
docker rungoes directly to Running — it never enters the Created state. Onlydocker createproduces a Created container.
The diagram shows 3 restart policies branching from the top. These are set when creating a container:
docker run --restart <policy> image_namedocker run --restart always nginxWhat the flowchart shows:
Container Start ──────────────────────► Container Exit
▲ │
│ ▼
└──────────── Restart Container ◄───────┘
▲
│
Docker Daemon Restart
Behavior summary:
- Restarts on clean exit (exit code 0) ✅
- Restarts on crash (exit code != 0) ✅
- Restarts after Docker daemon restart / system reboot ✅
- This is a permanent loop — the container will always come back
Use case: Web servers, databases, APIs — anything that must never be offline.
docker run -d --restart always -p 80:80 nginxdocker run --restart unless-stopped nginxWhat the flowchart shows:
Container Start ──────────────────────────────► Container Exit
▲ │
│ ▼
│ Manually Stopped?
│ Yes │ │ No
│ ▼ ▼
│ Don't Restart
│ Restart Container ──┐
│ │
└───────────────────────────────────────────────────────────┘
Docker Daemon Restart
│
▼
Was Running Before?
Yes │ │ No
▼ ▼
Restart Don't
Container Restart
Behavior summary:
- Restarts on crash ✅
- Restarts on clean exit ✅
- Respects
docker stop— will NOT come back after a manual stop ✅ - After daemon restart — only if it wasn't manually stopped
⚠️
Key difference from always: always ignores your manual stop. unless-stopped remembers it and stays stopped after daemon restarts too.
Use case: Production services where you occasionally need to take them offline for maintenance.
docker run -d --restart unless-stopped -p 3000:3000 my-appdocker run --restart on-failure nginx
# With a max retry limit:
docker run --restart on-failure:5 nginxWhat the flowchart shows:
Container Start ──────────────────────────────► Container Exit
▲ │
│ ▼
│ Exit Code != 0 ?
│ Yes │ │ No
│ ▼ ▼
│ Restart Don't
│ Container Restart
└───────────────────────────────────────────┘
Docker Daemon Restart
│
▼
Restart Container
│
└──────────────────────────────► Container Start
Behavior summary:
- Restarts on crash (exit code != 0) ✅
- Does NOT restart on clean exit (exit code 0) ❌ — intentionally won't restart a finished job
- Restarts after Docker daemon restart ✅
:Nlimits max retries — e.g.,on-failure:5= give up after 5 consecutive crashes
Use case: One-time scripts, batch jobs, or worker processes that should finish and stop cleanly, but automatically retry if they crash.
# Data import job — retry up to 3 times if it crashes, stop when done
docker run --restart on-failure:3 my-data-importerTaken directly from the image:
| Policy | Description |
|---|---|
always |
Restart the container no matter what, even if it exits cleanly (exit code 0) |
unless-stopped |
Same as always, except it won't restart if the container was manually stopped |
on-failure |
Restart only when the container exits with a non-zero exit code (i.e., it failed) |
Extended comparison:
| Policy | Exit 0 | Exit !=0 | Daemon Restart | Manual Stop Respected |
|---|---|---|---|---|
no (default) |
❌ | ❌ | ❌ | — |
always |
✅ | ✅ | ✅ | ❌ |
unless-stopped |
✅ | ✅ | ✅ (if was running) | ✅ |
on-failure |
❌ | ✅ | ✅ | — |
Images are the templates/blueprints that containers are created from. An image is read-only — when you run it, Docker adds a writable layer on top to create a container.
| Command | Example | What It Does |
|---|---|---|
docker pull |
docker pull nginx |
Download image from Docker Hub |
docker images |
docker images |
List all local images |
docker build |
docker build -t my-app . |
Build image from Dockerfile |
docker rmi |
docker rmi nginx |
Remove an image |
docker tag |
docker tag my-app my-app:v1 |
Add tag/version to image |
docker push |
docker push username/my-app |
Push image to registry |
docker image prune |
docker image prune |
Remove unused images |
docker inspect |
docker inspect nginx |
View image details |
docker build -t my-app . # Build and tag as "my-app:latest"
docker build -t my-app:v2 . # Build with specific version tag
docker build -f Dockerfile.prod -t my-app:prod . # Use a different Dockerfile
docker build --no-cache -t my-app . # Force rebuild all layers (ignore cache)The . at the end is the build context — the directory Docker reads files from.
docker rmi nginx # remove by name
docker rmi a6bd71f48f68 # remove by image ID
docker rmi nginx:1.25 # remove specific tagCannot remove an image that is being used by an existing container (even stopped). Remove the container first.
docker tag my-app my-app:v1 # add a version tag
docker tag my-app username/my-app:latest # tag for Docker Hub pushTagging does NOT copy the image — it just adds a new label pointing to the same image data.
docker inspect nginxReturns a JSON object with: Image ID, creation date, environment variables, exposed ports, entrypoint, CMD, layer info, volume mounts.
Every instruction in a Dockerfile that modifies the filesystem creates a new layer. Layers are stacked on top of each other to form the final image.
┌──────────────────────────────┐
│ Layer 5: COPY . . │ ← your app source code
├──────────────────────────────┤
│ Layer 4: RUN npm ci │ ← node_modules
├──────────────────────────────┤
│ Layer 3: COPY package*.json │ ← package files
├──────────────────────────────┤
│ Layer 2: ENV NODE_ENV=... │ ← env config
├──────────────────────────────┤
│ Layer 1: FROM node:20-alpine│ ← base OS + Node
└──────────────────────────────┘
Docker caches each layer. When you rebuild, Docker only re-runs layers that changed and everything after them.
Smart order (this project's Dockerfile):
COPY package*.json ./ # Layer A — only changes when dependencies change
RUN npm ci # Layer B — only re-runs if Layer A changed
COPY . . # Layer C — changes every time you edit codeYou change a .js file:
Layer A (package.json) → cache HIT ← not re-run
Layer B (npm ci) → cache HIT ← not re-run ← saves minutes
Layer C (COPY . .) → cache MISS ← re-run (your file changed)
Naive order (bad):
COPY . . # Changes every time → cache busted
RUN npm ci # Always re-runs → slow!Rule: Put rarely changing instructions first, frequently changing ones last. This maximizes cache hits and makes builds fast.
Environment variables let you configure your app without changing the code or rebuilding the image. There are two ways to set them in Docker.
ENV NODE_ENV=production
ENV PORT=5000- The variable is built into the image — every container from this image gets it
- Available at both build time and runtime
- Good for variables that are always the same (e.g.,
NODE_ENV=production)
docker run -e PORT=3000 my-app
docker run -e NODE_ENV=development my-app
docker run -e DB_URL=mongodb://localhost:27017/mydb my-app- Overrides or adds variables when starting the container
- Does NOT change the image — different containers can have different values
- Good for secrets, database URLs, API keys — things that differ per environment
# .env file:
NODE_ENV=production
PORT=5000
DB_PASSWORD=supersecret
docker run --env-file .env my-app- Loads all variables from a file — cleaner than many
-eflags - Never bake
.envfiles into your image — use--env-fileor a secrets manager
--env-file < -e flag < ENV in Dockerfile
↑
-e flag overrides --env-file
Actually: -e flag always overrides --env-file values, so runtime flags take precedence.
// index.js
const port = process.env.PORT || 5000;
const env = process.env.NODE_ENV;
console.log(`Running in ${env} mode on port ${port}`);By default, a container's filesystem is temporary. When the container is deleted, all data written inside it is gone. Volumes solve this.
Container (no volume):
You write data to /app/data inside the container
docker rm container
→ All data is permanently lost
| Type | Syntax | Use Case |
|---|---|---|
| Named Volume | -v my-volume:/app/data |
Persistent data (databases, uploads) managed by Docker |
| Bind Mount | -v /host/path:/container/path |
Share host files with container (local dev) |
| tmpfs | --tmpfs /tmp |
In-memory only, fast, non-persistent |
# Create a named volume
docker volume create my-data
# Use it when running a container
docker run -v my-data:/app/data my-app
# List all volumes
docker volume ls
# Inspect a volume (see where Docker stores it)
docker volume inspect my-data
# Remove a volume
docker volume rm my-data
# Remove all unused volumes
docker volume prune- Docker stores the volume data on the host (usually under
/var/lib/docker/volumes/) - The volume persists even after the container is removed
- Multiple containers can share the same volume
# Mount current directory into /app in the container
docker run -v $(pwd):/app my-app
# Windows (PowerShell):
docker run -v ${PWD}:/app my-app
# Mount a specific host path
docker run -v /Users/zihad/myproject:/app my-appUse case for local development:
# Your code changes on host are instantly reflected in the container
docker run -d -p 5000:5000 -v $(pwd):/app my-express-appBind mounts are great for dev (see live code changes), but named volumes are better for production (Docker manages the location and lifecycle).
Without volume: With named volume:
Container deleted Container deleted
→ data gone ❌ → volume persists ✅
New container New container
→ starts empty ❌ → mounts same volume, data intact ✅
By default, containers are isolated — they can't talk to each other. Networks allow containers to communicate.
Docker creates three networks automatically:
| Network | Driver | Description |
|---|---|---|
bridge |
bridge | Default network for containers. Containers can talk to each other via IP. |
host |
host | Container shares the host's network stack. No port mapping needed. |
none |
null | No networking at all. Complete isolation. |
docker network ls
# NETWORK ID NAME DRIVER SCOPE
# abc123 bridge bridge local
# def456 host host local
# ghi789 none null localOn the default bridge network, containers cannot reach each other by name — only by IP. A custom bridge network enables DNS-based discovery (container name = hostname).
# Create a custom network
docker network create my-network
# Run containers on that network
docker run -d --name app --network my-network my-express-app
docker run -d --name db --network my-network mongo
# Now "app" can connect to "db" just by using the hostname "db"
# e.g., mongodb://db:27017/mydb# List networks
docker network ls
# Create a network
docker network create my-network
# Inspect a network (see which containers are connected)
docker network inspect my-network
# Connect a running container to a network
docker network connect my-network container_id
# Disconnect a container from a network
docker network disconnect my-network container_id
# Remove a network
docker network rm my-network
# Remove unused networks
docker network pruneWithout custom network: With custom network:
container A → cannot find "db" container A → "db" resolves to db container IP
must use IP: 172.17.0.2 can use: mongodb://db:27017
Real example — Express app + MongoDB:
docker network create app-net
docker run -d \
--name mongo \
--network app-net \
mongo
docker run -d \
--name express-app \
--network app-net \
-p 5000:5000 \
-e DB_URL=mongodb://mongo:27017/mydb \
my-express-appThe Express app connects to MongoDB using mongodb://mongo:27017 — mongo resolves to the MongoDB container's IP automatically.
A dangling image is an image with no name and no tag — it appears as <none>:<none> in docker images. It exists on disk but no container or tag references it.
The most common cause is rebuilding an image with the same tag:
# Build 1 — image created, tagged as "my-app:latest"
docker build -t my-app .
# ... you edit your code ...
# Build 2 — a NEW image gets the "my-app:latest" tag
# The OLD image LOSES its tag → becomes <none>:<none> (dangling)
docker build -t my-app .Every rebuild leaves the old image orphaned. Over many rebuilds, these pile up and eat disk space silently.
docker images
# REPOSITORY TAG IMAGE ID CREATED SIZE
# my-app latest abc123def456 30 seconds ago 320MB
# <none> <none> d1e017099d17 5 minutes ago 310MB ← dangling
# <none> <none> a2f9b3c44e21 1 hour ago 308MB ← danglingdocker images -f dangling=true- Waste disk space — each build can leave behind hundreds of MBs
- Accumulate invisibly — don't show up clearly in plain
docker images - Serve no purpose — no container runs from them, no tag points to them
# Remove only dangling images
docker image prune
# Remove ALL images not currently used by a container
docker image prune -a
# Skip confirmation prompt
docker image prune -f
# Remove everything unused (containers, images, networks, cache)
docker system pruneDocker Hub is the default public registry. Once an image is pushed, anyone can docker pull it from anywhere in the world.
Your Machine Docker Hub
───────────── ──────────────────────────────
[local image] ─── push ──► [username/repository:tag]
docker login
# Prompts for username and passworddocker build -t my-app .Docker Hub requires the format: username/repository:tag
# Format:
docker tag <local-image-name> <dockerhub-username>/<repository>:<tag>
# Example:
docker tag my-app zihadrahman/my-app:v1
docker tag my-app zihadrahman/my-app:latestdocker push zihadrahman/my-app:v1
docker push zihadrahman/my-app:latestDocker pushes each layer separately. Layers already on Docker Hub are skipped (Layer already exists), so subsequent pushes are very fast.
Your image is now at: hub.docker.com/r/zihadrahman/my-app
Anyone can pull it with:
docker pull zihadrahman/my-app:v1docker login
docker build -t my-express-app .
docker tag my-express-app zihadrahman/my-express-app:v1
docker tag my-express-app zihadrahman/my-express-app:latest
docker push zihadrahman/my-express-app:v1
docker push zihadrahman/my-express-app:latest:latest— always points to the most recent build (default pulled by others):v1— a permanent snapshot. Even after you push v2, v1 remains pullable.
Pulling downloads a Docker image from a registry to your local machine so you can run containers from it.
docker pull nginx # pulls nginx:latest
docker pull nginx:1.25 # pulls a specific version
docker pull node:20-alpine # the exact base image this project usesDocker images are made of layers. Docker downloads each layer separately and caches them:
docker pull node:20-alpine
18-alpine: Pulling from library/node
31e352740f53: Pull complete ← base OS layer
b2b5bfcb75ab: Pull complete ← node runtime layer
Digest: sha256:abc123...
Status: Downloaded newer image for node:20-alpine
If you pull another image that shares a layer, that layer is not re-downloaded — reused from cache.
# Official image (maintained by Docker/vendor)
docker pull nginx
docker pull postgres
# User/community image (username/repository)
docker pull zihadrahman/my-express-app:v1
# Specific version tag
docker pull nginx:1.25.3
# Immutable digest reference (exact version, never changes)
docker pull nginx@sha256:abc123...docker pull |
docker run |
|
|---|---|---|
| Downloads image | ✅ | ✅ (if not cached locally) |
| Creates container | ❌ | ✅ |
| Starts container | ❌ | ✅ |
Use docker pull when you want to pre-download an image without running it (useful for warming CI/CD cache).
| Real Command | Example Command | Usage |
|---|---|---|
docker build |
docker build -t my-app . |
Build image from Dockerfile in current directory |
docker build |
docker build -t my-app:v1 . |
Build image with a specific version tag |
docker build |
docker build --no-cache -t my-app . |
Force rebuild all layers, ignore layer cache |
docker build |
docker build -f Dockerfile.prod -t my-app . |
Build using a specific Dockerfile file |
| Real Command | Example Command | Usage |
|---|---|---|
docker run |
docker run nginx |
Create and start a container (foreground, blocks terminal) |
docker run -d |
docker run -d nginx |
Run container in background (detached mode) |
docker run -p |
docker run -p 5000:5000 nginx |
Map host port 5000 to container port 5000 |
docker run --name |
docker run --name web nginx |
Give the container a custom name |
docker run -e |
docker run -e NODE_ENV=production my-app |
Set an environment variable inside the container |
docker run --env-file |
docker run --env-file .env my-app |
Load environment variables from a file |
docker run -v |
docker run -v my-vol:/app/data my-app |
Mount a named volume for persistent data |
docker run -v |
docker run -v ${PWD}:/app my-app |
Bind mount current directory into container (dev) |
docker run --rm |
docker run --rm node sh |
Auto-remove container when it stops |
docker run --memory |
docker run --memory 256m my-app |
Limit container memory to 256 MB |
docker run --cpus |
docker run --cpus 0.5 my-app |
Limit container to half a CPU core |
docker run --network |
docker run --network my-net my-app |
Connect container to a custom network |
docker run --restart |
docker run --restart always my-app |
Set restart policy when starting the container |
| Real Command | Example Command | Usage |
|---|---|---|
docker ps |
docker ps |
List only currently running containers |
docker ps -a |
docker ps -a |
List all containers (running + stopped) |
docker stop |
docker stop container_id |
Gracefully stop a running container (SIGTERM) |
docker start |
docker start container_id |
Start a previously stopped container |
docker restart |
docker restart container_id |
Stop and start a container in one command |
docker rm |
docker rm container_id |
Remove a stopped container |
docker rm -f |
docker rm -f container_id |
Force remove a running container |
docker logs |
docker logs container_id |
View all stdout/stderr output from a container |
docker logs -f |
docker logs -f container_id |
Follow/stream container logs in real time |
docker logs --tail |
docker logs --tail 100 container_id |
Show only the last 100 lines of logs |
docker exec -it |
docker exec -it container_id sh |
Open an interactive shell inside the container |
docker container prune |
docker container prune |
Remove all stopped containers at once |
docker system prune |
docker system prune |
Remove all unused containers, images, networks, cache |
| Real Command | Example Command | Usage |
|---|---|---|
docker run --restart always |
docker run --restart always nginx |
Always restart — on any exit or after system reboot |
docker run --restart unless-stopped |
docker run --restart unless-stopped nginx |
Restart on any exit, but not if manually stopped |
docker run --restart on-failure |
docker run --restart on-failure nginx |
Restart only when exit code is non-zero (crash) |
docker run --restart on-failure:N |
docker run --restart on-failure:5 nginx |
Restart on crash, but give up after N retries |
| Real Command | Example Command | Usage |
|---|---|---|
docker images |
docker images |
List all images stored locally |
docker pull |
docker pull nginx |
Download the latest version of an image from Docker Hub |
docker pull |
docker pull nginx:1.25 |
Download a specific version/tag of an image |
docker tag |
docker tag my-app username/my-app:v1 |
Add a registry tag to an image before pushing |
docker push |
docker push username/my-app:v1 |
Upload a tagged image to Docker Hub |
docker rmi |
docker rmi nginx |
Remove a local image by name |
docker rmi |
docker rmi a6bd71f48f68 |
Remove a local image by ID |
docker inspect |
docker inspect nginx |
View detailed metadata of an image (JSON output) |
| Real Command | Example Command | Usage |
|---|---|---|
docker images -f |
docker images -f dangling=true |
Show only dangling (<none>:<none>) images |
docker image prune |
docker image prune |
Remove all dangling images |
docker image prune -a |
docker image prune -a |
Remove all images not used by any container |
docker image prune -f |
docker image prune -f |
Remove dangling images without confirmation prompt |
docker system prune |
docker system prune |
Remove all unused Docker data (nuclear cleanup) |
| Real Command | Example Command | Usage |
|---|---|---|
docker volume create |
docker volume create my-vol |
Create a named volume managed by Docker |
docker volume ls |
docker volume ls |
List all volumes |
docker volume inspect |
docker volume inspect my-vol |
View volume details and host storage path |
docker volume rm |
docker volume rm my-vol |
Remove a specific volume |
docker volume prune |
docker volume prune |
Remove all volumes not used by any container |
| Real Command | Example Command | Usage |
|---|---|---|
docker network ls |
docker network ls |
List all Docker networks |
docker network create |
docker network create my-net |
Create a custom bridge network |
docker network inspect |
docker network inspect my-net |
View network details and connected containers |
docker network connect |
docker network connect my-net container_id |
Connect a running container to a network |
docker network disconnect |
docker network disconnect my-net container_id |
Disconnect a container from a network |
docker network rm |
docker network rm my-net |
Remove a network |
docker network prune |
docker network prune |
Remove all networks not used by any container |
| Real Command | Example Command | Usage |
|---|---|---|
docker login |
docker login |
Log in to Docker Hub (prompts for username + password) |
docker logout |
docker logout |
Log out of Docker Hub |
Habibur Rahman Zihad
Full-Stack Developer
© 2026 All Rights Reserved