Skip to main content
DevOps Insightsguides & answers

Docker · guide

Docker Guide

From installation to production. Copy-paste commands for every step, with the check command that tells you each one worked.

Last updated

On this page · 13 sections

1. Check if Docker Is Installed

Run this in your terminal. If you see a version number, Docker is already installed.

bash
docker --version

Check the full client and server info (engine version, storage driver, etc.):

bash
docker info

2. Install Docker on Linux (apt / yum / convenience script)

Ubuntu (apt) — on Debian, run the same steps with linux/debian in place of linux/ubuntu in both URLs (Docker publishes a separate Debian repository):

bash
# Remove conflicting unofficial packages (apt may report that none are installed)
sudo apt-get remove -y $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)

# Install prerequisites
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg

# Add Docker's official GPG key and repo
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker Engine
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

CentOS Stream (dnf/yum) — RHEL and Fedora have their own repositories: on RHEL use https://download.docker.com/linux/rhel/docker-ce.repo in the repo line below, and on Fedora replace that line with sudo dnf config-manager addrepo --from-repofile https://download.docker.com/linux/fedora/docker-ce.repo (the CentOS repo points at release paths that do not exist for Fedora):

bash
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl start docker && sudo systemctl enable docker

Convenience script (test and development machines only — Docker does not recommend it for production):

bash
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

