⌨️ Basic Docker Commands
You've got the mental model and you've seen the architecture. Now it's time to actually drive. This lesson is the practical core of the week: the handful of commands you'll type every single day to pull images, launch containers, publish ports, peek at logs, run a shell inside a container, and clean up afterward. Learn these well and Docker stops feeling like magic and starts feeling like a tool.
Week 11 · Day 1 (Monday: Docker Fundamentals) · Lecture 3
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Pull images with
docker pulland list what you have withdocker images - Launch containers with
docker run, including detached mode (-d) and port publishing (-p) - List, stop, and remove containers with
docker ps,docker stop, anddocker rm - Inspect a running container with
docker logsand open a shell inside it withdocker exec - Explain the difference between removing a container (
rm) and removing an image (rmi) - Walk a container through its complete lifecycle from pull to cleanup
Estimated Time: 65 minutes
Practice: Run an Nginx web server, view its logs, shell into it, then stop and remove it cleanly.
In This Lesson
The Docker CLI
The Docker command-line interface (CLI) is the docker program you met last lesson — the client that sends your instructions to the daemon. It groups its work into a few areas, and once you see the shape, new commands are easy to guess.
run, ps, stop, rm"] A --> C["Images
pull, images, rmi"] A --> D["Logs & Exec
logs, exec"] A --> E["System
info, prune"]
📖 Your best friend: --help
Every command documents itself. Forget an option? Ask:
docker --help # all top-level commands
docker run --help # every option for run
docker ps --help # every option for ps
Get in the habit early — it's faster than searching the web and always matches your installed version.
Anatomy of a Command
Docker commands follow a predictable structure. Once you can name the parts, long commands stop looking intimidating.
docker [command] [options] [image or container] [arguments]
Take a real one and label every piece:
docker run -d -p 8080:80 --name web nginx
| Part | What it is |
|---|---|
docker | The base command (the client) |
run | The command — create and start a container |
-d | A flag — detached (run in the background) |
-p 8080:80 | An option with a value — publish host port 8080 to container port 80 |
--name web | An option — give the container a friendly name |
nginx | The argument — which image to run |
💡 Two spellings, same result
Docker offers a newer, explicit form (docker container run, docker image ls) and a classic shorthand (docker run, docker ps). They do the same thing. The shorthand is what you'll see most in the wild, so we'll use it here — just know both exist.
Working with Images
Before you can run a container, you need an image. Images come from a registry (Docker Hub by default), and docker run will pull one automatically if it's missing — but it's worth knowing the explicit commands.
# Download an image from the registry
docker pull nginx
# Download a specific version (tag) — always prefer a real tag over "latest"
docker pull nginx:1.27
# List the images you have locally
docker images
# Remove an image you no longer need
docker rmi nginx:1.27
Output of docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest a72860cb95fd 2 weeks ago 188MB
node 20-alpine 4d3f8e2b1c9a 3 weeks ago 135MB
Pulling an image is like downloading an app from an app store: you grab it once, it's cached locally, and every container you launch from it reuses that download. The SIZE column shows why the Alpine variants (built on tiny Alpine Linux) are so popular for keeping images lean.
⚠️ Avoid relying on latest
nginx:latest is not a fixed version — it's whatever the maintainers most recently tagged as latest, and it changes over time. Pinning a real tag like nginx:1.27 makes your builds reproducible, so a rebuild months later doesn't silently pull a different version.
Running Containers
docker run is the command you'll type most. It creates a container from an image and starts it in one step. Its options configure nearly everything about how the container behaves — here are the ones that matter on day one.
# Simplest form — runs in the foreground, ties up your terminal
docker run nginx
# Detached (-d): run in the background and get your prompt back
docker run -d nginx
# Publish a port (-p HOST:CONTAINER): reach the app from your browser
docker run -d -p 8080:80 nginx
# Give it a name so you don't have to copy container IDs around
docker run -d -p 8080:80 --name web nginx
# Pass environment variables (-e) — common for databases
docker run -d -e POSTGRES_PASSWORD=secret --name db postgres:16
# Interactive shell (-it) into a fresh Ubuntu container
docker run -it ubuntu bash
# Auto-remove the container the moment it stops (--rm) — great for one-offs
docker run --rm hello-world
Two flags to understand deeply: -d and -p
These two show up constantly, so let's be precise about what they do.
Detached mode (-d) runs the container in the background instead of attaching it to your terminal. Without it, a server like nginx would hold your terminal hostage, printing its logs, until you pressed Ctrl+C (which stops it). With -d you get your prompt back and the container keeps running.
Port publishing (-p HOST:CONTAINER) connects a port on your machine to a port inside the container. A container is isolated by default — the web server listening on port 80 inside the container is unreachable from your browser until you map it out. -p 8080:80 says "traffic to port 8080 on my machine goes to port 80 in the container."
-p 8080:80, a request to localhost:8080 is forwarded into the container's port 80, where nginx is listening. Without it, the container stays sealed off.✅ Try it right now
docker run -d -p 8080:80 --name web nginx
Then open http://localhost:8080 in your browser — you'll see the Nginx welcome page, served from inside a container you started with one line.
The Container Lifecycle
A container moves through predictable states. Knowing the transitions tells you which command to reach for.
# See what's running right now
docker ps
# See everything, including stopped containers
docker ps -a
# Stop a running container gracefully (by name or ID)
docker stop web
# Start a stopped container back up
docker start web
# Restart in one step
docker restart web
Output of docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
3f9a1b2c4d5e nginx "/docker-entrypoint.…" Up 2 minutes 0.0.0.0:8080->80/tcp web
Notice the NAMES column — because we passed --name web, we can use web everywhere instead of copying that container ID. And docker ps shows only running containers; add -a to see stopped ones too (a common "where did my container go?" gotcha).
Inspecting & Interacting
Once a container is running, two commands do most of the debugging work: logs to see its output, and exec to step inside it.
Reading logs
A container's logs are whatever its main process wrote to stdout and stderr — for a server, that's the request log and any errors. This is the first place to look when something isn't working.
# Print the container's logs
docker logs web
# Follow the logs live, like tail -f (Ctrl+C to stop watching)
docker logs -f web
# Just the last 50 lines, with timestamps
docker logs --tail 50 -t web
Running a command inside a container
docker exec runs an additional command inside an already-running container. Combined with -it (interactive + terminal), it drops you into a shell — like SSH-ing into a server, but for a container.
# Open an interactive shell inside the running "web" container
docker exec -it web bash
# Some minimal images (like Alpine) have sh but not bash
docker exec -it web sh
# Run a one-off command without an interactive session
docker exec web ls /usr/share/nginx/html
⚠️ exec vs. run — a common mix-up
docker run starts a brand-new container. docker exec runs a command in an existing, already-running one. If you "exec" into a container and make changes, remember those changes live in that container's writable layer — they vanish when the container is removed. To make a change permanent, edit your Dockerfile and rebuild.
You can also get a full JSON dump of a container's configuration:
# Everything Docker knows about the container
docker inspect web
# Live CPU / memory usage of all running containers
docker stats
Cleaning Up
Containers and images accumulate. Left unchecked they eat disk space, so cleanup is a real skill. The key distinction: rm removes containers, rmi removes images.
# Remove a stopped container
docker rm web
# Force-remove a running container (stop + remove)
docker rm -f web
# Remove an image (fails if a container still uses it)
docker rmi nginx
# Remove ALL stopped containers at once
docker container prune
# Remove dangling/unused images
docker image prune
💡 Why can't I remove this image?
If docker rmi nginx complains that the image is "being used by" a container, it's protecting you: an image can't be deleted while a container built from it still exists. Remove the container first (docker rm), then the image. This mirrors the image-vs-container relationship — you can't tear up the blueprint while a house built from it still stands.
⚠️ prune is a bulk delete
docker container prune removes all stopped containers; docker system prune -a goes further and removes unused images and networks too. They're great for reclaiming space, but read the confirmation prompt — this is one of the few Docker commands that deletes many things at once.
Practice & Quiz
🏋️ Exercise 1: Full lifecycle of a web server
Goal: Take an Nginx container from launch to cleanup, touching every command from this lesson. Write the commands to: (1) run nginx detached, named myweb, on host port 8080; (2) confirm it's running; (3) view its logs; (4) open a shell inside it; (5) stop it; (6) remove it.
💡 Hint
You'll need run with -d, -p, and --name; then ps, logs, exec -it, stop, and rm — all referring to the container by its name myweb.
✅ Solution
# 1. Run detached, named, with port published
docker run -d -p 8080:80 --name myweb nginx
# 2. Confirm it's running
docker ps
# 3. View its logs (open http://localhost:8080 first to generate some)
docker logs myweb
# 4. Open a shell inside it (type "exit" to leave)
docker exec -it myweb bash
# 5. Stop it
docker stop myweb
# 6. Remove it
docker rm myweb
Tip: steps 5 and 6 can be combined with docker rm -f myweb, which stops and removes in one go.
🏋️ Exercise 2: Diagnose a "port already in use" error
Goal: You run docker run -d -p 8080:80 --name web2 nginx and get an error that port 8080 is already allocated. What likely happened, and how do you fix it without losing your work?
✅ Solution
An earlier container (from Exercise 1, perhaps) is still using host port 8080. Two containers can't publish to the same host port at once. Fixes: publish the new one on a different host port (-p 8081:80), or stop/remove the container holding 8080 (docker ps to find it, then docker stop/docker rm). Note the container port stays 80 — it's the host side that must be unique.
🎯 Quick Quiz
Question 1: What does the -d flag do in docker run -d nginx?
Question 2: In -p 8080:80, which number is the port inside the container?
Question 3: Which command removes an image (not a container)?
Best Practices & Pitfalls
✅ Do
- Name your containers with
--nameso you never wrestle with long IDs - Use
-dfor anything long-running, like a server or database - Reach for
docker logsfirst when a container misbehaves - Prune stopped containers and unused images regularly to reclaim disk
- Pin real image tags (
node:20-alpine) instead oflatest
❌ Don't
- Forget
-a—docker pshides stopped containers, and then you "lose" them - Confuse
rm(containers) withrmi(images) - Rely on changes made via
execto persist — they die with the container - Publish two containers to the same host port at once
- Run
docker system prune -awithout reading what it will delete
✅ A tiny cheat sheet
| Goal | Command |
|---|---|
| Get an image | docker pull nginx |
| Run it in the background on a port | docker run -d -p 8080:80 --name web nginx |
| See what's running | docker ps |
| Read its logs | docker logs -f web |
| Shell into it | docker exec -it web bash |
| Stop and remove it | docker rm -f web |
Summary
🎉 Key Takeaways
docker pullfetches an image;docker imageslists what you havedocker runcreates and starts a container — use-dfor the background and-p HOST:CONTAINERto publish a portdocker psshows running containers; add-ato see stopped ones toodocker logsshows a container's output;docker exec -it … bashopens a shell inside itdocker stophalts a container;rmremoves containers andrmiremoves images
📚 Additional Resources
- Docker Docs — The docker CLI reference
- Docker Docs — docker run reference
- Docker Docs — Official CLI cheat sheet (PDF)
- Docker Docs — Published ports
🚀 What's Next?
So far you've been running other people's images. Next you'll build your own: Dockerfile Syntax teaches you to write the recipe — FROM, WORKDIR, COPY, RUN, EXPOSE, CMD — that turns your Node.js app into a shippable image.
🎉 You can drive Docker now!
Pull, run, inspect, clean up — this is the daily loop every containerized workflow is built on. Next stop: building images of your own.