DEV Community

Cover image for Docker Changed Everything for Me: 10 Lessons I Wish I Knew Before Containerizing My First Application
Yash Sonawane
Yash Sonawane

Posted on

Docker Changed Everything for Me: 10 Lessons I Wish I Knew Before Containerizing My First Application

There was a time when deploying an application meant spending hours figuring out why it worked perfectly on my machine but refused to run on someone else's.

Different Node versions.

Missing dependencies.

Different operating systems.

Different environment variables.

Different package managers.

Every deployment felt like solving a new mystery.

Then I discovered Docker.

At first, I thought Docker was just another DevOps buzzword. But after using it in real projects, I realized Docker isn't just a tool—it fundamentally changes how you build, test, and deploy software.

In this article, I'll share the 10 biggest lessons I learned while containerizing real-world applications, along with practical examples and common mistakes to avoid.

Whether you're a beginner or already using Docker, these lessons can save you countless hours.


Table of Contents

  • Why Docker Matters
  • Lesson 1: Containers Are Not Virtual Machines
  • Lesson 2: Smaller Images Are Faster
  • Lesson 3: Layer Caching Saves Hours
  • Lesson 4: Never Run as Root
  • Lesson 5: Multi-Stage Builds
  • Lesson 6: Environment Variables
  • Lesson 7: Docker Compose
  • Lesson 8: Health Checks
  • Lesson 9: Volumes
  • Lesson 10: Networking
  • Best Practices
  • FAQs
  • Conclusion

Why Docker Matters

Imagine packaging your application together with:

  • Source code
  • Runtime
  • Dependencies
  • Libraries
  • Configuration

Now imagine running that exact package on your laptop, your teammate's computer, your CI/CD pipeline, and production—without changing anything.

That's Docker.

Instead of saying:

"It works on my machine."

You can confidently say:

"Run this Docker image."


flowchart LR

Developer --> DockerImage
DockerImage --> Laptop
DockerImage --> CI
DockerImage --> Cloud
DockerImage --> Kubernetes
DockerImage --> Production
Enter fullscreen mode Exit fullscreen mode

Lesson 1 — Containers Are NOT Virtual Machines

One of the biggest misconceptions is thinking Docker containers are lightweight virtual machines.

They are not.

A virtual machine contains:

  • Guest Operating System
  • Kernel
  • Libraries
  • Applications

A Docker container shares the host operating system kernel.

That makes containers:

  • ✅ Smaller
  • ✅ Faster
  • ✅ Easier to start
  • ✅ More resource efficient

Virtual Machine

Hardware
│
Hypervisor
│
Guest OS
│
Application
Enter fullscreen mode Exit fullscreen mode

Docker

Hardware
│
Host OS
│
Docker Engine
│
Container
Enter fullscreen mode Exit fullscreen mode

Containers usually start in seconds.

VMs can take minutes.


Lesson 2 — Smaller Images Mean Faster Deployments

My first Docker image was over 2.3 GB.

It took forever to:

  • Build
  • Push
  • Pull
  • Deploy

The fix?

Use lightweight base images.

Instead of:

FROM ubuntu
Enter fullscreen mode Exit fullscreen mode

Use:

FROM node:22-alpine
Enter fullscreen mode Exit fullscreen mode

or

FROM python:3.12-slim
Enter fullscreen mode Exit fullscreen mode

Benefits include:

  • Faster CI/CD
  • Lower bandwidth usage
  • Better security
  • Faster deployments

Lesson 3 — Docker Layer Caching Is Magic

Consider this Dockerfile:

FROM node:22

COPY . .

RUN npm install

CMD ["npm","start"]
Enter fullscreen mode Exit fullscreen mode

Every code change forces Docker to reinstall every dependency.

Instead:

FROM node:22

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

CMD ["npm","start"]
Enter fullscreen mode Exit fullscreen mode

Docker caches the dependency layer.

Future builds become dramatically faster.

This single optimization can save several minutes in every CI/CD pipeline.


Lesson 4 — Never Run Containers as Root

Running your application as the root user increases risk.

Instead:

RUN addgroup app && adduser -S app -G app

USER app
Enter fullscreen mode Exit fullscreen mode

If someone exploits your application, they'll have far fewer permissions.

Security starts with simple habits like this.


Lesson 5 — Multi-Stage Builds Are Incredible

Suppose you're building a React application.

You don't need Node.js in production.

Use a multi-stage build.

# Build Stage
FROM node:22 AS builder

WORKDIR /app

COPY . .

RUN npm install

RUN npm run build

# Production Stage
FROM nginx:alpine

COPY --from=builder /app/dist /usr/share/nginx/html
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Smaller images
  • Reduced attack surface
  • Faster deployments

Lesson 6 — Never Hardcode Secrets

❌ Don't do this:

ENV DATABASE_PASSWORD=password123
Enter fullscreen mode Exit fullscreen mode

Instead:

docker run \
-e DATABASE_PASSWORD=mySecret \
myapp
Enter fullscreen mode Exit fullscreen mode

