Back to Blog
Developer ToolsDockerDevOpsTutorial

Docker for Developers -- From Zero to Containerized in 20 Minutes

Published on April 4, 202614 min read

Docker for Developers -- From Zero to Containerized in 20 Minutes

You have heard "it works on my machine" enough times. Docker solves this by packaging your app and all its dependencies into a container that runs identically everywhere -- your laptop, your colleague's laptop, staging, production.

This is not a theory guide. By the end, you will have a working Dockerized application with a database, and you will understand every line of configuration.


Installing Docker

macOS

Download Docker Desktop from the official Docker website. Install it like any other app. Once running, you will see the whale icon in your menu bar. Verify in your terminal:

> docker --version

> docker compose version

Windows

Download Docker Desktop. During installation, enable WSL 2 backend when prompted. Restart your machine, then verify:

> docker --version

> docker compose version

Linux

> curl -fsSL https://get.docker.com | sh

> sudo usermod -aG docker $USER

Log out and back in, then verify with the same commands.


Your First Dockerfile

A Dockerfile is a recipe that tells Docker how to build your application image. Here is a real one for a Node.js app:

FROM node:20-alpine

>

WORKDIR /app

>

COPY package*.json ./

RUN npm ci --only=production

>

COPY . .

>

EXPOSE 3000

>

CMD ["node", "server.js"]

What Each Line Does

  • FROM node:20-alpine -- start with a lightweight Node.js base image. Alpine images are about 50MB vs 900MB for full images.
  • WORKDIR /app -- set the working directory inside the container. All subsequent commands run from here.
  • COPY package*.json ./ -- copy package.json and package-lock.json first. This is intentional -- Docker caches layers, so if your dependencies have not changed, it skips the npm install step.
  • RUN npm ci --only=production -- install dependencies. Using npm ci instead of npm install gives you reproducible builds.
  • COPY . . -- copy the rest of your application code.
  • EXPOSE 3000 -- document which port the app listens on. This does not actually publish the port.
  • CMD -- the command to run when the container starts.

Build and Run

> docker build -t my-app .

> docker run -p 3000:3000 my-app

Your app is now running at localhost:3000 inside a container. The -p flag maps port 3000 on your machine to port 3000 in the container.


The .dockerignore File

Just like .gitignore keeps files out of your repo, .dockerignore keeps files out of your container. Create one in your project root:

node_modules

.git

.env

npm-debug.log

Dockerfile

docker-compose.yml

.dockerignore

README.md

.DS_Store

This prevents Docker from copying unnecessary files into the image, making builds faster and images smaller.


Docker Compose -- Multi-Service Apps

Real applications rarely run alone. You need a database, maybe a cache, maybe a message queue. Docker Compose lets you define and run all of these together.

Create a docker-compose.yml file:

version: "3.9"

>

services:

app:

build: .

ports:

- "3000:3000"

environment:

- DATABASE_URL=postgres://user:password@db:5432/myapp

- REDIS_URL=redis://cache:6379

depends_on:

- db

- cache

volumes:

- .:/app

- /app/node_modules

>

db:

image: postgres:16-alpine

environment:

POSTGRES_USER: user

POSTGRES_PASSWORD: password

POSTGRES_DB: myapp

volumes:

- pgdata:/var/lib/postgresql/data

ports:

- "5432:5432"

>

cache:

image: redis:7-alpine

ports:

- "6379:6379"

>

volumes:

pgdata:

Start Everything

> docker compose up

That single command starts your app, a PostgreSQL database, and a Redis cache. They can communicate using their service names as hostnames -- your app connects to the database at db:5432, not localhost.

Useful Compose Commands

  • docker compose up -d -- start in background (detached mode)
  • docker compose down -- stop and remove all containers
  • docker compose logs -f app -- follow logs for the app service
  • docker compose exec app sh -- open a shell inside the running app container
  • docker compose build --no-cache -- rebuild images from scratch
  • docker compose ps -- see running containers and their status

Development vs Production

Your development Dockerfile and production Dockerfile should be different. Use multi-stage builds to keep production images small:

# Stage 1: Build

