Docker for Beginners: Learn Containers in 1 Hour (2026)
Learn Docker from scratch in 2026. Understand containers vs images, write your first Dockerfile, and master essential commands in under an hour.
Get more content like this on Telegram!
Daily AI tips, notes & resources — free
I still remember the first time Docker made sense to me. I was trying to run a Python app on a colleague's machine and it failed because their Python version was different. Someone said "just use Docker" and I had no idea what that meant. Three hours later I was hooked.
If that sounds familiar, this post is for you. By the time you finish reading, you'll understand what Docker actually does, why developers everywhere use it, and you'll have written your first working Dockerfile. The whole thing should take about an hour if you follow along.
Why Docker Exists (The Real Story)
The phrase "it works on my machine" is basically a meme at this point. The problem it describes is genuinely annoying. Your app might depend on Node.js 20, a specific version of OpenSSL, an environment variable set in a certain way, and a config file in /etc/app/. Your teammate has different versions of all of those. Your production server has a different OS entirely.
Docker solves this by bundling your app together with everything it needs to run. The bundle — called a container — behaves identically everywhere Docker is installed. Your laptop, your CI server, AWS, wherever.
According to the 2023 Stack Overflow Developer Survey, Docker is the most used tool in the "other tools" category, with over 52% of professional developers using it regularly. That number has only grown.
Images vs Containers: Getting This Right
This is the concept most beginners muddle. Let me be direct about it.
An image is a frozen snapshot. It includes your application code, the runtime it needs (Node, Python, Java, whatever), system libraries, and configuration. Images are built from a Dockerfile and stored either locally or in a registry like Docker Hub. They're read-only.
A container is a running instance of an image. When you start a container, Docker takes the image, adds a thin writable layer on top, and runs the process. You can have ten containers all running from the same image simultaneously, each isolated from the others.
Think of it like this — an image is a recipe, a container is the meal you cooked from it.
Image (blueprint, read-only)
└── Container 1 (running, writable layer)
└── Container 2 (running, writable layer)
└── Container 3 (stopped, writable layer)
When a container stops, its writable layer is gone unless you explicitly save it. This is why persistent data needs volumes (more on that in a moment).
Installing Docker
Head to docs.docker.com/get-docker and grab Docker Desktop for your OS. It installs everything you need: the daemon, CLI, and a GUI dashboard.
Once installed, verify it works:
docker --version
# Docker version 26.1.4, build 5650f9b
docker run hello-world
That last command pulls a tiny test image from Docker Hub and runs it. If you see "Hello from Docker!" printed out, you're good.
Writing Your First Dockerfile
A Dockerfile is a text file with instructions that tell Docker how to build an image. Let's build a simple Node.js app.
First, create a new directory and add these files:
app.js
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from Docker!\n');
});
server.listen(3000, () => {
console.log('Server running on port 3000');
});
package.json
{
"name": "docker-demo",
"version": "1.0.0",
"main": "app.js",
"scripts": {
"start": "node app.js"
}
}
Now the Dockerfile (no extension):
# Start from the official Node.js image
FROM node:20-alpine
# Set the working directory inside the container
WORKDIR /app
# Copy package files first (Docker layer caching trick)
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of your app
COPY . .
# Tell Docker which port the app listens on (documentation only)
EXPOSE 3000
# The command to run when the container starts
CMD ["node", "app.js"]
Let me walk through each instruction:
FROM
Every Dockerfile starts with FROM. This is the base image you're building on top of. node:20-alpine means Node.js version 20 on Alpine Linux — a minimal Linux distro that's only about 5MB. Using alpine variants keeps your images small.
WORKDIR
Sets the working directory for subsequent commands. If the directory doesn't exist, Docker creates it. Think of it as mkdir /app && cd /app.
COPY and RUN order matters
I copy package*.json first and run npm install before copying the rest of the app. This is a deliberate caching strategy. Docker builds images in layers, and it caches each layer. If your source code changes but package.json doesn't, Docker skips the npm install step and uses the cached layer. Build times go from 30 seconds to 2 seconds. I've been using this pattern in prod for years.
CMD vs RUN
RUN executes during image build time. CMD executes when a container starts from the image. Common mistake — people put CMD in the middle of the file and wonder why their app doesn't start.
Building and Running Your Image
# Build the image, tag it as "my-node-app"
docker build -t my-node-app .
# Run it, mapping port 3000 on your machine to port 3000 in the container
docker run -p 3000:3000 my-node-app
Open http://localhost:3000 in your browser. You'll see "Hello from Docker!".
To run it in the background (detached mode):
docker run -d -p 3000:3000 --name my-running-app my-node-app
Essential Docker Commands
Here are the commands you'll use every single day. I've been working with Docker for years and these cover probably 90% of my daily usage.
| Command | What It Does | Example |
|---|---|---|
docker build | Build an image from a Dockerfile | docker build -t myapp:1.0 . |
docker run | Create and start a container | docker run -d -p 8080:80 nginx |
docker ps | List running containers | docker ps |
docker ps -a | List all containers (including stopped) | docker ps -a |
docker stop | Stop a running container gracefully | docker stop my-running-app |
docker rm | Remove a stopped container | docker rm my-running-app |
docker exec | Run a command inside a running container | docker exec -it my-running-app sh |
docker logs | View container output | docker logs -f my-running-app |
docker images | List local images | docker images |
docker rmi | Remove an image | docker rmi my-node-app |
docker pull | Download an image from a registry | docker pull postgres:16 |
docker push | Upload an image to a registry | docker push myuser/myapp:1.0 |
docker exec — your best debugging friend
When something goes wrong inside a container, exec lets you get a shell in it:
docker exec -it my-running-app sh
# Now you're inside the container
ls /app
cat /app/package.json
exit
The -it flags mean interactive + TTY. Without them you'd just get output with no way to interact.
docker logs
# Follow logs in real time (like tail -f)
docker logs -f my-running-app
# Show last 50 lines
docker logs --tail 50 my-running-app
Understanding Volumes
I mentioned earlier that container storage disappears when the container stops. Volumes solve this.
# Mount a local directory into the container
docker run -d \
-p 3000:3000 \
-v $(pwd)/data:/app/data \
my-node-app
Now anything written to /app/data inside the container persists on your host machine at ./data. This is essential for databases — you don't want to lose your Postgres data every time you restart the container.
Named volumes (Docker manages the location):
docker volume create mydata
docker run -d -v mydata:/app/data my-node-app
Environment Variables
Hardcoding config in your Docker image is bad practice. Use environment variables instead:
docker run -d \
-p 3000:3000 \
-e NODE_ENV=production \
-e DATABASE_URL=postgres://... \
my-node-app
Or use a .env file:
docker run -d --env-file .env -p 3000:3000 my-node-app
Inside your Node app, access them with process.env.DATABASE_URL. Nothing special needed — they just show up as regular environment variables.
Pushing to Docker Hub
Once you have an image you want to share or deploy, push it to Docker Hub (or any registry).
# Log in
docker login
# Tag your image with your username
docker tag my-node-app yourusername/my-node-app:1.0
# Push it
docker push yourusername/my-node-app:1.0
Now anyone (or any server) can pull and run it with docker pull yourusername/my-node-app:1.0.
Common Mistakes Beginners Make
I've seen these over and over again — both from people I've mentored and from my own early days.
Running as root. By default, containers run as root. That's a security risk. Add a user in your Dockerfile:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
Ignoring .dockerignore. Without it, COPY . . copies everything — including node_modules, .git, and local .env files. Create a .dockerignore:
node_modules
.git
.env
*.log
.DS_Store
Using latest tags in production. FROM node:latest in a Dockerfile means your build might break tomorrow when Node releases a new version. Pin to a specific version: FROM node:20.14-alpine.
One giant RUN command vs too many. Each RUN creates a layer. For related commands, chain them:
RUN apt-get update && \
apt-get install -y curl git && \
rm -rf /var/lib/apt/lists/*
Where to Go From Here
You've got the fundamentals down. The natural next step from here is Docker for backend developers, which goes deeper on multi-stage builds, production hardening, and working with databases.
When you're ready to stop running single containers and start managing many of them, check out the comparison of Kubernetes vs Docker Swarm vs Nomad. And if containers are part of a larger project — say, building a web app — the web dev roadmap 2026 gives good context on how Docker fits into the bigger picture.
For full deployments, deploy AI model to production covers how containers fit into actual production pipelines. You'll also want to look at the FastAPI tutorial if Python is your language of choice — FastAPI + Docker is a very common stack in 2026.
Wrapping Up
Docker is one of those tools where the initial learning curve feels steep, then one day it just clicks. You went from "what even is a container" to building, running, and debugging Docker images. That's real progress.
The key takeaway: images are blueprints, containers are running instances, and Dockerfiles define how to build images. Master those three concepts and everything else — volumes, networks, Docker Compose, Kubernetes — builds on top.
Start by containerising one of your existing projects this week. Don't try to make it perfect. Just get it running in a container and see how it feels. You can read the official Docker documentation when you hit specific walls — it's genuinely good.
FAQ
What is the difference between a Docker image and a container? A Docker image is a read-only template — think of it as a blueprint or a snapshot of your app and its dependencies. A container is a running instance of that image. You can spin up dozens of containers from one image, and each runs in isolation. Images live on disk; containers live in memory while they're running.
Do I need Linux to run Docker? No. Docker Desktop runs on Windows and macOS by running a lightweight Linux VM under the hood. On Linux, Docker runs natively without a VM. Docker Desktop is the easiest way to get started on any OS in 2026, and it bundles the CLI, GUI, and extensions in one installer.
How is Docker different from a virtual machine? VMs virtualise the entire hardware stack and run a full OS kernel, which makes them heavy (GBs) and slow to start. Docker containers share the host OS kernel and only package the application and its dependencies, so they start in milliseconds and use far less memory. The tradeoff is isolation — VMs are stronger security boundaries.
Frequently Asked Questions
AiTechWorlds Team
✓ Verified WriterThe AiTechWorlds team is passionate about AI, technology, and education. We create high-quality, research-backed content to help you learn, grow, and succeed in the modern digital world.
Related Articles
How to Use Docker Compose for Local Dev (Node.js + PostgreSQL)
Set up a full local dev environment with Docker Compose, Node.js, PostgreSQL, and pgAdmin. Includes .env config, named volumes, healthchecks, and common error fixes.
7 Logging Strategies for Microservices (ELK, Loki, Fluentd)
Centralized logging for microservices: compare ELK, Loki, Fluentd, and Datadog with real configs, cost breakdown, and 7 battle-tested strategies.
How to Use MongoDB Atlas: Free Cloud Database Setup (2026)
Set up a free MongoDB Atlas cloud cluster, connect with Mongoose, and build a full CRUD app. Plus a real comparison of Atlas free vs Supabase, Neon, and PlanetScale free tiers.
How to Use AutoGen with Docker (Containerized Agents)
Learn how to run AutoGen agents in Docker containers for isolated, reproducible code execution. Covers DockerCommandLineCodeExecutor, docker-compose, and custom images.