
Almost every cloud and DevOps job advert now mentions Docker somewhere. It is one of those tools that looks intimidating from the outside and turns out to be quite logical once you have used it for a week. This guide explains what containers actually are, why teams rely on them, and then walks you through a hands-on path: running your first container, writing your first Dockerfile, building and publishing an image, and finally running a small multi-container app with Docker Compose. Every command here is real and can be typed on your own machine.
What problem does Docker solve?
Imagine you build a small Python web app on your laptop. It works. You send it to a colleague and it fails, because they have a different Python version, a missing library, or a different operating system. You deploy it to a server and it fails again for a third reason. This is the old "it works on my machine" problem.
Docker solves it by packaging your application together with everything it needs to run: the runtime, the libraries, the system tools and the configuration. That package is called an image. When you run an image, you get a container: an isolated process that behaves the same on your laptop, your colleague's laptop, a CI server and a cloud virtual machine.
The result is predictable software. If the image runs in one place, it will almost always run the same way everywhere else that has Docker installed.
Containers explained in plain terms
A container is not a small virtual machine, even though it is often described that way. It is a normal process running on the host's Linux kernel, but with walls around it. The kernel uses two features to build those walls:
- Namespaces give the process its own view of the system: its own process list, network interfaces, hostname and filesystem.
- Control groups (cgroups) limit how much CPU and memory the process can use.
Because containers share the host kernel instead of booting a whole operating system, they start in seconds (often less) and use far fewer resources than virtual machines. This is also why understanding Linux helps so much with Docker. If you are still shaky on the command line, work through Linux skills for cloud jobs alongside this guide.
Containers vs virtual machines
| Virtual machine | Container | |
|---|---|---|
| What it includes | A full guest operating system plus your app | Your app plus its libraries, sharing the host kernel |
| Start-up time | Usually tens of seconds to minutes | Usually under a few seconds |
| Typical size | Gigabytes | Megabytes to a few hundred megabytes |
| Isolation | Strong, hardware-level | Good, process-level |
| Best for | Running different operating systems, strong isolation | Packaging and shipping applications consistently |
In practice, the two work together. In the cloud you usually run containers on virtual machines, such as an EC2 instance on AWS or a VM on Azure.
The key Docker vocabulary
Learn these words first. Most beginner confusion comes from mixing them up.
- Image: a read-only template containing your app and its dependencies. Think of it as a recipe that has already been cooked and frozen.
- Container: a running (or stopped) instance of an image. You can run many containers from one image.
- Dockerfile: a text file with instructions for building an image.
- Registry: a place to store and share images. Docker Hub is the best known public one; AWS has Amazon ECR, Azure has Azure Container Registry, and GitHub has GitHub Container Registry.
- Tag: a label on an image, such as
nginx:1.27ormyapp:v2. If you do not give a tag, Docker assumeslatest. - Volume: storage managed by Docker that survives when a container is deleted.
- Docker Compose: a tool for defining and running multi-container applications from a single YAML file.
If you prefer to see these ideas visually before typing commands, the interactive diagrams at CloudVision: Docker show how images, containers and registries relate.
Step 1: Install Docker
You have three sensible options:
- Docker Desktop on Windows or macOS. It installs the Docker Engine, the CLI and Compose in one go. On Windows it uses WSL 2, so enable that first. Docker Desktop is free for personal use, education and small businesses, but larger companies need a paid subscription, so check the licence terms on the official Docker site if you are using it at work.
- Docker Engine on Linux. On Ubuntu, follow the official "Install Docker Engine on Ubuntu" instructions on docs.docker.com, which add Docker's own package repository. Avoid random scripts from blog posts.
- A cloud VM. A small Linux VM on AWS, Azure or Google Cloud works well if your laptop is low on memory. If you go this route, set a budget alert before you start, because cloud is billed in dollars to your card and a forgotten VM keeps charging. Stop or delete it when you finish each session.
A note on laptops: Docker Desktop is comfortable with 8 GB of RAM and gets tight below that. If your machine struggles, a Linux VM in the cloud or a browser-based lab is a perfectly good alternative.
Once installed, check it works:
docker --version
docker compose version
docker run hello-worldThe last command downloads a tiny test image and runs it. If you see "Hello from Docker!", your setup is working.
On Linux, if you get a "permission denied" error on the Docker socket, add yourself to the docker group and log out and back in:
sudo usermod -aG docker $USERBe aware that membership of the docker group is effectively root access on that machine, so only do this on machines you control.
Step 2: Run real containers
Start a web server in one command:
docker run -d --name web -p 8080:80 nginx:1.27Here is what each part means:
-druns it in the background (detached).--name webgives the container a friendly name.-p 8080:80maps port 8080 on your machine to port 80 inside the container.nginx:1.27is the image and tag.
Open http://localhost:8080 in your browser and you will see the Nginx welcome page. Now explore:
docker ps # list running containers
docker logs web # see the container's output
docker exec -it web sh # open a shell inside the container
exit # leave that shell
docker stop web # stop it
docker ps -a # list all containers, including stopped ones
docker rm web # delete the container
docker images # list images on your machineNotice that deleting the container did not delete the image. Run docker run again and it starts instantly because the image is already cached locally.
Try an interactive container too:
docker run -it --rm ubuntu:24.04 bashYou now have a shell in a clean Ubuntu environment. Install something, break something, then type exit. The --rm flag deletes the container when it stops, so nothing you did remains. This makes containers a safe playground for experiments.
Step 3: Write your first Dockerfile
Running other people's images is useful, but the real skill is packaging your own application. Create a folder called hello-app with three files.
app.py:
from flask import Flask
import os
app = Flask(__name__)
@app.route("/")
def home():
name = os.environ.get("APP_NAME", "SKILL.SCH demo")
return f"Hello from {name} running in a container!\n"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)requirements.txt:
flask==3.0.3Dockerfile:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]Line by line:
FROMpicks a base image. Theslimvariant is smaller than the full Python image.WORKDIRsets the folder inside the image where the following commands run.COPY requirements.txt .thenRUN pip installinstalls dependencies. We copy the requirements file before the app code on purpose, which is explained below.COPY app.py .adds your code.EXPOSEdocuments which port the app listens on. It does not publish the port by itself;-pdoes that.CMDis the command that runs when a container starts.
Note that the app listens on 0.0.0.0, not 127.0.0.1. Inside a container, 127.0.0.1 means "only inside this container", so traffic from your browser would never reach it. This is one of the most common beginner bugs.
Build and run it:
cd hello-app
docker build -t hello-app:v1 .
docker run -d --name hello -p 5000:5000 -e APP_NAME="my first image" hello-app:v1
curl http://localhost:5000You should see Hello from my first image running in a container!. The -e flag passes an environment variable, which is how containers are usually configured.
Why the order of instructions matters
Docker builds images in layers, one per instruction, and caches each layer. When you rebuild, Docker reuses cached layers until it reaches the first instruction whose inputs have changed, then rebuilds everything after it.
Because we copied requirements.txt and installed dependencies before copying app.py, editing your code only rebuilds the last couple of layers. If you had written COPY . . at the top, every small code change would reinstall all your dependencies. On a slow or metered connection that difference is very noticeable, so get into the habit early.
Add a .dockerignore file
Create .dockerignore next to the Dockerfile so you do not copy junk or secrets into the image:
__pycache__/
*.pyc
.git
.env
venv/Step 4: Tag and push your image to a registry
An image on your laptop is only useful to you. To share it, push it to a registry. With a free Docker Hub account:
docker login
docker tag hello-app:v1 YOUR_DOCKERHUB_USERNAME/hello-app:v1
docker push YOUR_DOCKERHUB_USERNAME/hello-app:v1Anyone (or any server) can now run it with docker run YOUR_DOCKERHUB_USERNAME/hello-app:v1. In company settings you will more often push to a private registry such as Amazon ECR or Azure Container Registry, but the commands follow the same pattern of login, tag and push.
Two habits worth building now:
- Use meaningful tags such as a version number or Git commit hash, not just
latest.latestdoes not mean "newest"; it is simply the default tag name, and relying on it makes it hard to know what is actually running. - Never put passwords or API keys in a Dockerfile. Anything in an image layer can be extracted by anyone who can pull the image. Pass secrets at runtime instead.
Step 5: Keep data with volumes
Containers are meant to be disposable. When you delete one, any data written inside it is gone. For databases and uploads, use a volume:
docker volume create pgdata
docker run -d --name db \
-e POSTGRES_PASSWORD=devpassword \
-v pgdata:/var/lib/postgresql/data \
postgres:16Delete the container and create a new one with the same -v pgdata:... option, and your data is still there. During development you can also use a bind mount, which maps a folder on your machine into the container, for example -v "$(pwd)":/app. Bind mounts are handy for live code editing; named volumes are better for data Docker should manage.
Step 6: Run a multi-container app with Docker Compose
Real applications rarely run as a single container. A typical web app needs the app itself, a database and perhaps a cache. Typing several long docker run commands and creating networks by hand gets tiring fast. Docker Compose lets you describe the whole set-up in one file.
Let us extend the Flask app to count visits in Redis. Update app.py:
from flask import Flask
import os
import redis
app = Flask(__name__)
cache = redis.Redis(host=os.environ.get("REDIS_HOST", "redis"), port=6379)
@app.route("/")
def home():
count = cache.incr("hits")
return f"Hello! This page has been viewed {count} times.\n"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)Update requirements.txt:
flask==3.0.3
redis==5.0.8Now create compose.yaml in the same folder:
services:
web:
build: .
ports:
- "5000:5000"
environment:
REDIS_HOST: redis
depends_on:
- redis
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
volumes:
redis-data:Start everything:
docker compose up -d --build
curl http://localhost:5000
curl http://localhost:5000The counter goes up each time. A few things are happening that are worth understanding:
- Compose creates a private network for the project, and each service can reach the others by service name. That is why the app connects to the host called
redis. build: .tells Compose to build the image from your Dockerfile, whileimage: redis:7-alpinepulls a ready-made image.depends_oncontrols start-up order only. It does not wait for Redis to be ready. For databases that take a while to start, look uphealthchecktogether withdepends_onandcondition: service_healthyin the Compose documentation.- The
version:line you see at the top of older Compose files is no longer needed. Modern Docker Compose (thedocker composecommand, with a space) ignores it.
Useful Compose commands:
docker compose ps # status of the services
docker compose logs -f web # follow the web service's logs
docker compose down # stop and remove containers and network
docker compose down -v # also remove volumes (deletes data)Congratulations: you have gone from a single test container to a small multi-service application defined as code. That is exactly the kind of work containers are used for in real teams.
Common beginner mistakes
- Binding the app to 127.0.0.1 inside the container. Listen on
0.0.0.0, then publish the port with-p. - Forgetting `-p`.
EXPOSEin a Dockerfile does not make the app reachable from your browser. - Using `latest` everywhere. Pin versions such as
python:3.12-slimandpostgres:16so builds are repeatable. - Putting secrets in images. Use environment variables,
.envfiles that are excluded from Git and the image, or your cloud provider's secrets service. - Storing important data inside the container. Use volumes.
- Huge images. Start from
slimoralpinebases where suitable, clean package caches, and use a.dockerignore. Later, learn multi-stage builds. - Running out of disk space. Old images and stopped containers pile up. Check with
docker system dfand clean up withdocker system prune(read what it will delete before confirming). - Running everything as root. For production images, add a non-root user with the
USERinstruction. - Confusing `docker-compose` and `docker compose`. The hyphenated version is the older standalone tool. Use
docker compose.
Where Docker fits in a DevOps career
Docker is rarely the whole job, but it sits in the middle of most modern delivery pipelines:
- Git stores the code and the Dockerfile. See Git and GitHub for DevOps.
- CI/CD pipelines build the image, run tests inside it and push it to a registry. The CI/CD with GitHub Actions guide shows how.
- Kubernetes runs those images at scale across many machines. Once Compose feels comfortable, move on to Kubernetes for beginners.
- Terraform creates the cloud infrastructure the containers run on.
Interviewers often ask you to explain the difference between an image and a container, how layer caching works, what happens to data when a container is deleted, and how containers talk to each other. If you can answer those from your own hands-on experience, you are ahead of many applicants.
A realistic 6-week practice plan
This assumes about an hour a day on weekdays and a longer session at the weekend. If power or internet is unreliable where you are, pull the images you need when you have a good connection; once they are cached locally, most of the practice works offline.
| Week | Focus | What to produce |
|---|---|---|
| 1 | Linux basics refresher, install Docker, run hello-world, nginx and ubuntu containers | Notes on run, ps, logs, exec, stop, rm in your own words |
| 2 | Write Dockerfiles for a small Python or Node.js app; learn layer caching and .dockerignore | A working image that you rebuild after code changes and observe the cache |
| 3 | Tags, registries, pushing to Docker Hub; environment variables and configuration | A public image anyone can run with one command |
| 4 | Volumes, bind mounts and Docker networks; run Postgres and connect to it | A short write-up showing data surviving container deletion |
| 5 | Docker Compose: app plus database plus cache, health checks, logs | A compose.yaml that starts the whole stack with one command |
| 6 | Image hygiene: smaller images, multi-stage builds, non-root users; add a GitHub Actions workflow that builds the image | A GitHub repository with a clear README, ready for your portfolio |
For extra repetition, the Docker Arena in CloudArcade gives you short challenges to practise commands until they become second nature. When you finish week 6, add the project to your portfolio; cloud portfolio projects has ideas for growing it further.
Frequently asked questions
Do I need to know programming to learn Docker?
You need to be able to read simple code and edit files, but you do not need to be a developer. Most Docker work is about packaging, configuration and the command line. A little Python or JavaScript helps you build test apps to containerise.
Is Docker still relevant now that Kubernetes is popular?
Yes. Kubernetes runs containers, and the images it runs are commonly built with Docker or Docker-compatible tools from a Dockerfile. Kubernetes no longer uses Docker Engine as its runtime internally, but images built with Docker run on it without changes. Learning Docker first makes Kubernetes much easier to understand.
Can I learn Docker on Windows?
Yes. Install Docker Desktop with WSL 2 enabled. You will still be running Linux containers, so practising Linux commands inside WSL is a good habit.
Is there a Docker certification?
There has been a Docker Certified Associate exam, but the ownership and availability of Docker certifications has changed over the years, so check the official site for the current situation before paying for anything. Many employers care more about a working project on GitHub. If you want a certification path that includes containers, cloud provider exams and Kubernetes certifications such as the CKA are common next steps.
How much does it cost to practise?
Docker Engine is free and open source, and Docker Desktop is free for personal and educational use. You can do everything in this guide on your own laptop without spending money. If you use a cloud VM instead, use free-tier eligible sizes where available, set a budget alert, and shut the VM down after each session.
Get help with your next step
Docker is one of the highest-value skills you can add as a beginner, because it connects directly to CI/CD, Kubernetes and cloud deployment. If you would like structured training, mentoring and projects reviewed by practising engineers, explore our courses, or book a free consultation and we will help you plan a route from Docker basics to your first cloud or DevOps role.

