How to run a container from a Docker image?
docker run is the most-used command in the entire Docker CLI. It pulls the image if needed, creates a container, and starts it — all in one shot. Knowing the half-dozen flags that show up in every production command is most of what "using Docker" actually is.
Theory
TL;DR
- Anatomy:
docker run [FLAGS] IMAGE[:TAG] [COMMAND] [ARGS...] - Image goes between flags and any command override. Everything after the image replaces the image's
CMD. - Six flags cover 90% of cases:
-d,--name,-p,-v,-e,--rm. docker run=docker pull(if missing) +docker create+docker start. Three operations in one command.- Use
--rmfor one-off commands (auto-cleanup on exit). Use-d+--restartfor long-lived services.
Quick example
# Minimum: pull and run
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
Digest: sha256:9f8e7d6c...
Status: Downloaded newer image for hello-world:latest
Hello from Docker!The daemon noticed the image was missing, pulled it from Docker Hub, then ran it. One command, three operations.
The six flags you will use every day
-d / --detach — run in the background
docker run -d nginx:1.27-alpine
# Returns immediately with the container ID; container keeps running.Without -d, the CLI streams logs and exits when the container does. With -d, you get back to your shell instantly.
--name — give it a memorable name
docker run -d --name web nginx:1.27-alpine
# Now you can do: docker stop web, docker logs web, etc.
# Without --name, Docker assigns a random name like "sleepy_einstein".-p / --publish — expose ports
# Format: HOST_PORT:CONTAINER_PORT
docker run -d -p 8080:80 nginx:1.27-alpine
# Container's port 80 is reachable at http://localhost:8080
# Bind to specific host interface
docker run -d -p 127.0.0.1:8080:80 nginx:1.27-alpine
# Only loopback, not the public network
# Random host port (auto-assigned)
docker run -d -p 80 nginx:1.27-alpine
# Run docker port <name> to find the host port that was pickedWithout -p, the container is isolated on its own network — reachable from other containers on the same network, but not from the host.
-v / --volume — mount storage
# Named volume
docker run -d -v pgdata:/var/lib/postgresql/data postgres:16
# Bind mount (specific host path)
docker run -d -v /home/me/site:/usr/share/nginx/html nginx:1.27-alpine
# Read-only mount
docker run -d -v ./conf:/etc/myapp:ro myappUse named volumes for persistent state, bind mounts for live-reload dev or specific host paths.
-e / --env — set environment variables
# Single var
docker run -e POSTGRES_PASSWORD=devpass postgres:16
# Pass through from your shell
docker run -e API_KEY postgres:16
# From a file
docker run --env-file .env postgres:16Do NOT pass secrets via -e in production — they show up in docker inspect, image history, and process listings. Use Swarm secrets, BuildKit secret mounts, or external secret managers.
--rm — auto-cleanup on exit
# One-off command, container vanishes when done
docker run --rm -it alpine sh
# When you exit the shell, the container is removed (no orphan).Makes ad-hoc commands feel like running local scripts. Without --rm, every exited container piles up until you docker rm them.
Long-lived service flags
--restart=no # default: do not restart
--restart=on-failure # restart only if exit code != 0
--restart=always # restart always (even on docker daemon restart)
--restart=unless-stopped # like 'always', but respects manual docker stopFor production services, --restart=unless-stopped is the usual choice.
Putting it all together: a realistic command
docker run -d \
--name api \
--restart=unless-stopped \
-p 3000:3000 \
-v api_uploads:/app/uploads \
-v ./api/.env:/app/.env:ro \
-e NODE_ENV=production \
--memory=512m \
--cpus=0.5 \
--health-cmd='curl -f http://localhost:3000/health' \
--health-interval=30s \
myapp:1.0This is essentially what a production single-host deploy looks like before you graduate to Compose, Swarm, or Kubernetes.
Overriding the image's CMD
Anything after the image name replaces the image's default command:
# Image's default CMD (probably the service)
docker run nginx:1.27-alpine
# Override CMD with a one-off command
docker run nginx:1.27-alpine sh -c 'nginx -V'
# Runs nginx -V then exits, instead of starting the daemon.
# Drop into a shell for debugging
docker run -it nginx:1.27-alpine shIf the image has an ENTRYPOINT, your trailing args become its arguments instead. Use --entrypoint /bin/sh to fully override an entrypoint when debugging.
Common mistakes
Forgetting -d for a service
$ docker run nginx:1.27-alpine
# Terminal hangs streaming nginx logs.
# Ctrl+C stops the container too.For anything other than a one-off command, you usually want -d.
Wrong port direction in -p
# WRONG: thinks 80 is on the host, 8080 in container
docker run -p 80:8080 nginx:1.27-alpine
# Nothing serves on container port 8080; you get connection refused on host:80.
# RIGHT: HOST:CONTAINER
docker run -p 8080:80 nginx:1.27-alpineThe order is host first, container second. Common gotcha.
Putting flags after the image name
# WRONG: -p is treated as an argument to nginx
docker run nginx:1.27-alpine -p 8080:80
# RIGHT: flags before image
docker run -p 8080:80 nginx:1.27-alpineDocker stops parsing flags at the image name. Anything after is the command for the container, not for docker run.
Running interactive without -it
# WRONG: shell exits immediately because there is no TTY
docker run alpine sh
# RIGHT: -i keeps stdin open, -t allocates a TTY
docker run -it alpine sh-it (or -i -t) is the magic incantation for interactive sessions.
Real-world usage
- Local development:
docker run --rm -it -v $PWD:/work node:22 npm test— disposable container, mounts current dir, runs tests, vanishes. - Staging/single-host deploy: the long
docker run -d --name api --restart=unless-stopped ...form, often wrapped in a shell script or Compose file. - CI/CD:
docker run --rm myapp:test npm run testproduces an exit code that the CI uses for pass/fail. - One-off DB migrations:
docker run --rm --network mynet -e DATABASE_URL=... migrator:1.0runs and disappears.
Follow-up questions
Q: What is the difference between docker run and docker create?
A: create allocates the container but does not start it; you have to docker start after. run does both. create is useful when you want to set up the container and start it later, or look at its config (docker inspect) before starting.
Q: How do I run a command in an existing container instead of starting a new one?
A: docker exec. docker run always creates a new container; docker exec runs a command in a running one. For a shell into an existing container: docker exec -it <name> sh.
Q: Why is my interactive docker run -it exiting immediately?
A: Probably because the image's ENTRYPOINT or CMD is not a shell. Try docker run -it --entrypoint sh <image> to force a shell. Or check the image's docs for the right command to run.
Q: Can I run a container without giving it a name?
A: Yes — Docker generates a random one (musing_einstein, crazy_curie). Fine for one-off --rm commands. For anything you will reference later (logs, exec, stop), --name saves you typing.
Q: (Senior) Why might you avoid docker run in production and use Compose or Kubernetes instead?
A: A long docker run command is imperative state living in someone's terminal history. Compose makes it declarative (in compose.yaml), version-controllable, and reproducible. Kubernetes goes further (multi-host, self-healing, rolling updates). For a single throwaway service, docker run is fine. For anything you will redeploy, declarative beats imperative.
Examples
Local one-off: run a quick test
$ docker run --rm -it \
-v $PWD:/work \
-w /work \
node:22-alpine \
npm test
# Tests run inside the container, output streams to your terminal,
# container removed when done. Your laptop stays clean.--rm, -it, -v $PWD:/work, -w /work: the four flags that make a container behave like a local script.
Production-style web service
$ docker run -d \
--name web \
--restart=unless-stopped \
-p 80:80 -p 443:443 \
-v web_certs:/etc/nginx/certs \
-v ./nginx.conf:/etc/nginx/nginx.conf:ro \
--memory=256m \
nginx:1.27-alpine
$ docker ps --filter name=web
CONTAINER ID IMAGE STATUS PORTS NAMES
f7c2a9e1b8d4 nginx:1.27-alpine Up 3 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp webDetached, named, restart-on-reboot, two ports published, certs in a named volume, config bind-mounted read-only, memory cap.
Override the image's CMD for debugging
# Image's default CMD starts postgres. We want a shell.
$ docker run --rm -it postgres:16 bash
# Or skip the entrypoint entirely
$ docker run --rm -it --entrypoint sh postgres:16Useful when an image's startup is failing and you need to poke around inside.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.
Comments
No comments yet