Posted on 25 Jul 2026
This is the first article of the Getting Started with Docker series. By the end of this article you will understand what Docker is, what problems it solves, and you will have your first containerized web server running on your machine in minutes — without writing a single line of Dockerfile.
You should read this article if:
All the commands shown here work identically with both Docker and Podman. Where there are differences, they are explicitly noted.
Before diving into commands, it is worth spending a minute on the problem Docker actually solves.
Imagine you are a developer. You write an application on your laptop, it works perfectly. Then you hand it off to a colleague or deploy it to a server — and it breaks. Different OS version, different library installed, different path. This is the classic “it works on my machine” problem, and it has tormented developers for decades.
Docker solves this by packaging your application together with everything it needs to run — runtime, libraries, configuration — into a single portable unit called a container. That container runs identically on your laptop, your colleague’s machine, and the production server.
This matters not just for individual developers. Modern applications are built as dozens (sometimes hundreds) of independent services — microservices — each with its own dependencies. Docker makes it possible to deploy, update, and scale each service independently, without interference.
Both Docker and Podman are tools for running containers. For everything covered in this series, they are interchangeable — the CLI commands are identical.
The key differences:
| Docker | Podman | |
|---|---|---|
| Architecture | Requires a background daemon (dockerd) |
Daemonless — runs containers directly |
| Privileges | Daemon runs as root | Rootless by default — more secure |
| License | Docker Desktop requires a paid license for enterprise use | Fully open source (Apache 2.0) |
| Installation on Mac | Docker Desktop | brew install podman + podman machine init |
| Compose support | docker compose (v2 plugin) |
podman compose (from Podman 4.7+) |
| OCI compatibility | Yes | Yes — same image format |
In short: if Docker Desktop is available and licensed for your use, use Docker. If you are on a corporate machine where Docker Desktop requires a commercial license, or if you prefer an open source rootless alternative, use Podman. The rest of this series works exactly the same either way.
Throughout this series, all commands are shown with
docker. If you are using Podman, simply replacedockerwithpodman— or set the aliasalias docker=podmanonce and forget about it.
On Linux, install Docker Engine following the official documentation. On Mac and Windows, Docker Desktop is the easiest option if your license allows it: download it from docs.docker.com/desktop.
On macOS, Podman uses a lightweight Linux virtual machine behind the scenes — the same approach Docker Desktop uses. Install and initialise it with three commands:
brew install podman
podman machine init
podman machine start
After this, podman is ready and behaves exactly like docker.
Before running anything, two concepts are essential.
A Docker image is a read-only, layered package that contains everything needed to run a specific application: the OS filesystem, runtime, libraries, configuration files, and the application itself.
Images are stored in registries. The most popular is Docker Hub, which hosts thousands of official and community images. When you run a container, Docker (or Podman) downloads the image from the registry if it is not already present on your machine.
Images are built in layers. Each instruction that modifies the filesystem adds a layer on top of the previous ones. Layers are cached and shared between images — this is what keeps images small and builds fast.
A container is a running instance of an image. You can think of the image as a class and the container as an object instantiated from it. Multiple containers can run from the same image simultaneously, each fully isolated from the others.
Containers share the host OS kernel but are isolated from each other using Linux kernel features: namespaces (for process, network, filesystem isolation) and cgroups (for resource limits like CPU and memory).
This is the fundamental difference from a Virtual Machine: a VM emulates an entire hardware stack and runs a separate OS kernel. A container shares the host kernel and is much lighter as a result.

We will explore this topic in depth in the next article.
Here is the minimal set of commands you need to start working with Docker. These are the ones you will use every day.
docker image ls
At the beginning, the list is empty.
docker pull nginx:alpine
This downloads the official Nginx image based on Alpine Linux — a very small (~40 MB) but fully functional web server image.
docker run -d -p 8080:80 --name my-nginx nginx:alpine
Breaking down the options:
-d — runs the container in detached mode (in the background, shell does not hang)-p 8080:80 — maps port 8080 on your host to port 80 inside the container--name my-nginx — gives the container a friendly name instead of a random onenginx:alpine — the image to useOpen your browser at http://localhost:8080 and you will see the Nginx welcome page. You just ran a web server without installing anything on your machine.
docker container ls
To see all containers including stopped ones:
docker container ls -a
docker logs my-nginx
docker exec -it my-nginx /bin/sh
The -it flags attach an interactive terminal. Note: Alpine-based images use sh instead of bash.
docker stop my-nginx
docker rm my-nginx
Or in one command:
docker rm -f my-nginx
docker image rm nginx:alpine
docker system prune
This removes all stopped containers, dangling images, and unused networks. Add -a to also remove unused images that are not referenced by any container.
Let us put it all together with a practical example. You will serve a custom HTML page through Nginx — without installing Nginx, without a Dockerfile, using only the official image and a single docker run command.
Create a file called index.html anywhere on your machine:
<!DOCTYPE html>
<html>
<head><title>Hello from Docker</title></head>
<body>
<h1>Hello, World!</h1>
<p>This page is served by Nginx running inside a container.</p>
</body>
</html>
Now run Nginx and mount that file into the container:
docker run -d \
-p 8080:80 \
--name hello-nginx \
-v $(pwd)/index.html:/usr/share/nginx/html/index.html:ro \
nginx:alpine
The -v option bind mounts your local file into the container’s web root. The :ro suffix makes the mount read-only inside the container (a good practice).
Open http://localhost:8080 in your browser:
Hello, World! This page is served by Nginx running inside a container.
You served a custom page without installing a web server, without writing a Dockerfile, and without touching your system configuration. When you are done:
docker rm -f hello-nginx
Your machine is exactly as it was before. Nothing installed, nothing left behind.
This first article introduced the core concepts and got you running immediately. In the upcoming articles we will progressively build on this foundation:
Dockerfile, build and tag a custom image, understand the layer cachedocker compose upEach article builds on the previous one around the same practical Nginx application, adding one concept at a time.
In this article we covered:
The next article goes deeper into the conceptual foundations: what actually makes a container different from a virtual machine, and what Linux kernel mechanisms make it all work.
If you enjoyed this article, don’t forget to give it a clap 👏, share it with your friends 🔗, and follow me for more tips and tutorials on software development 📘. Your support helps me create more content like this — thank you! 🙌