Page 2 of 2
Here's something that surprises a lot of people the first time: anything a container writes to its own filesystem disappears the moment that container is removed. Containers are meant to be disposable, so their disk is disposable too, by design.
That's a problem the moment you have data you actually need to keep - a database's files, uploaded images, anything that matters. A volume is Docker's answer: storage that lives outside the container's own disposable filesystem, so it survives even after the container using it is removed.
docker run -v my-data:/var/lib/data my-app
# my-data is a named volume Docker manages for you
# /var/lib/data is the folder INSIDE the container that gets backed by it
There's also a simpler version, a bind mount, where instead of a Docker-managed volume you point directly at a real folder on your own machine - handy in local development when you want to edit a file on your laptop and see the change immediately inside the container.
Almost every real app needs some configuration - a database address, a feature flag, an API key - and you don't want to hard-code that inside the image itself, because then changing a setting means rebuilding the whole thing.
docker run -e LOG_LEVEL=debug -e DATABASE_URL=postgres://... my-app
Inside your Dockerfile, you can also set a default with ENV:
ENV LOG_LEVEL=info
A value passed with -e at docker run time overrides whatever ENV set in the Dockerfile - so the image has a sensible default, but anyone running it can still adjust it without rebuilding anything.
Most real apps aren't just one container - a typical setup might need your app, a database, and a cache, all running together and able to talk to each other. Starting each one by hand with a long docker run command gets old fast, and it's easy to forget a flag.
Docker Compose lets you describe your whole setup in one file and start everything with one command.
services:
web:
build: .
ports:
- "8080:3000"
database:
image: postgres:16
environment:
POSTGRES_PASSWORD: example
docker compose up # build/start everything described above
docker compose down # stop and remove it all
docker compose logs -f # follow logs from every service at once
Notice that web can reach database just by that name - Compose sets up networking between your services automatically, so you never need to hunt down an IP address.
Docker doesn't delete things automatically, so unused images, stopped containers, and old volumes quietly pile up over weeks of normal use - it's one of the most common "why is my disk full?" surprises for anyone new to it.
docker container prune # remove all stopped containers
docker image prune # remove images no longer used by any container
docker volume prune # remove volumes no longer used by any container
docker system prune -a # the big one - clean up everything unused at once
docker system prune -a is genuinely useful to know early, if only so a slow laptop doesn't become a mystery.
Real scenario-based DevOps questions, hands-on practice, and clear explanations for every answer.