If you’re new to Docker, it can feel like “magic”: you run a single command and suddenly an app works on your machine without installing a bunch of dependencies. 🧙♂️
This Docker for Beginners guide explains Docker from zero: what it is, why it matters, how to configure containers safely, how images and volumes work, and how to manage a full stack with Compose — plus the everyday commands you’ll use to run, stop, inspect, and remove things. ✅
What Docker is (in plain terms)
Docker is a tool that lets you package an application together with everything it needs (libraries, runtime, config defaults) into an image. From that image you start a container — a running, isolated process on your system. 📦➡️🏃
Key idea: You don’t install apps directly on the OS; you run them inside containers.
Docker vs Virtual Machines
- Virtual Machine (VM): ships a whole OS per VM. Heavier, slower to start. 🧱
- Docker container: shares the host OS kernel and isolates processes. Lighter, fast start. ⚡
Docker is not “more secure by default” than a VM; it’s just a different isolation model. Keep security basics in mind (we’ll cover them). 🔐
Why Docker is useful
- Reproducible environments: “Works on my machine” becomes “works anywhere that runs Docker.” 🌍
- Easy upgrades & rollbacks: switch image versions, restart, done. 🔁
- Clean host OS: dependencies stay inside the container. 🧼
- Portable stacks: run databases, apps, monitoring tools together via Docker Compose. 🧩
Core concepts you must know
1) Image
A read-only template: app + dependencies + default config.
Examples: nginx:alpine, postgres:16, redis:7.
2) Container
A running instance of an image.
Think: image = blueprint, container = built house. 🏠
3) Registry
A place to store images.
- Docker Hub (public)
- GitHub Container Registry
- Private registries
4) Volume
Persistent storage for containers. Without volumes, data can be lost when containers are removed. 💾
5) Network
A way for containers to talk to each other by service name (especially in Docker Compose). 🌐
Your first Docker commands
Check Docker is working
docker version
docker info
Run a test container
docker run --rm hello-world
runstarts a container.--rmdeletes it automatically after it exits.
Everyday container lifecycle commands
List containers
# Running containers
docker ps
# All containers (including stopped)
docker ps -a
Start / stop / restart
docker stop <container_name_or_id>
docker start <container_name_or_id>
docker restart <container_name_or_id>
Remove containers
# Remove a stopped container
docker rm <container_name_or_id>
# Force-remove a running container
docker rm -f <container_name_or_id>
View logs
docker logs <container_name_or_id>
# Follow logs live
docker logs -f <container_name_or_id>
# Last 200 lines
docker logs --tail 200 <container_name_or_id>
Execute a shell inside a running container
# Try sh first (common on Alpine)
docker exec -it <container_name_or_id> sh
# Or bash (common on Debian/Ubuntu images)
docker exec -it <container_name_or_id> bash
Inspect details (super useful)
docker inspect <container_name_or_id>
# Example: get container IP address (not always needed in Compose)
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <container>
Running containers: the most important docker run parameters
1) Naming the container
docker run --name my-nginx nginx:alpine
2) Detached mode (run in background)
docker run -d --name my-nginx nginx:alpine
3) Ports (publish container port to host)
# host_port:container_port
docker run -d --name web -p 8080:80 nginx:alpine
- Container listens on
80. - Your host exposes it on
http://localhost:8080. 🌐
4) Environment variables
docker run -d --name app \
-e NODE_ENV=production \
-e APP_PORT=3000 \
my-image:latest
Environment variables are the most common way to configure containers.
5) Volumes: persistent data
Named volume (recommended for most data)
docker volume create pgdata
docker run -d --name postgres \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:16
Bind mount (map a host folder into container)
docker run -d --name web \
-p 8080:80 \
-v /srv/web:/usr/share/nginx/html:ro \
nginx:alpine
- Bind mounts are great for dev and for “I want my files on the host.”
:romakes it read-only (safer). 🔒
6) Restart policies (auto-start after reboot)
docker run -d --name app --restart unless-stopped my-image:latest
Common options:
no(default)alwaysunless-stopped✅on-failure
7) Resource limits (avoid one container eating your server)
docker run -d --name heavy \
--cpus="2" \
--memory="1g" \
my-image:latest
8) User / permissions (reduce “root inside container” problems)
# Run as UID:GID (example)
docker run -d --name app \
--user 1000:1000 \
-v /srv/appdata:/data \
my-image:latest
This matters a lot when using bind mounts: the container must have permission to read/write host files. 🧩
Images: build, pull, list, remove
Pull an image
docker pull nginx:alpine
List images
docker images
Remove an image
docker rmi nginx:alpine
Build your own image (Dockerfile basics)
Example Dockerfile (minimal Node.js app):
# Use an official base image
FROM node:20-alpine
# Set working directory
WORKDIR /app
# Copy package files first (better caching)
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy the rest of the source code
COPY . .
# Expose app port (documentation purpose)
EXPOSE 3000
# Run the app
CMD ["node", "server.js"]
Build it:
docker build -t my-node-app:1.0 .
Run it:
docker run -d --name myapp -p 3000:3000 my-node-app:1.0
Docker Compose (recommended for real projects)
If docker run is “single container,” Docker Compose is “a stack” (app + database + cache + reverse proxy) defined in a docker-compose.yml file. 🧩
This is the point where Docker for Beginners usually “clicks”: instead of juggling lots of long docker run commands, you keep everything readable and repeatable in one YAML file. 📄✅
Why Compose is better for beginners
- One file documents your setup
- Easy to start/stop everything
- Networks are automatic
- Containers can refer to each other by service name
Example: app + database
services:
db:
image: postgres:16
container_name: demo_postgres
environment:
# Use strong secrets in real setups
POSTGRES_USER: demo
POSTGRES_PASSWORD: demo_password
POSTGRES_DB: demo_db
volumes:
- db_data:/var/lib/postgresql/data
restart: unless-stopped
app:
image: nginx:alpine
container_name: demo_nginx
ports:
- "8080:80"
depends_on:
- db
restart: unless-stopped
volumes:
db_data:
Run:
docker compose up -d
Stop:
docker compose down
List services:
docker compose ps
View logs:
docker compose logs -f
Important Compose fields you’ll use often
services: your containersimage: which image to usebuild: build from a Dockerfile instead of pulling an imagecontainer_name: optional fixed name (not required, but convenient)ports: host:container port mappingenvironment: env varsenv_file: load variables from a.envfilevolumes: persistent storage or bind mountsrestart: auto-start policydepends_on: start order (not “readiness”)healthcheck: verify container is actually readynetworks: custom networks if neededuser: run as non-root UID:GID
Volumes: don’t lose your data
A beginner mistake: removing a container and accidentally losing the database. 😬
Rules of thumb:
- App code/config: bind mount (common in dev)
- Databases: named volumes (common in prod)
- Backups: always do backups outside the container
Useful commands:
# List volumes
docker volume ls
# Inspect volume
docker volume inspect <volume_name>
# Remove volume (DANGEROUS: deletes data)
docker volume rm <volume_name>
Networks: how containers talk to each other
In Compose, containers are automatically connected to a default network and can reach each other via service name. For example, your app can connect to Postgres at host db (the service name). 🌐
Commands:
# List networks
docker network ls
# Inspect a network
docker network inspect <network_name>
Healthchecks (the “is it actually ready?” problem)
depends_on ensures start order, but does not guarantee the database is ready to accept connections.
A healthcheck example (Postgres):
services:
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U demo"]
interval: 10s
timeout: 5s
retries: 5
Cleanup: keep Docker from eating disk space
Over time you collect unused images, stopped containers, old networks, and build cache.
Safe-ish cleanup:
# Remove stopped containers, unused networks, unused images
docker system prune
More aggressive (removes unused images too):
docker system prune -a
Remove unused volumes (be careful):
docker volume prune
Troubleshooting checklist
- Container keeps restarting 🔁
- Check logs:
docker logs --tail 200 <container> - Check exit code:
docker inspect -f '{{.State.ExitCode}}' <container>
- Port not reachable 🌐
- Confirm port mapping:
docker port <container> - Confirm service is listening inside container
- Permission denied on volumes 🧷
- Check UID/GID on host vs container user
- Prefer
--useror images that supportPUID/PGID
- App can’t reach DB 🔌
- In Compose, use service name (
db), notlocalhost - Verify network:
docker network inspect ...
Security basics (beginner-friendly)
- Avoid
--privilegedunless you truly need it. - Prefer
read_only: truewhere possible. - Drop Linux capabilities if you know what you’re doing.
- Don’t run everything as root; use
user: 1000:1000when feasible. - Don’t store secrets in plain text inside
docker-compose.ymlfor public repos.
Quick cheat sheet (copy/paste)
# Containers
docker ps
docker ps -a
docker start <c>
docker stop <c>
docker restart <c>
docker rm <c>
docker rm -f <c>
docker logs -f <c>
docker exec -it <c> sh
docker inspect <c>
# Images
docker pull <image:tag>
docker images
docker rmi <image:tag>
docker build -t my-image:1.0 .
# Volumes
docker volume ls
docker volume inspect <v>
docker volume rm <v>
# Compose
docker compose up -d
docker compose down
docker compose ps
docker compose logs -f
# Cleanup
docker system prune
Official docs (recommended)
Docker documentation: https://docs.docker.com/
Docker Compose documentation: https://docs.docker.com/compose/
Dockerfile reference: https://docs.docker.com/reference/dockerfile/
Docker CLI reference: https://docs.docker.com/reference/cli/docker/


Leave a Reply