Run Docker without sudo (add your user to the docker group). ⚠️ The docker group grants root-level privileges on the host, so add only users you would trust with root; on shared machines consider rootless mode (https://docs.docker.com/engine/security/rootless/) instead:

bash
sudo usermod -aG docker $USER
# Log out and back in for the group change to take effect

3. Install Docker on macOS / Windows

Install Docker Desktop, which bundles Docker Engine, CLI, Compose, and BuildKit. Docker Desktop is free for personal use, education, non-commercial open source and small businesses (fewer than 250 employees and under $10 million annual revenue); other professional use needs a paid subscription.

After installing, verify from a terminal:

bash
docker --version
docker compose version

4. Run Your First Container

Pull and run the hello-world image to confirm Docker works end-to-end:

bash
docker run hello-world

Run an interactive Ubuntu shell inside a container:

bash
docker run -it ubuntu bash
# You're now inside the container — try "ls" or "cat /etc/os-release"
# Type "exit" to leave

Run Nginx and map port 8080 on the host to port 80 in the container:

bash
docker run -d --name my-nginx -p 8080:80 nginx:alpine
# Visit http://localhost:8080 in your browser

5. Docker Images

Images are read-only templates used to create containers. They are pulled from registries like Docker Hub.

bash
# Pull an image
docker pull nginx:alpine

# List all local images
docker images

# Remove an image
docker rmi nginx:alpine

# Remove dangling (untagged) images; add -a to also remove every image no container uses
docker image prune

# Search Docker Hub from the CLI
docker search postgres

Image tags follow the pattern name:tag. If you omit the tag, Docker defaults to :latest. Always pin a specific tag in production (e.g. postgres:16-alpine).


6. Building Images with a Dockerfile

A Dockerfile is a text file with instructions to build a custom image. Each instruction creates a layer.

Create a file named Dockerfile (no extension):

dockerfile
# Use a slim base image
FROM node:24-alpine

# Set working directory inside the container
WORKDIR /app

# Copy dependency files first (for layer caching)
COPY package.json package-lock.json ./

# Install dependencies
RUN npm ci --omit=dev

# Copy the rest of the source code
COPY . .

# Expose the port the app listens on
EXPOSE 3000

# Default command to run
CMD ["node", "server.js"]

Build and run:

bash
# Build the image and tag it
docker build -t my-app:1.0 .

# Run a container from it
docker run -d -p 3000:3000 my-app:1.0

7. Dockerfile Best Practices

Follow these patterns to keep images small, secure, and fast to build.

Use Alpine or slim base images:

dockerfile
FROM python:3.12-alpine
# Instead of FROM python:3.12 — roughly twenty times larger to download (compare the compressed sizes on Docker Hub). Alpine uses musl, not glibc; if a dependency breaks, try python:3.12-slim

Multi-stage builds — compile in one stage, copy only the binary to a minimal final image:

dockerfile
# Stage 1: build
FROM golang:1.27-alpine AS builder
WORKDIR /src
COPY . .
RUN go build -o /app .

# Stage 2: runtime
FROM alpine:3.24
COPY --from=builder /app /app
CMD ["/app"]

Run as non-root user:

dockerfile
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

Use a .dockerignore file to exclude files from the build context. Create .dockerignore in the project root:

gitignore
node_modules
.git
*.md
.env

Order layers for caching — put instructions that change rarely (OS packages) before those that change often (source code). Copy dependency manifests and install before copying source.


8. Volumes

Volumes persist data beyond a container's lifecycle. Docker manages named volumes; bind mounts map a host path directly.

bash
# Create a named volume
docker volume create mydata

# Run a container with the named volume (postgres needs a password and keeps its data in /var/lib/postgresql/data)
docker run -d -e POSTGRES_PASSWORD=change-me -v mydata:/var/lib/postgresql/data postgres:16-alpine

# Bind-mount a host directory (useful for development)
docker run -d -v $(pwd)/src:/app/src my-app:1.0

# List volumes
docker volume ls

# Inspect a volume (see mount point on host)
docker volume inspect mydata

# Remove a volume
docker volume rm mydata

# Remove unused anonymous volumes (add --all for unused named volumes too)
docker volume prune

Bind mounts reflect host filesystem changes immediately, making them ideal for dev workflows. Named volumes are preferred for production data persistence.


9. Networking

Docker creates a default bridge network. Containers on the same user-defined network can resolve each other by container name.

bash
# List networks
docker network ls

# Create a custom bridge network
docker network create my-net

# Run two containers on the same network
docker run -d --name app --network my-net my-app:1.0
docker run -d --name db --network my-net -e POSTGRES_PASSWORD=change-me postgres:16-alpine

# "app" can now reach "db" by hostname: postgres://db:5432

Network types:

  • bridge (default) — isolated network on a single host. Containers communicate via container name when on a user-defined bridge.
  • host — container shares the host's network stack directly. No port mapping needed (-p is ignored). Supported on Docker Engine on Linux, and on Docker Desktop 4.34 and later once enabled in Settings.
  • none — no networking at all.
bash
# Run with host networking (Linux, or Docker Desktop 4.34+ with host networking enabled)
docker run -d --network host nginx:alpine

# Connect a running container to an additional network
docker network connect my-net existing-container

# Disconnect from a network
docker network disconnect my-net existing-container

10. Resource Limits and Why Containers Get Killed

By default a container may use as much memory and CPU as the host will give it, so one runaway process can starve everything else on the machine. Limits are set per container at run time.

bash
# Hard memory limit. The container is killed if it exceeds this.
docker run -d --memory 512m --name web nginx:alpine

# Disable swap for the container by setting swap equal to the memory limit.
# Without this, a container over its memory limit may swap instead of failing,
# which is usually slower and harder to diagnose than being killed.
docker run -d --memory 512m --memory-swap 512m nginx:alpine

# Limit CPU. 1.5 means one and a half cores' worth of quota.
docker run -d --cpus 1.5 nginx:alpine

Watch usage against the limit:

bash
# Live CPU, memory, network and disk per container. MEM USAGE / LIMIT is the
# column that matters — it shows the limit being approached before it is hit.
docker stats

# One snapshot instead of a live view
docker stats --no-stream

What it looks like when a container hits its memory limit. The kernel's OOM killer terminates the process with SIGKILL, and the container exits with code 137 (128 + 9, where 9 is SIGKILL). Under a restart policy this presents as a restart loop: it starts, memory climbs, it is killed, it starts again.

bash
# Was it the memory limit, or something else?
docker inspect web --format 'OOMKilled={{.State.OOMKilled}} ExitCode={{.State.ExitCode}}'

⚠️ Read those two fields together. OOMKilled=true means the memory limit was the cause. ExitCode=137 with OOMKilled=false means something else sent SIGKILL — an orchestrator stopping the container, a docker kill, or a process supervisor inside the image. Treating every 137 as an out-of-memory event sends you tuning a limit that was never the problem.

💡 Raising the limit is the fix only when the workload genuinely needs the memory. If usage climbs steadily and never levels off, the limit is reporting a leak rather than causing one, and a higher limit buys time instead of fixing it.


11. Docker Compose Quick Start

Docker Compose lets you define multi-container apps in a single YAML file and manage them with one command. If you have more than one container that work together, use Compose.

bash
# Start services defined in docker-compose.yml
docker compose up -d

# Stop and remove the containers and networks (named volumes are kept; add -v to delete them)
docker compose down

See the full Docker Compose guide for detailed coverage of services, networking, volumes, health checks, and production patterns.


12. Common Troubleshooting

"permission denied" when running docker commands:

bash
# Add your user to the docker group (Linux) — this grants root-level access to the host
sudo usermod -aG docker $USER
# Then log out and log back in

"port is already allocated":

bash
# Find what is using the port
sudo lsof -i :8080
# Or change the host port mapping: -p 8081:80 instead of -p 8080:80

Container exits immediately (won't stay running):

bash
# Check the logs to see why it exited
docker logs <container-name>

# Run interactively to debug
docker run -it <image> sh

Disk space — Docker can consume significant disk:

bash
# See disk usage breakdown
docker system df

# Remove all stopped containers, unused networks, dangling images, and build cache
docker system prune

# Also remove unused anonymous volumes (careful — this deletes data). Named volumes are
# left alone; docker volume prune --all removes unused named ones too
docker system prune --volumes

DNS resolution issues inside containers:

bash
# Test DNS from inside a container
docker run --rm alpine nslookup google.com

# If failing, try specifying a DNS server
docker run --dns 8.8.8.8 --rm alpine nslookup google.com

13. Useful Commands Cheat Sheet

bash
# List running containers
docker ps

# List all containers (including stopped)
docker ps -a

# Follow logs of a container
docker logs -f <container>

# Execute a command inside a running container
docker exec -it <container> sh

# Stop a container gracefully
docker stop <container>

# Kill a container immediately
docker kill <container>

# Remove a stopped container
docker rm <container>

# ⚠️ Stops and removes EVERY container on this host, not just this project's
docker stop $(docker ps -aq) && docker rm $(docker ps -aq)

# Copy files between host and container
docker cp <container>:/path/in/container ./local-path
docker cp ./local-file <container>:/path/in/container

# Show real-time resource usage
docker stats

# ⚠️ Aggressive cleanup: removes all stopped containers, unused networks, every image not
# used by a container, all build cache and unused anonymous volumes (named volumes are kept)
docker system prune -a --volumes
Terms used here (6)

Short definitions for the vocabulary on this page, in case any of it is new. Each links to the full entry.

Container
A running instance of an image: an isolated process with its own view of the filesystem, network and other system resources, plus a thin writable layer on top of the image's read-only layers that is discarded when the container is deleted. Containers share the host OS kernel, making them far more efficient than traditional virtual machines.
Container Registry
A service for storing and distributing container images. A registry holds many repositories, each a collection of related images. Registries like Docker Hub, GitHub Container Registry, and Amazon ECR allow teams to push, pull, and version their images as part of the CI/CD pipeline.
Docker
A platform for building, shipping, and running applications inside containers. Docker uses OS-level virtualization to deliver software in portable, reproducible packages that work identically across development, staging, and production.
Docker Compose
A tool for defining and running multi-container Docker applications using a YAML configuration file. With a single command you can spin up all the services your app needs — databases, caches, workers — in an isolated environment.
ECS
Amazon Elastic Container Service — a fully managed container orchestration service from AWS. AWS runs the orchestration control plane for you; the containers run on capacity you choose — AWS Fargate or ECS Managed Instances, where AWS manages the servers, or EC2 instances you manage yourself. ECS integrates tightly with other AWS services like ALB and CloudWatch.
Image
A read-only template used to create containers. A container image packages application code, runtime, libraries, and dependencies into a single artifact that can be versioned, shared via registries, and run identically on any compatible host.

All 59 terms in the glossary