Better yet, use:

  • Docker Secrets
  • AWS Secrets Manager
  • Kubernetes Secrets
  • HashiCorp Vault

Secrets should never be baked into an image.


Lesson 7 — Docker Compose Makes Local Development Easy

Instead of starting every service manually:

docker run postgres

docker run redis

docker run backend

docker run frontend
Enter fullscreen mode Exit fullscreen mode

Create a docker-compose.yml file.

version: "3.9"

services:

  backend:
    build: .
    ports:
      - "5000:5000"

  postgres:
    image: postgres:16

  redis:
    image: redis:7
Enter fullscreen mode Exit fullscreen mode

Now simply run:

docker compose up
Enter fullscreen mode Exit fullscreen mode

Everything starts together.


Lesson 8 — Health Checks Matter

A running container doesn't always mean a healthy application.

Add health checks.

HEALTHCHECK \
CMD curl --fail http://localhost:3000 || exit 1
Enter fullscreen mode Exit fullscreen mode

Platforms like Kubernetes can automatically restart unhealthy containers.


Lesson 9 — Containers Are Ephemeral

Containers can disappear at any moment.

Never store important data inside them.

Wrong:

Container
│
Database
│
Data
Enter fullscreen mode Exit fullscreen mode

Right:

Container

↓

Volume

↓

Persistent Storage
Enter fullscreen mode Exit fullscreen mode

Example:

docker volume create postgres_data
Enter fullscreen mode Exit fullscreen mode

Then mount it:

docker run \
-v postgres_data:/var/lib/postgresql/data \
postgres
Enter fullscreen mode Exit fullscreen mode

Now your data survives even if the container is removed.


Lesson 10 — Learn Docker Networking

Containers communicate over Docker networks.

Example:

services:

  api:
    build: .

  database:
    image: postgres
Enter fullscreen mode Exit fullscreen mode

Inside the API container:

postgres://database:5432
Enter fullscreen mode Exit fullscreen mode

Notice we're using the service name, not localhost.

That's one of the most common beginner mistakes.


Common Mistakes

  • ❌ Using the latest tag everywhere
  • ❌ Huge Docker images
  • ❌ Running as root
  • ❌ Hardcoding secrets
  • ❌ Ignoring .dockerignore
  • ❌ Copying unnecessary files
  • ❌ Forgetting health checks
  • ❌ Not using volumes
  • ❌ Installing unnecessary packages
  • ❌ Building everything in one stage

Best Practices Checklist

  • Use lightweight base images
  • Use multi-stage builds
  • Pin image versions
  • Add health checks
  • Run as a non-root user
  • Use .dockerignore
  • Keep containers stateless
  • Store secrets securely
  • Scan images regularly
  • Keep dependencies updated

Production Dockerfile Example

FROM node:22-alpine AS builder

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

RUN npm run build

FROM node:22-alpine

WORKDIR /app

COPY --from=builder /app .

RUN addgroup app && adduser -S app -G app

USER app

EXPOSE 3000

CMD ["npm","start"]
Enter fullscreen mode Exit fullscreen mode

Security Tips

  • Scan images with Trivy.
  • Use official images.
  • Remove unnecessary packages.
  • Never embed secrets.
  • Keep dependencies updated.
  • Drop unnecessary Linux capabilities.
  • Use read-only filesystems where possible.

Performance Tips

Technique Benefit
Multi-stage builds Smaller images
Layer caching Faster builds
Alpine images Lower image size
.dockerignore Smaller build context
npm ci Faster installs
Version pinning Stable deployments

Frequently Asked Questions

Is Docker difficult to learn?

No. Most developers become productive after a weekend of practice.

Do I need Docker for every project?

Not necessarily. Small scripts might not need it, but collaborative and production projects benefit enormously.

Is Docker faster than virtual machines?

Yes. Containers share the host kernel, making them much lighter and faster.

Can Docker replace Kubernetes?

No.

Docker creates containers.

Kubernetes manages containers at scale.


Key Takeaways

  • Docker eliminates "works on my machine" problems.
  • Small images deploy faster.
  • Layer caching saves build time.
  • Multi-stage builds reduce image size.
  • Run containers as non-root users.
  • Store secrets outside images.
  • Docker Compose simplifies local development.
  • Health checks improve reliability.
  • Volumes protect your data.
  • Understanding networking prevents common connection issues.

Conclusion

Docker isn't just another tool—it's become one of the foundational technologies in modern software development.

The biggest improvements don't come from memorizing every Docker command.

They come from following a handful of proven practices:

  • Build lean images.
  • Treat containers as disposable.
  • Secure them from day one.
  • Optimize for repeatable builds.
  • Automate wherever possible.

Master these habits, and your deployments will become faster, more reliable, and much easier to maintain.


Discussion

💬 What's the biggest Docker mistake you've ever made?

💬 Which Docker feature saved you the most time?

💬 Do you have a favorite Docker tip that every developer should know?

Share your thoughts in the comments!

Top comments (0)