The Docker Commands You Actually Type: A Working Docker Cheat Sheet
A practical Docker cheat sheet for everyday container work — run, build, ps, logs, exec, compose, prune, plus how to debug a container that keeps exiting.
The Docker Commands You Actually Type: A Working Docker Cheat Sheet
I have used Docker daily for years, and I still keep a short list of commands within reach. Not because the others are hard, but because nine times out of ten the job is the same: start something, look at what it printed, poke around inside it, and clean up the mess afterward. The official docs cover all 200 commands. This post covers the dozen you reach for before lunch, plus the one workflow that wastes an afternoon when you do not know it: figuring out why a container keeps dying.
If you want the searchable version with every flag and pitfall spelled out, keep the Docker cheat sheet open in another tab while you read.
Images vs containers: the distinction that trips everyone
The single most common confusion is treating an image and a container as the same thing. An image is the frozen blueprint — a stack of read-only layers you pull or build. A container is a running (or stopped) instance of that image, with its own writable layer on top. You can have one nginx image and ten nginx containers from it.
This matters because the delete commands are different and unforgiving:
docker rm my-container # removes a CONTAINER
docker rmi nginx:latest # removes an IMAGE (note the i)
Run docker rm nginx:latest to free disk and you get Error: No such container: nginx:latest. Then you add -f, the wrong target finally matches something, and you have just force-removed a running database. The i in rmi is the whole difference. List them with docker ps (containers) and docker images (images) and you will never mix the two up again.
The everyday container loop: run, ps, logs, exec
Here is the cycle I run dozens of times a day. Start a detached web server, map a port, and give it a name:
docker run -d -p 8080:80 --name web nginx
Break that down: -d detaches it to the background, -p 8080:80 publishes container port 80 on host port 8080, and --name web lets you refer to it without copying a hex ID around. Visit localhost:8080 and nginx answers.
Now the four follow-ups you will use constantly:
docker ps # what is running right now
docker logs -f web # tail its output, -f to follow
docker exec -it web sh # open a shell INSIDE the running container
docker stop web && docker rm web # stop, then remove
The -it on exec is two flags glued together: -i keeps stdin open and -t allocates a pseudo-terminal. Drop either and your shell behaves strangely or exits immediately. Inside that shell you can check config files, run env, or curl localhost to test the app from the container's own network view — which is often different from yours.
One flag worth adding for throwaway work is --rm, which deletes the container the moment it exits:
docker run --rm -it alpine sh
Note that --rm fires at exit time. It does not touch the image, and it does nothing if the container is still running. People expect it to "clean up the image" and it never will.
Building images and reading the layers
When you have a Dockerfile, build turns it into a tagged image:
docker build -t myapp:1.0 .
The . is the build context — the directory Docker tars up and ships to the daemon. If that directory holds node_modules, .git, and a 400MB dist/, all of it travels, and your builds crawl. A .dockerignore file fixes that the same way .gitignore keeps junk out of commits.
To see why an image is huge, read its layers:
docker history --no-trunc myapp:1.0
Every line is a Dockerfile instruction and the bytes it added. Usually one fat RUN or a COPY . . that swept in files you did not mean to ship is the culprit. One real caution: never pass secrets through --build-arg. Those ARG values are baked into docker history, so anyone with the image can read your token. Use BuildKit's --secret mount instead.
Compose for anything with more than one container
Once your app needs a database alongside it, stop running docker run by hand and write a compose.yaml. Then the whole stack is three commands:
docker compose up -d # start everything, detached
docker compose logs -f # tail logs from all services
docker compose down # stop and remove the whole stack
Note the space: docker compose, not docker-compose. The dashed Python version was deprecated in September 2023 and is gone from fresh Docker Desktop installs. The V2 plugin is rewritten in Go and ships with the engine. If old docs show the dash, the subcommands are identical — swap the dash for a space.
Cleaning up disk before it bites you
Docker is a packrat. Stopped containers, dangling images, unused build cache, and orphaned volumes pile up until df -h shows your disk at 98% and builds fail with no space left on device. The blunt instrument:
docker system df # see what is eating space
docker system prune # remove stopped containers, unused networks, dangling images
docker system prune -a --volumes # also remove unused images AND volumes — careful
I run plain docker system prune weekly without a second thought. The -a --volumes variant is the one to respect: --volumes will delete data volumes nothing is currently using, which on a laptop is fine and on a server can erase a database you forgot to label as in-use. Read the confirmation prompt before you hit y.
Worked scenario: why does my container keep exiting?
Here is the case that eats afternoons. You run your app, docker ps shows nothing, and docker ps -a shows it Exited (137) 4 seconds ago. No logs, no clue.
Start with the exit code, because it is a precise signal:
- 137 = 128 + SIGKILL(9), nearly always the OOM killer. The kernel or Docker Desktop's VM memory cap killed it.
- 125 = Docker itself errored — a bad flag or a missing image.
- 126 = the command was found but is not executable (a script missing
chmod +x). - 127 = command not found in the container — the classic Alpine trap, since Alpine ships
sh, notbash.
For our 137, confirm the OOM theory directly:
docker inspect --format "{{.State.OOMKilled}}" web
If that prints true, the process blew past its memory limit. Raise it with --memory 1g on the run, or in Compose bump the service's mem_limit — and on Docker Desktop, check the VM has enough RAM allocated in the first place. If it prints false, the app exited on its own; grab the last words it managed to print:
docker logs --tail 50 web
That nearly always reveals the real failure — a missing env var, a config file that is not where the container expects it, a port already in use. The trick is to read the exit code first and the logs second. I have watched people rebuild images for an hour over a 137 that one docker inspect would have explained in five seconds.
Where to go next
These commands cover the vast majority of daily container work. When you are ready for the rest — networks, volumes, multi-stage builds, buildx, multi-arch — add them when a real need shows up, not before. Premature depth is how you build a stack you cannot debug.
For the full searchable reference with every flag, pitfall, and copy-ready example, keep the Docker cheat sheet handy. And since version control is the other thing every developer Googles weekly, the companion Git cheat sheet pairs naturally with it.
Made by Toolora · Updated 2026-06-13