FROM node:20-alpine AS builder

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

RUN npm run build

>

# Stage 2: Production

FROM node:20-alpine

WORKDIR /app

COPY --from=builder /app/dist ./dist

COPY --from=builder /app/node_modules ./node_modules

COPY package*.json ./

EXPOSE 3000

CMD ["node", "dist/server.js"]

The final image only contains the built output and production dependencies. Build tools, source code, and devDependencies are left behind in the builder stage.


Hot Reload in Development

For development, you want code changes to reflect immediately without rebuilding the container. Use a volume mount:

services:

app:

build: .

volumes:

- .:/app

- /app/node_modules

command: npm run dev

The volume mount syncs your local files into the container. The /app/node_modules line prevents your local node_modules from overwriting the container's.


Common Patterns

Environment Variables

Never hardcode secrets in Dockerfiles. Use environment variables:

> services:

> app:

> env_file:

> - .env

Or pass them directly:

> docker run -e API_KEY=your_key my-app

Health Checks

Add health checks so Docker knows if your app is actually working:

> HEALTHCHECK --interval=30s --timeout=3s --retries=3 \

> CMD wget --quiet --tries=1 --spider http://localhost:3000/health || exit 1

Networking

By default, all services in a Docker Compose file share a network. They can reach each other by service name. If you need to isolate services, create custom networks:

> networks:

> frontend:

> backend:


Debugging Docker Issues

  • Container exits immediately? Check logs with docker logs container_name. Usually the app is crashing on startup.
  • Cannot connect to database? Make sure depends_on is set, and your app retries the connection on startup. depends_on only waits for the container to start, not for the database to be ready.
  • Build is slow? Check your .dockerignore. If node_modules or .git are being copied, it will take forever.
  • Out of disk space? Run docker system prune to clean up unused images, containers, and volumes.
  • Port already in use? Another service or container is using that port. Check with docker ps or lsof -i :3000.

Essential Docker Commands Cheat Sheet

  • docker ps -- list running containers
  • docker ps -a -- list all containers including stopped ones
  • docker images -- list all images
  • docker logs -f container -- follow container logs
  • docker exec -it container sh -- open a shell in a running container
  • docker stop container -- gracefully stop a container
  • docker rm container -- remove a stopped container
  • docker rmi image -- remove an image
  • docker system prune -a -- remove all unused images, containers, networks, and volumes

What Next?

Once you are comfortable with the basics:

  • Set up CI/CD -- build and push Docker images automatically with GitHub Actions
  • Learn Docker networking -- custom networks, host networking, overlay networks
  • Explore container orchestration -- Kubernetes or Docker Swarm for running containers at scale
  • Try multi-architecture builds -- build images that run on both Intel and ARM (Apple Silicon)

Docker is one of those tools that feels like overhead at first but becomes indispensable once it is part of your workflow. Start with one project, get comfortable, and gradually Dockerize everything.

For more developer guides, check out our blog and explore our free developer tools.

Explore Our Free Tools & Games

Check out our curated collection of completely free browser games, tools, and extensions.

Browse Free Stuff

More Articles

Developer ToolsJSON

Stop Squinting at Messy JSON - Format It Instantly (Free Tool Inside)

Messy JSON is a productivity killer. Learn why formatting matters, common JSON pitfalls developers hit daily, and try our free browser-based JSON Formatter that works instantly with zero sign-ups.

7 min readRead More→
Browser GamesFree Games

Best Free Browser Games You Can Play Right Now in 2025

Discover the top free browser games of 2025 that require no downloads, no installs, and no sign-ups. From puzzle games to multiplayer adventures, these games run right in your browser.

8 min readRead More→
Developer ToolsFree Tools

Free Developer Tools Every Programmer Needs in Their Toolkit

A comprehensive guide to the best free developer tools available online. From JSON formatters to regex testers, these browser-based tools will supercharge your productivity.

10 min readRead More→
Chrome ExtensionsProductivity

10 Free Chrome Extensions That Will Boost Your Productivity

These free Chrome extensions will transform how you browse, work, and manage your time online. From tab management to dark mode, these extensions are must-haves.

7 min readRead More→