Contents

Docker Run Tutorial: Essential Flags and Real-World Examples

docker run is Docker's most commonly used command, and it has more flags than any other command. Its basic syntax is:

docker run [flags] IMAGE[:tag] [COMMAND] [COMMAND_ARGS]

The part after the image name replaces the image's default command (CMD):

$ docker run --rm alpine:3.23 cat /etc/alpine-release
3.23.6

--rm is a Docker flag. cat /etc/alpine-release is the command that runs inside the container, replacing Alpine's default /bin/sh.

Always put flags before the image name

Everything after the image name is treated as a command to run inside the container:

$ docker run alpine:3.23 --rm echo hi
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "--rm": executable file not found in $PATH

Run 'docker run --help' for more information

Docker tries to run a program named --rm inside the container and, unsurprisingly, can't find it (executable file not found). When you see this error, check the flag placement first.

Run modes

FlagDescription
-d, --detachRun in the background and return immediately after printing the container ID
-i, --interactiveKeep standard input open
-t, --ttyAllocate a pseudo-terminal
--rmAutomatically remove the container when it exits
--nameSet the container name; otherwise, Docker generates a random one (for example, dreamy_solomon)
docker run -it --rm ubuntu:26.04 bash      # Open an interactive shell and remove the container when you exit
docker run -d --name cache redis:8-alpine  # Run a background service

-it are almost always used together to open an interactive shell. With -i alone, you can pipe data into a container:

$ echo "hello" | docker run -i --rm alpine:3.23 cat
hello

Ports: -p

The syntax is -p HOST_PORT:CONTAINER_PORT. Here are three forms in action:

$ docker run -d --name p1 -p 8080:80 nginx:1.30
27b89a4fae92dfe05bf051e11700033c7b9bb71f8747acc986d2a535cc1baa00

$ docker run -d --name p2 -p 127.0.0.1:8081:80 nginx:1.30
3327f3c1df4fa1e03fa3a120e5d889859fc0cb53d0aaf269826c81b25f4411c2

$ docker run -d --name p3 -P nginx:1.30
57c9a309c4758786361738b7b9a4231f404cb9bbab28f38cad39b4b6e996d9e0

$ docker port p1
80/tcp -> 0.0.0.0:8080
80/tcp -> [::]:8080

$ docker port p2
80/tcp -> 127.0.0.1:8081

$ docker port p3
80/tcp -> 0.0.0.0:32768
80/tcp -> [::]:32768
FormResult
-p 8080:80Binds to all network interfaces (0.0.0.0 and IPv6 [::]), so other computers can connect
-p 127.0.0.1:8081:80Binds only to the local machine, so only this computer can connect
-PMaps ports declared by the image with EXPOSE to random host ports (this time, port 32768)

Run docker port CONTAINER_NAME to see the actual mappings. For a UDP port, use -p 5353:53/udp.

On a cloud host, -p 8080:80 means external clients can connect directly. Docker also modifies firewall rules, so ufw settings might not block access. For services such as databases that should not be exposed externally, bind to 127.0.0.1 or omit -p and use a custom network to connect containers.

Data: -v and --mount

-v HOST_PATH:CONTAINER_PATH mounts a host directory into a container (a bind mount). Create a site directory containing an index.html file:

$ ls site
index.html

$ docker run -d --name site -p 8080:80 -v "$(pwd)/site:/usr/share/nginx/html:ro" nginx:1.30
e91f46b91ff8f0f64d59d6605bfddea0ee5bca809de86d81cbd5051815e65dd1

