What you'll build
You'll install Docker and create your first container: a Node.js app packaged to work identically on any computer. You'll learn to run Ubuntu containers, databases like PostgreSQL, and use volumes to avoid losing data. When you're done, you'll have a Dockerized app with its Dockerfile, docker-compose.yml, and the ability to spin up services with one command. It's the foundation for working in teams where "it works on my machine" will no longer be a problem.
Step 1: Install Docker
| System | Installation |
|---|---|
| macOS | Docker Desktop |
| Windows | Docker Desktop |
| Linux | `curl -fsSL https://get.docker.com |
Verify:
docker --version
docker run hello-world
โ ๏ธ Windows: You need WSL2 enabled. Docker Desktop guides you through installation.
Step 2: Your first container
# Download and run Ubuntu in seconds
docker run -it ubuntu bash
# Now you're INSIDE the container
cat /etc/os-release # You'll see "Ubuntu"
apt update # Works like real Linux
exit # Exit the container
What happened? Docker downloaded an Ubuntu image (~30MB, not 4GB) and ran it isolated from your system.
Step 3: Run useful services
Docker shines when you need databases or services without permanent installation:
# PostgreSQL ready in 10 seconds
docker run -d --name my-postgres \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
postgres:16-alpine
# Redis for caching
docker run -d --name my-redis -p 6379:6379 redis:alpine
# Adminer (web interface for databases)
docker run -d --name adminer -p 8080:8080 adminer
Open http://localhost:8080 to see Adminer running.
โ ๏ธ PROBLEM: If you run docker rm my-postgres, you lose ALL the data. Keep reading to fix this.
Step 4: Volumes (Persistent Data)
The most common beginner mistake: They create a database, save data, remove the container... and lose everything.
Why does this happen?
WITHOUT VOLUME:
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Container โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ Database โ โ โ Data lives INSIDE
โ โ (your data) โ โ the container
โ โโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
docker rm postgres
โ
๐ DATA LOST
WITH VOLUME:
โโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ Container โ โ Volume โ
โ โโโโโโโโโโโโโโโโโโโ โ โโโโ โ (your disk) โ
โ โ Database โโโโโโโโโโโโโ your data โ
โ โโโโโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Data is stored
docker rm postgres OUTSIDE container
โ โ
Container removed โ
DATA SAFE
Create PostgreSQL with persistent volume
# Create a named volume
docker volume create postgres_data
# Run PostgreSQL using that volume
docker run -d --name my-postgres \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_USER=dev \
-e POSTGRES_DB=myapp \
-p 5432:5432 \
-v postgres_data:/var/lib/postgresql/data \
postgres:16-alpine
The magic is in -v postgres_data:/var/lib/postgresql/data:
postgres_data= volume name on your machine/var/lib/postgresql/data= where Postgres stores data inside the container
Test that it works
# Connect and create a table
docker exec -it my-postgres psql -U dev -d myapp -c "CREATE TABLE test (id INT);"
docker exec -it my-postgres psql -U dev -d myapp -c "INSERT INTO test VALUES (1), (2), (3);"
# Remove the container
docker stop my-postgres && docker rm my-postgres
# Create a new one with the SAME volume
docker run -d --name my-postgres \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_USER=dev \
-e POSTGRES_DB=myapp \
-p 5432:5432 \
-v postgres_data:/var/lib/postgresql/data \
postgres:16-alpine
# Verify the data is still there
docker exec -it my-postgres psql -U dev -d myapp -c "SELECT * FROM test;"
# Result: 1, 2, 3 โ
Volume commands
| Command | What it does |
|---|---|
docker volume create name | Create a volume |
docker volume ls | List all volumes |
docker volume inspect name | See details (real location) |
docker volume rm name | Delete volume (and its data!) |
docker volume prune | Delete unused volumes |
Mount types
| Type | Syntax | Use case |
|---|---|---|
| Named Volume | -v my_volume:/data | Production, databases |
| Bind Mount | -v ./local:/data | Development, live reload |
| Anonymous | -v /data | Temporary, not recommended |
Bind Mount example for development:
# Your local code syncs with the container
docker run -d --name my-app \
-v $(pwd):/app \
-p 3000:3000 \
node:20-alpine npm start
Change a file โ the container sees it immediately.
Step 5: Create your own image
Now that you understand containers and volumes, create your own image.
Create a docker-hello folder with these 3 files:
Dockerfile:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "index.js"]
index.js:
console.log("Hello from Docker! ๐ณ");
console.log("Date:", new Date().toISOString());
console.log("Node version:", process.version);
package.json:
{ "name": "docker-hello", "version": "1.0.0" }
Build and run:
docker build -t my-app .
docker run my-app
Step 6: Install Portainer (Visual Management)
Portainer is a web interface for Docker. Perfect for beginners:
# Create a volume to persist data
docker volume create portainer_data
# Install Portainer
docker run -d -p 9000:9000 \
--name portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latest
Open http://localhost:9000 and create your admin user.
What can you do with Portainer?
- See all your containers in a table
- Start/stop/remove with one click
- View logs in real-time
- Create containers without commands
- Manage images, volumes, networks
๐ก Tip: Keep Portainer running always. It's like "Finder" or "Explorer" but for Docker.
๐ ๏ธ Management tools
| Tool | Type | Ideal for |
|---|---|---|
| Portainer | Local/server UI | Visual Docker management |
| Docker Desktop | Desktop app | Mac/Windows, includes basic UI |
| Lazydocker | Terminal TUI | Terminal fans |
For production deployment
| Tool | Description | Cost |
|---|---|---|
| EasyPanel | Deploy like Heroku, but on your VPS | Free self-hosted |
| Coolify | Open source, very complete | Free self-hosted |
| Dokku | Mini-Heroku on your server | Free |
| CapRover | Simple PaaS with Let's Encrypt | Free |
๐ฏ Recommendation for beginners: Use Portainer to learn, then EasyPanel or Coolify when you want easy deployment.
Essential commands
| Command | What it does |
|---|---|
docker run -d name | Run in background |
docker ps | List active containers |
docker ps -a | List ALL (even stopped) |
docker logs name | View logs |
docker logs -f name | Real-time logs |
docker stop name | Stop container |
docker rm name | Remove container |
docker images | List downloaded images |
docker rmi name | Remove image |
docker system prune | Clean all unused |
If something failed
| Error | Cause | Solution |
|---|---|---|
daemon not running | Docker not running | Open Docker Desktop |
permission denied | No permissions | Linux: sudo usermod -aG docker $USER and restart session |
port already in use | Port occupied | Change port: -p 9001:9000 |
no space left | Disk full of images | docker system prune -a |
cannot connect to localhost | Container not exposing port | Add -p port:port |
What you learned
โ Install Docker and verify it works โ Run pre-made containers (Ubuntu, Postgres, Redis) โ Use volumes to not lose data (beginner's #1 mistake) โ Create your own image with Dockerfile โ Use Portainer for visual management โ Essential commands for daily use
Next steps
โ Consume a JSON API โ Connect your app with real data โ Deploy with Docker โ Take your container to production โ Docker Basics (theory) โ Understand containers vs VMs