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 nameSeeing what is running
docker ps # running containers
docker ps -a # every container, including stopped onesdocker 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 itdocker 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-appstop 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 postgresThis 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-nodeEnvironment variables
docker run -e DATABASE_URL=postgres://... -e NODE_ENV=production my-appPassing 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.