Skip to content
Home
Docker for Beginners: What, Why, and How

Docker for Beginners: What, Why, and How

Docker Docker 8 min read 1537 words Beginner ExcellentWiki Editorial Team

Docker packages your application and its dependencies into a lightweight, portable container. Instead of installing Python, Node, databases, and system libraries directly on your machine, Docker bundles everything your app needs into a self-contained unit that runs identically on any system — your laptop, a teammate’s machine, a staging server, or a production cluster.

Why Use Docker?

Before Docker, the most common complaint among developers was “it works on my machine.” Environment differences between development, staging, and production caused subtle bugs that were hard to reproduce. Docker solves this by shipping the entire environment alongside the code.

Key benefits include:

  • Consistency — The same image runs the same way everywhere
  • Isolation — Each application gets its own environment without conflicts
  • Portability — Run on Linux, macOS, Windows, or any cloud provider
  • Scalability — Spin up dozens of containers from the same image in seconds
  • Efficiency — Containers share the host OS kernel, using far fewer resources than VMs

Containers vs Virtual Machines

The most common question newcomers ask is how containers differ from virtual machines. While both provide isolation, they work at different levels of the stack.

┌─────────────────────┐  ┌─────────────────────┐
│   App A   │  App B  │  │  App A  │  App B    │
├───────────┴─────────┤  ├─────────┴───────────┤
│  Guest OS │ Guest OS│  │   Docker Engine     │
├─────────────────────┤  ├─────────────────────┤
│    Hypervisor       │  │   Host OS           │
├─────────────────────┤  ├─────────────────────┤
│    Host OS          │  │    Hardware         │
├─────────────────────┤  └─────────────────────┘
│    Hardware         │
└─────────────────────┘
    Virtual Machines             Containers

Key differences:

VMsContainers
Boot timeMinutesSeconds
SizeGBsMBs
OS per unitFull OSShared host OS
IsolationStrongProcess-level
Resource usageHighLightweight
KernelSeparate per VMShared with host

VMs virtualize the hardware and run a full guest operating system, which makes them heavy but provides strong isolation. Containers virtualize the operating system and share the host kernel, making them fast and efficient but with slightly weaker isolation. For most development and microservice use cases, containers are the better choice.

Installing Docker

Getting started with Docker is straightforward. On Linux, you install the Docker engine directly. On macOS and Windows, Docker Desktop provides a graphical interface and includes everything you need.

# Ubuntu / Debian
sudo apt update
sudo apt install docker.io

# macOS / Windows
# Download Docker Desktop from docker.com

# Verify
docker --version
docker run hello-world

After installation, run docker run hello-world to verify everything works. This command downloads a tiny test image and runs it in a container. If you see a welcome message, Docker is working correctly.

Your First Docker Container

Let’s run a few real containers to see Docker in action:

# Run an interactive Ubuntu container
docker run -it ubuntu bash

# Run Nginx in the background
docker run -d -p 8080:80 nginx

# Visit http://localhost:8080

The -it flag gives you an interactive terminal inside the container — useful for exploring or debugging. The -d flag runs a container detached (in the background), and -p 8080:80 maps port 8080 on your host to port 80 inside the container. Now open http://localhost:8080 in your browser to see the Nginx welcome page.

Dockerfile

A Dockerfile is a recipe for building a Docker image. It contains a series of instructions that tell Docker how to set up your application environment.

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["python", "app.py"]

Build and run

docker build -t my-app .
docker run -p 8000:8000 my-app

docker build -t my-app . reads the Dockerfile in the current directory, executes each instruction, and produces an image tagged my-app. Each instruction creates a layer that Docker caches, so subsequent builds are faster — only changed layers are rebuilt.

Common Commands

Here’s a cheat sheet of the most frequently used Docker commands:

# Images
docker images                         # list images
docker pull nginx                     # download image
docker rmi nginx                      # remove image

# Containers
docker ps                             # running containers
docker ps -a                          # all containers
docker run nginx                      # create + start
docker start CONTAINER_ID             # start existing
docker stop CONTAINER_ID              # stop
docker rm CONTAINER_ID                # remove

# Logs and exec
docker logs CONTAINER_ID              # view logs
docker logs -f CONTAINER_ID           # follow logs
docker exec -it CONTAINER_ID bash     # shell inside container

# Cleanup
docker system prune                   # remove unused containers, images, networks
docker system prune -a                # remove everything unused

Docker Compose