$ curl -s http://localhost:8080 | head -n 3
<!doctype html>
<html><head><meta charset="utf-8"><title>My site</title>
<style>body{font-family:system-ui,sans-serif;max-width:640px;margin:60px auto;padding:0 16px;color:#1f2937}h1{color:#1d63ed}</style></head>

Windows equivalents (select a tab to switch):

docker run -d --name site -p 8080:80 -v "$(pwd)/site:/usr/share/nginx/html:ro" nginx:1.30
docker run -d --name site -p 8080:80 -v "${PWD}/site:/usr/share/nginx/html:ro" nginx:1.30
docker run -d --name site -p 8080:80 -v "%cd%/site:/usr/share/nginx/html:ro" nginx:1.30
nginx displaying a web page mounted with a bind mount
nginx displays the contents of site/index.html on the host

The trailing :ro means read-only. Attempts to write to this directory from inside the container will be denied:

$ docker exec site sh -c "echo test > /usr/share/nginx/html/x.txt"
sh: 1: cannot create /usr/share/nginx/html/x.txt: Read-only file system

Another option is a Docker-managed Volume (-v NAME:CONTAINER_PATH), which is suitable for storing data such as database files:

docker run -d -v pgdata:/var/lib/postgresql postgres:18

--mount is a more explicit, but longer, way to write a mount. See Volumes and Bind Mounts to learn about the differences and when to use each.

Environment variables: -e and --env-file

$ docker run --rm -e GREETING=hello -e APP_ENV=dev alpine:3.23 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=18f035817c43
GREETING=hello
APP_ENV=dev
HOME=/root

If you specify -e VARIABLE_NAME without a value, Docker uses the host's current environment variable with the same name:

$ APP_ENV=prod docker run --rm -e APP_ENV alpine:3.23 sh -c 'echo $APP_ENV'
prod

When you have many variables, use --env-file FILE instead. See Environment Variables and .env Files for details.

Networks

Containers on the same custom network can connect to each other by container name:

$ docker network create mynet
2ecd542737519b0b8919061077814e007a72e2d07973f0840608b1b5454c678e

$ docker run -d --name myweb --network mynet nginx:1.30
9a6ca3dca3a003e28b9870f3eec436cf7023a6872e8ddd4cee971d29f845583b

$ docker run --rm --network mynet alpine:3.23 ping -c 2 myweb
PING myweb (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.094 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.064 ms

--- myweb ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.064/0.079/0.094 ms

$ docker run --rm alpine:3.23 ping -c 1 -W 2 myweb
ping: bad address 'myweb'
  • The first ping container is on mynet, where Docker's built-in DNS resolves myweb to 172.19.0.2.
  • The second container has no --network flag, so it uses the default network and cannot find myweb (bad address).

Avoid container names that are also top-level domains (TLDs), such as web or app. When containers aren't on the same network, external DNS may resolve these names to an unexpected address (for example, 127.0.53.53), making troubleshooting more confusing.

docker run --network host nginx:1.30     # Use the host's network directly (Linux)
docker run --network none alpine:3.23    # Disable networking entirely

See Networking and Container Connectivity for details.

Restart policies

docker run -d --restart unless-stopped nginx:1.30

Available values are no, on-failure, always, and unless-stopped. See Container Lifecycle for how they work.

Resource limits

$ docker run -d --name limited --memory 64m --cpus 0.5 nginx:1.30
2e65ce89f346cb7d9d57f1e6ad529d63ad53d1d914b6d3178dc3344a1d3325f0

$ docker stats --no-stream limited
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O       BLOCK I/O         PIDS
2e65ce89f346   limited   0.00%     3.922MiB / 64MiB    6.13%     736B / 126B   1.77MB / 8.19kB   2

MEM USAGE / LIMIT shows 3.922MiB / 64MiB, confirming that the memory limit is in effect. When idle, nginx uses less than 4 MB.

FlagDescription
--memory, -mMemory limit, such as 512m or 2g; if exceeded, the process is killed (OOM, exit code 137)
--memory-swapCombined limit for memory and swap
--cpusNumber of CPUs available; can be a decimal, so 0.5 means half a CPU
--pids-limitMaximum number of processes in the container

For a real example of exceeding the memory limit, see the OOM example in Viewing Logs and Troubleshooting.

Override image defaults

$ docker run --rm --entrypoint sh nginx:1.30 -c "nginx -v; whoami"
nginx version: nginx/1.30.5
root

$ docker run --rm -u 1000:1000 alpine:3.23 id
uid=1000 gid=1000 groups=1000

$ docker run --rm -w /tmp alpine:3.23 pwd
/tmp

$ docker run --rm -h myhost alpine:3.23 hostname
myhost
FlagDescription
--entrypointOverride ENTRYPOINT; often used to inspect an image that would otherwise start a service immediately
-u, --userRun as the specified user (UID:GID)
-w, --workdirOverride the working directory
-h, --hostnameSet the container's hostname

Use -u to make files created by a container belong to you instead of root:

docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd):/work" -w /work node:24-alpine npm install

Read-only file system

$ docker run --rm --read-only alpine:3.23 touch /hello
touch: /hello: Read-only file system

--read-only makes the container's entire file system read-only, so even a compromised application can't modify files. Allow writes where needed with --tmpfs /tmp or a Volume. See Security Best Practices.

Other useful flags

FlagDescription
--initUse a small init process as PID 1 to forward signals correctly (docker stop won't need to wait 10 seconds; see Container Lifecycle)
--platformSpecify a platform, such as linux/amd64
--add-hostAdd an entry to the hosts file, such as --add-host host.docker.internal:host-gateway
--labelAdd a label to make filtering easier
--pull alwaysPull the image again before each run
--log-opt max-size=10mLimit the log file size

Quick reference to more flags

This section covers useful flags not explained in detail in this chapter, based on docker run --help (Docker 29). Run docker run --help for the full list.

CategoryFlagDescription
Security--cap-add / --cap-dropAdd or remove Linux capabilities; see Security Best Practices for a hands-on example
Security--security-optSecurity options, such as no-new-privileges and seccomp settings
Security--privilegedGive the container all privileges, nearly disabling isolation; avoid using it
Security--usernsSelect the user namespace
Health checks--health-cmdHealth check command, such as --health-cmd "curl -f http://localhost/"
Health checks--health-interval / --health-timeout / --health-retriesCheck interval, timeout, and number of consecutive failures before marking a container unhealthy
Health checks--health-start-period / --health-start-intervalGrace period and check interval during startup
Health checks--no-healthcheckDisable the HEALTHCHECK set in the image
Networking--network-aliasAdd another name on the network; see docker network
Networking--ipAssign a static IP on a custom network
Networking--dns / --dns-searchSet custom DNS servers and search domains
Networking--exposeDeclare a port without mapping it to the host; same effect as Dockerfile EXPOSE
Networking-P, --publish-allMap all declared ports to random host ports
Stopping--stop-signalSignal sent by docker stop; defaults to SIGTERM
Stopping--stop-timeoutNumber of seconds docker stop waits; defaults to 10
Resources--cpuset-cpusUse only specified CPUs, such as 0,1 or 0-3
Resources-c, --cpu-sharesRelative CPU weight (applies only when resources are contested)
Resources--memory-reservationSoft memory limit
Resources--shm-sizeSize of /dev/shm; applications such as PostgreSQL and browsers often need a larger value
Resources--ulimitFor example, --ulimit nofile=65535:65535 adjusts the open-file limit
Resources--oom-kill-disableDisable killing a process when memory runs low (must be used with --memory)
Devices--devicePass a host device to the container, such as /dev/ttyUSB0
Devices--gpusUse GPUs, such as --gpus all (requires NVIDIA Container Toolkit)
Namespaces--pid host / --ipc / --utsShare namespaces with the host or another container; see Under the Hood for how this works
System--sysctlSet kernel parameters for the container, such as net.core.somaxconn=1024
System--umaskSet the umask for processes in the container
Mounts--volumes-fromMount all Volumes from another container
Other--cidfileWrite the container ID to a file for use in scripts
Other--label-fileRead labels from a file
Other--log-driverLog driver, such as json-file, local, or syslog
Other-q, --quietHide progress when pulling an image
Other--use-api-socketMount the Docker API socket and login credentials into the container (equivalent to host root access; see Security Best Practices)

Common combinations

# 1. One-off utility container: run a command in the current directory
docker run --rm -v "$(pwd):/data" -w /data alpine:3.23 ls -la

# 2. Local development database, accessible only from the local machine
docker run -d --name devdb --restart unless-stopped \
  -p 127.0.0.1:5432:5432 \
  -e POSTGRES_PASSWORD=dev \
  -v devdb:/var/lib/postgresql \
  postgres:18

# 3. Production service with resource and log size limits
docker run -d --name api --restart unless-stopped \
  --memory 1g --cpus 2 \
  --log-opt max-size=10m --log-opt max-file=3 \
  --env-file .env -p 3000:3000 \
  myorg/api:1.4.2
# 1. One-off utility container: run a command in the current directory
docker run --rm -v "${PWD}:/data" -w /data alpine:3.23 ls -la

# 2. Local development database, accessible only from the local machine
docker run -d --name devdb --restart unless-stopped `
  -p 127.0.0.1:5432:5432 `
  -e POSTGRES_PASSWORD=dev `
  -v devdb:/var/lib/postgresql `
  postgres:18

# 3. Production service with resource and log size limits
docker run -d --name api --restart unless-stopped `
  --memory 1g --cpus 2 `
  --log-opt max-size=10m --log-opt max-file=3 `
  --env-file .env -p 3000:3000 `
  myorg/api:1.4.2
# 1. One-off utility container: run a command in the current directory
docker run --rm -v "%cd%:/data" -w /data alpine:3.23 ls -la

# 2. Local development database, accessible only from the local machine
docker run -d --name devdb --restart unless-stopped ^
  -p 127.0.0.1:5432:5432 ^
  -e POSTGRES_PASSWORD=dev ^
  -v devdb:/var/lib/postgresql ^
  postgres:18

# 3. Production service with resource and log size limits
docker run -d --name api --restart unless-stopped ^
  --memory 1g --cpus 2 ^
  --log-opt max-size=10m --log-opt max-file=3 ^
  --env-file .env -p 3000:3000 ^
  myorg/api:1.4.2

Commands get hard to maintain as the number of flags grows. For long-running services or setups with multiple containers, use Docker Compose and put the configuration in a file.

Recap

  • Put flags before the image name; anything after it is a command to run inside the container.
  • -d, --rm, --name, -p, -v, and -e are the most common flags.
  • --memory, --cpus, and --pids-limit limit resources.
  • --entrypoint, -u, and -w override image defaults.

Exercises

Exercise 1: Write a command to run nginx:1.30-alpine in the background, name it site, allow connections to port 8080 only from the local machine, set a 128 MB memory limit, and remove the container when it exits.

Answer
docker run -d --rm --name site -p 127.0.0.1:8080:80 --memory 128m nginx:1.30-alpine

--rm automatically removes the container after it stops.

Exercise 2: The postgres:18 image's ENTRYPOINT starts the database. How can you open its shell without starting the database?

Answer
docker run --rm -it --entrypoint sh postgres:18

--entrypoint overrides the image's ENTRYPOINT.

FAQ

Why does a flag have no effect when I put it after the image name?

Everything after the image name is treated as a command to run inside the container. You must put docker run flags before the image name.

What's the difference between -p 8080:80 and -p 127.0.0.1:8080:80?

The first binds to all network interfaces, so other computers can connect. The second binds only to the local machine. For services such as databases that should not be exposed externally, use the second form or omit -p entirely.

How many flags does docker run have?

Docker 29's docker run --help lists about 100 flags. This chapter covers the most common ones, and the final Quick reference to more flags lists other useful options.

Updated October 14, 2026