Before running your first container, there is a foundation that needs to be prepared: basic Linux CLI skills (filesystem navigation, environment variables, the concepts of PID, port, and localhost) up to choosing your Docker environment and verifying the client + daemon installation.

Welcome to the Learn Docker series! This series will take you from zero to deploying production-grade applications with Docker: starting with prerequisites and environment setup, the history of containerization, Docker architecture, running your first container, container lifecycle, Dockerfile, multi-stage builds, storage, networking, Docker Compose, registry, security, observability, Swarm, CI/CD, all the way to a complete production architecture case study. A total of 27 episodes that will change the way you think about application deployment.
Why is Docker so important? Because Docker is the answer to the oldest problem in the software industry: "It works on my machine!" — an application that runs smoothly on your laptop suddenly breaks on the production server because of different library versions, a different operating system, or missing dependencies. Docker wraps your application along with its entire environment — libraries, configuration files, even the operating system — into a single standard package that runs identically anywhere. A DevOps Engineer who doesn't understand Docker is like a mechanic without a toolbox: they can work, but only in one place.
Episode 0 is the roadmap. Before talking about containers, we need to make sure of three things: (1) the basic Linux skills you must master, (2) the Docker environment choice that suits you best, and (3) verification that Docker is installed and running flawlessly. Don't skip around — a shaky foundation will make this whole series feel heavy. Let's begin.
Docker is a product born from Linux and it lives in the terminal. You will type hundreds of Docker commands in the shell, check logs, copy files, and read daemon output. If your terminal fundamentals are shaky, you'll get lost halfway through. Here are the skills you must have before continuing.
You must be comfortable "moving around" the Linux filesystem. Understand the difference between absolute paths (/etc/nginx/nginx.conf) and relative paths (../config/app.conf), the meaning of ~ (home user), . (current directory), and .. (parent directory). Think of the filesystem as a warehouse: you have to know where things are stored before you can organize them.
| Command | Function | Analogy |
|---|---|---|
pwd | Show the current directory | Checking our position on the map |
cd | Change directory | Walking into another room |
ls | List directory contents | Opening a door and seeing what's in the room |
mkdir | Create a directory | Building a new room |
rm | Remove files/directories | Shredding documents (no recycle bin!) |
cp | Copy files | Photocopying documents |
mv | Move / rename | Moving a file from the desk to a shelf |
You don't need to memorize every flag option. What matters is that you've used them and understand what they do, because almost all Docker work starts from these simple commands — for example, when we later read configuration files or inspect the /var/lib/docker directory.
An environment variable is a name=value pair that the system inherits into every running process. Think of it like an office bulletin board: all employees (processes) can read it, and the values on it determine their behavior. Variables like $HOME, $USER, and $PATH are always present in every Linux shell.
echo "$HOME"
echo "$USER"
export MY_VAR="nilai saya"
echo "$MY_VAR"Notice the export NAMA=nilai syntax — without export, the variable only lives in your shell and isn't inherited by child processes. This matters because Docker is a separate process: when we later set variables for a container with docker run -e, we're sending values from the host into the container process.
These three concepts are the body language of the networking and process management world — you must understand them because they'll show up in almost every episode:
PID (Process ID) — a unique number the kernel assigns to every running process. When you run docker run, the Docker daemon creates new processes that each get their own PID on the host. Check your processes with ps aux or top; the Docker processes show up as dockerd and containerd.
Port — a numbered "door" (0–65535) on a machine that applications use to accept connections. Standard web servers sit on port 80, PostgreSQL databases on 5432, Redis on 6379. In Docker, ports inside a container must be mapped to host ports so they can be reached from outside — a topic we'll dig into in episode 3.
localhost — the name that refers to the machine itself (loopback, IP 127.0.0.1). When you open http://localhost:8080, you're accessing your own machine. Later, "localhost inside the container" is different from "localhost on the host" — this difference is the source of a classic Docker beginner's confusion.
ps aux | grep docker
ss -tlnpss -tlnp shows the ports that are currently "listening" along with the processes using them — an invaluable tool when ports clash later.
Docker configuration files (Dockerfile, compose.yaml, daemon.json) are plain text files. You need an editor you're comfortable with: nano (most beginner-friendly), vim (steep learning curve but most efficient), or VS Code with remote SSH — the industry standard for DevOps. Pick one and be consistent; every episode in this series will require you to write and edit configuration files.
Tip
Get in the habit of experimenting in a safe environment. Create a dedicated practice folder, for example ~/docker-lab, and feel free to break anything inside it. Docker will create and destroy lots of resources — better to do it in your own laboratory.
Docker runs on all major operating systems, but the installation methods differ and — more importantly — how it works behind the scenes differs. Understand the trade-offs of each choice:
| Environment | How It Works | Pros | Cons |
|---|---|---|---|
| Docker Engine (Linux native) | Containers run directly on the Linux kernel | Fastest, most authentic, exactly like production servers | Requires Linux installed |
| Docker Desktop (macOS) | Containers run in a small Linux VM | Convenient, GUI, macOS integrated | VM overhead, license for large enterprises |
| Docker Desktop (Windows WSL2) | Containers run in a WSL2 distro (real Linux kernel) | Real Linux kernel on Windows without reinstalling | WSL2 setup, layered filesystem |
| OrbStack (macOS) | Containers run in a very lightweight Linux VM | Super lightweight, fast, battery-efficient | macOS only |
| Rancher Desktop | Open-source Docker Desktop alternative | Fully free, can use pure containerd | Less polished than Docker Desktop |
Important
Main recommendation for this series: Docker Engine on Linux (Ubuntu Server, Debian, or Rocky Linux). This isn't just a preference — containers on native Linux run without a VM layer, so every command, performance characteristic, and behavior matches the production servers you'll be managing. If you're on Windows, use WSL2; on macOS, OrbStack or Docker Desktop. Whichever you choose, the docker commands in this series are identical across all platforms.
There are two ways to install Docker Engine on the Debian/Ubuntu family, and both are valid — the difference lies in the level of freshness and control:
docker.io package — maintained by the distro maintainers, tested against that distro, but the version can lag behind by a few months. Good enough for learning and most production use.docker-ce (Community Edition) package straight from Docker Inc, always the latest version, and the standard choice for production servers.For Ubuntu/Debian:
sudo apt update
sudo apt install -y docker.io
sudo systemctl enable --now dockerFor Rocky Linux / the RHEL family:
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now dockerNotice the last package: docker-compose-plugin — the Compose v2 plugin (the docker compose command, with a space) that we'll use in phase 3 of this series. Without this package, you'd only have the separate, older docker-compose.
Once installed, verify with the following three commands. This is the "stress test" of your Docker environment:
docker version
docker info
docker compose versionThe docker version output is split into two major sections — and this is our first key concept:
Client:
Version: 27.4.1
Server:
Engine:
Version: 27.4.1
API version: 1.46 (minimum version 1.24)docker program you type in the terminal. It only sends commands.dockerd daemon that actually does the work: running, stopping, and managing containers. If the Server/Engine section doesn't appear or you get a "Cannot connect to the Docker daemon" error, the daemon isn't running — check with sudo systemctl status docker and start it with sudo systemctl start docker.docker info gives a health summary of the daemon: number of containers, storage driver (overlay2), kernel, and other important settings. docker compose version confirms that Compose v2 is ready to use. You'll use all three of these commands constantly, so memorize them.
By default, the docker command requires root access because the daemon listens on the Unix socket /var/run/docker.sock, which is owned by root. Every time you run it without sudo, you'll get a permission denied while trying to connect to the Docker daemon socket error. The fix: add your user to the docker group.
sudo usermod -aG docker $USER
newgrp docker
id -nGusermod -aG docker $USER adds the current user to the docker group, and newgrp docker activates the new membership without logging out. Verify with id -nG — make sure docker appears in the group list. After this, docker run works without sudo.
Warning
Members of the docker group have root-equivalent privileges on the machine. Because the daemon runs containers as root, anyone who can talk to the daemon socket can run commands with full privileges — for example, bind-mounting the host's / directory into a container and then reading the entire filesystem. So: don't casually add users to the docker group, and never run docker commands you don't understand. For single-user development it's safe. For multi-user servers, consider rootless Docker, which we'll cover in episode 13.
Now let's tie together everything we've prepared into a single test script that confirms your environment is truly ready to follow this series:
#!/bin/bash
echo "=== Docker Setup Verification ==="
docker version --format 'Client version : {{.Client.Version}}'
docker version --format 'Server version : {{.Server.Version}}'
docker compose version
echo "--- Info ringkas daemon ---"
docker info --format 'Containers : {{.Containers}}'
docker info --format 'Images : {{.Images}}'
docker info --format 'Kernel : {{.KernelVersion}}'
echo "--- Keanggotaan grup docker ---"
if groups | grep -q docker; then
echo "OK: user $USER ada di grup docker"
else
echo "PERLU: jalankan 'sudo usermod -aG docker $USER' lalu logout-login"
fibash verify-docker-setup.shIf you see the Server version printed (the daemon responding without sudo) and the "OK" line, your environment is officially ready. If "Cannot connect to the Docker daemon" appears, start from sudo systemctl status docker — 90% of these cases are a daemon that isn't running.
Daemon not running. The docker run command fails with Cannot connect to the Docker daemon. Fix: sudo systemctl enable --now docker and make sure the service is active.
Not using sudo before the user joins the docker group. Error permission denied while trying to connect to the Docker daemon socket. Fix: usermod -aG docker $USER + newgrp docker, then check id -nG.
Skipping verification. Missing docker version and docker info means you don't know whether the client and server are talking. Make verification a habit after any installation.
Weak PID/port fundamentals. Without understanding ports and ss -tlnp, you'll be confused when ports clash in episode 3. Strengthen this foundation now.
Confusing Compose v1 and v2. The docker-compose command (v1) is different from docker compose (v2). This series uses v2. Make sure docker compose version returns a 2.x version.
Note
The right debugging culture: read the full error message before asking Google. Docker errors are almost always explicit — they'll say "permission denied", "Cannot connect", or "port is already allocated". Match that message against your command, check the line number, and fix things one by one.
In episode 0 you've secured three foundations: basic Linux CLI skills (filesystem navigation with cd, ls, mkdir, rm, environment variables, plus the concepts of PID, port, and localhost), the right Docker environment (Docker Engine on Linux as the main recommendation; Docker Desktop/WSL2, OrbStack, or Rancher Desktop for other platforms), and installation plus verification that your Docker client and daemon run properly via docker version, docker info, docker compose version, and your first test script.
Points to take with you:
docker commands run without sudo.docker group is root-equivalent — understand the risk before using it.docker version and docker info.Remember, the Learn Docker series consists of 27 episodes that build on each other: the history of containerization, architecture, running containers, Dockerfile, storage, networking, Compose, security, observability, Swarm, and CI/CD plus production case studies. Episode 0 is the first brick — and you've just laid it perfectly. In the next episode we'll step back for a moment to understand the history, background, and why Docker was born: from the bare-metal era, the virtual machine era, the "It works on my machine!" problem, to the Linux kernel technologies (cgroups, namespaces) that became the foundation of containerization. See you in episode 1 — and happy building of your Docker lab!