Docker has a large command surface, but in practice a working developer reaches for the same dozen or so commands constantly. This tutorial is a practical tour of exactly those.

Running containers

docker run -p 8080:8080 my-app          # run in the foreground
docker run -d -p 8080:8080 my-app       # -d = detached, runs in the background
docker run --name my-container my-app   # give it a memorable name

Seeing what is running

docker ps            # running containers
docker ps -a         # every container, including stopped ones

docker ps is usually the very first command you run when something seems off — is the container even up, and what port did it actually bind to?

Looking inside a running container

docker logs my-container          # see its console output
docker logs -f my-container       # follow the logs live, like tail -f
docker exec -it my-container sh   # open an interactive shell inside it

docker exec -it ... sh (or bash, if the image has it) is enormously useful for debugging — you drop into a real shell running inside the exact same filesystem and environment as your app, and can poke around, check environment variables, or verify a file actually made it into the image.

Stopping and cleaning up

docker stop my-container
docker rm my-container
docker rmi my-app

stop gracefully shuts a container down, rm deletes a stopped container, and rmi deletes an image entirely. It is common early on to accumulate a pile of stopped containers and unused images; docker system prune clears out the unused ones in one go, which is worth knowing once your disk usage starts climbing.

Persisting data with volumes

A container's filesystem is disposable — delete the container and everything written inside it is gone. For data that needs to survive (a database's files, uploaded content), mount a volume:

docker run -v my-data:/var/lib/postgresql/data postgres

This tells Docker "store this container path on a named, persistent volume managed by Docker," so the data survives even if the container is removed and recreated. You can also mount a folder from your own machine directly, which is handy for local development so code edits on your laptop show up instantly inside the running container:

docker run -v $(pwd):/app -p 3000:3000 hello-node

Environment variables

docker run -e DATABASE_URL=postgres://... -e NODE_ENV=production my-app

Passing configuration as environment variables rather than baking it into the image is standard practice — it means the exact same image can run in development, staging and production, just configured differently at startup.

A quick mental checklist

  • Is it running? docker ps
  • What is it printing? docker logs -f
  • Need to poke around inside? docker exec -it ... sh
  • Need data to survive a restart? mount a volume with -v

Those handful of commands cover the overwhelming majority of day-to-day Docker work. Next: Docker Compose, for when one container is no longer enough.