DEV Community

Anusha Kuppili
Anusha Kuppili

Posted on

🚢 Docker, Explained from docker run to Docker Compose (With Real Examples)

If Docker ever felt like a long list of commands with no mental model, this post is for you.

Here’s the thing: Docker isn’t complicated. It’s just layered.
Once you see how the layers fit together, everything clicks.

Let’s break it down from the ground up.

1️⃣ Running Containers with docker run

The most basic Docker command:

docker run redis

What actually happens?

Docker checks if the image exists locally

If not, it pulls it from Docker Hub

It creates a container

It runs the default command defined in the image

By default, Docker uses the latest tag.

To run a specific version:

docker run redis:4.0

Why this matters:

latest is not predictable

Production systems should always use explicit tags

2️⃣ Interactive vs Non-Interactive Containers

Containers run in non-interactive mode by default.

That’s why this behaves oddly:

docker run kodekloud/simple-prompt-docker

To accept user input, you need both flags:

docker run -it kodekloud/simple-prompt-docker

-i → keep STDIN open

-t → allocate a pseudo terminal

Miss one, and your app won’t behave like a real CLI program.

3️⃣ Port Mapping: Making Containers Accessible

Containers have their own internal network.

If your app listens on port 5000 inside the container, it’s invisible from outside unless you map it.

docker run -p 80:5000 kodekloud/webapp

Now traffic flows like this:

Host:80 → Container:5000

Run multiple instances easily:

docker run -p 8000:5000 app
docker run -p 8001:5000 app
Enter fullscreen mode Exit fullscreen mode

Same app. Different host ports.

4️⃣ Data Persistence with Volumes

Containers are ephemeral.
Delete the container → data is gone.

Example problem:

docker run mysql
docker rm mysql
Enter fullscreen mode Exit fullscreen mode

💥 All data lost.

Solution: volumes.

docker run -v /opt/datadir:/var/lib/mysql mysql
Enter fullscreen mode Exit fullscreen mode

Now:

Data lives on the host

Containers can be recreated safely

5️⃣ Inspecting Containers Like a Pro

See running containers:

docker ps
Enter fullscreen mode Exit fullscreen mode

Deep inspection:

docker inspect

This shows:

Entrypoint

Environment variables

Mounts

Network configuration

Runtime state

When something breaks, docker inspect usually tells you why.

6️⃣ Logs and Detached Mode

Run containers in the background:

docker run -d nginx
Enter fullscreen mode Exit fullscreen mode

Check logs anytime:

docker logs <container_name>
Enter fullscreen mode Exit fullscreen mode

Detached mode is essential for:

Web servers

Databases

CI tools like Jenkins

7️⃣ CMD vs ENTRYPOINT (This Confuses Everyone)

Short version:

CMD → default arguments

ENTRYPOINT → fixed executable

Best practice pattern:

FROM ubuntu
ENTRYPOINT ["sleep"]
CMD ["5"]
Enter fullscreen mode Exit fullscreen mode

Usage:

docker run ubuntu-sleeper      # sleep 5
docker run ubuntu-sleeper 10   # sleep 10
Enter fullscreen mode Exit fullscreen mode

ENTRYPOINT stays fixed.
CMD stays flexible.

8️⃣ Building Your Own Docker Images

A Dockerfile is just documented automation.

Example:

FROM ubuntu
RUN apt-get update && apt-get install -y python python-pip
RUN pip install flask
COPY app.py /opt/app.py
ENTRYPOINT ["flask", "run", "--host=0.0.0.0", "--app", "/opt/app.py"]
Enter fullscreen mode Exit fullscreen mode

Each instruction:

Creates a layer

Is cached

Rebuilt only when it changes

That’s why Docker builds are fast when written well.

9️⃣ Environment Variables (Config Without Code Changes)

Hardcoding config is fragile.

Better approach:

color = os.environ.get("APP_COLOR")
Enter fullscreen mode Exit fullscreen mode

Run with:

docker run -e APP_COLOR=blue simple-webapp-color
Enter fullscreen mode Exit fullscreen mode

Same image.
Different behavior.
Zero code changes.

🔟 Docker Compose: When One Container Isn’t Enough

Running multiple containers manually doesn’t scale.

Compose fixes that.

version: "3"
services:
  redis:
    image: redis

  db:
    image: postgres:9.4
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres

  vote:
    image: voting-app
    ports:
      - "5000:80"

  result:
    image: result-app
    ports:
      - "5001:80"

  worker:
    image: worker-app
Enter fullscreen mode Exit fullscreen mode

Start everything with:

docker-compose up
Enter fullscreen mode Exit fullscreen mode

What Compose gives you:

Automatic networking

Service name DNS

One-command startup

Cleaner architecture

Final Takeaway

Docker isn’t about commands.
It’s about control.

Control over:

Environments

Dependencies

Networking

Data

Deployment behavior

Once that mental model clicks, Docker stops being scary and starts being obvious.

👋 If this helped:

Save it

Share it

Try the commands locally

That’s how Docker really sticks.

Top comments (0)