Real-world applications rarely consist of a single service. A web app typically needs a backend API, a database, a cache, and maybe a message queue. Docker Compose lets you define and run multi-container applications from a single YAML file.

# docker-compose.yml
services:
  web:
    build: .
    ports:
      - "8000:8000"
    depends_on:
      - db

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
docker compose up -d       # start all services
docker compose down        # stop and remove
docker compose logs -f     # follow all logs

With a single docker compose up -d, you start your entire stack. The depends_on setting ensures the database starts before the web service. Volumes persist data even after containers are removed — your database won’t lose data when you restart.

Useful Dockerfile Instructions

Dockerfiles support many instructions for different use cases. Multi-stage builds are one of the most powerful patterns:

FROM node:20-alpine AS builder  # multi-stage build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html

This two-stage build first compiles the application in a Node environment, then copies only the compiled output into a lean Nginx image. The final image contains no build tools, no source code, and no unnecessary packages — just the static files and the web server.

# Layer caching optimization
COPY requirements.txt .      # COPY first, then RUN
RUN pip install -r requirements.txt  # cached if requirements.txt unchanged
COPY . .                     # only then copy source

Docker Images and Registries

Images are the blueprints for containers. You can pull pre-built images from registries like Docker Hub, or build your own. Docker Hub hosts millions of public images for databases, programming languages, tools, and applications.

# Search for images
docker search postgres

# Pull an image without running it
docker pull alpine:3.19

# Tag an image for pushing
docker tag my-app username/my-app:1.0.0

# Push to Docker Hub (requires login)
docker push username/my-app:1.0.0

Use private registries (AWS ECR, Google Artifact Registry, GitHub Container Registry) for proprietary images. Always pin image versions in production to prevent unexpected changes.

Best Practices

  • Use specific tagspython:3.11-slim, not python:latest
  • Use .dockerignore — exclude node_modules, __pycache__, .git
  • Minimize layers — combine RUN commands with &&
  • Don’t run as root — use USER instruction
  • Multi-stage builds — keep final images small
  • Scan for vulnerabilitiesdocker scout quickview
  • Set resource limits — prevent a single container from using all host resources

FAQ

What is the difference between docker stop and docker kill?

docker stop sends a SIGTERM signal and waits for the container to shut down gracefully (default 10 seconds). docker kill sends SIGKILL immediately. Always use stop first; only use kill if the container is unresponsive.

How do I copy files between my host and a container?

Use docker cp. Copy from host to container: docker cp file.txt container_id:/path/. Copy from container to host: docker cp container_id:/path/file.txt .

Why is my container exiting immediately?

A container exits when its main process finishes. For example, docker run ubuntu starts and immediately exits because Ubuntu has no default command. Add a command like docker run -it ubuntu bash or use a service image like nginx that runs a long-running process.

How do I free up disk space used by Docker?

Use docker system prune -a --volumes to remove all unused containers, images, networks, and volumes. Be careful with --volumes as it deletes data volumes that are not referenced by any container.

Can I run Docker on Windows or macOS without Docker Desktop?

Yes. Use Colima (macOS), Rancher Desktop, or Podman. These provide Docker-compatible container runtimes without the Docker Desktop licensing requirements.


Related: Fix docker-compose errors and learn Dockerfile best practices.

Docker Compose for Multi-Service Applications

Docker Compose (defined in compose.yaml or docker-compose.yml) orchestrates multi-container applications. A typical web app stack includes a web server (Nginx), application server (Python/Node), database (PostgreSQL), and cache (Redis). Services are defined under the services key with build, image, ports, environment, and volumes directives. The depends_on key controls startup ordering. Health checks (healthcheck with test, interval, retries) ensure services are actually ready, not just started. Compose’s profiles property groups services for specific scenarios (development, testing, production). Environment variables are loaded from .env files automatically. Networks isolate service groups: a frontend network connects Nginx to the app, while a backend network connects the app to the database. The --profile flag enables selective service startup: docker compose --profile debug up -d starts debugging tools alongside core services.

Development Best Practices

Use bind mounts for development to reflect code changes instantly without rebuilds. Use Docker Compose watch (develop section with watch) to auto-rebuild on file changes. Set stdin_open: true and tty: true for interactive debugging with docker attach. Override command with command: sleep infinity for container exploration. Use .dockerignore to exclude node_modules, .git, and build artifacts. Label containers with org.opencontainers.image.source for provenance tracking.

Section: Docker 1537 words 8 min read Beginner 756 articles in section Report inaccuracy Back to top