docker run: Ports, Names, Detached & Env

Docker & DevOps · Lesson 6 of 26 8 min read Updated

docker run has more than a hundred options, but you will use about ten of them almost every day. This lesson covers those ten: how to reach a container through a port, how to name it, how to run it in the background, how to pass settings through environment variables, and how to make it restart by itself.

You will practise with two real images: the Nginx web server and the MySQL database.

The shape of a docker run command

Every docker run command follows the same order. Options for Docker come before the image name. Anything after the image name is the command to run inside the container, which replaces the image's default command.

Text
docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARGS...]

Putting an option after the image is a very common mistake: docker run nginx:alpine -p 8080:80 passes -p 8080:80 to Nginx as a command, not to Docker.

Publishing ports with -p

A container has its own private network. A web server listening on port 80 inside it cannot be reached from your browser until you publish that port with -p HOST_PORT:CONTAINER_PORT.

Terminal
docker run -d --name menu-site -p 8080:80 nginx:alpine
docker run -d --name menu-local -p 127.0.0.1:8081:80 nginx:alpine
docker run -d --name menu-random -P nginx:alpine
docker port menu-random
Output
80/tcp -> 0.0.0.0:32768
80/tcp -> [::]:32768
  • -p 8080:80 listens on port 8080 on every network interface of your computer, so other machines on the network can reach it too.
  • -p 127.0.0.1:8081:80 listens only on localhost. Only programs on the same computer can connect. Use this for databases and for apps that sit behind a reverse proxy.
  • -P (capital P) publishes every port the image declares with EXPOSE on a random free host port. docker port shows which ones it picked.
  • Add /udp for UDP ports, such as -p 5353:53/udp. You can repeat -p as many times as you need.

The left side must be free on your computer. Two containers cannot both publish host port 8080, but both can use port 80 inside their own network, because each container has its own.

Naming and detaching

--name gives a container a fixed name, so scripts and other commands can find it. Names must be unique among all containers, running or stopped.

-d runs the container detached, in the background. Without -d, the container is attached to your terminal: you see its output live, and pressing Ctrl+C stops it. Attached mode is handy for a quick test; detached mode is what you want for services.

Environment variables with -e and --env-file

Images are built once and run in many places, so they read their settings from environment variables, named values that a program can read when it starts. You set them with -e NAME=value:

Terminal
docker run --rm -e CITY=Pune -e SHOP="Meera Sweets" alpine:3.22 sh -c 'echo "$SHOP in $CITY"'
Output
Meera Sweets in Pune

The single quotes stop your shell from expanding $SHOP, so the shell inside the container does it instead.

Official images document which variables they understand. The MySQL image, for example, creates a root password, a database and a user from variables on its first start. When you have several variables, put them in a file, one NAME=value per line, and pass it with --env-file:

Text
# school-db.env
MYSQL_ROOT_PASSWORD=Root-Change-Me-2026
MYSQL_DATABASE=school
MYSQL_USER=kabir
MYSQL_PASSWORD=Kabir-Change-Me-2026
Terminal
docker run -d --name school-db --env-file school-db.env -p 127.0.0.1:3307:3306 mysql:8.4
docker ps --filter name=school-db --format '{{.Names}}: {{.Status}}'
Output
school-db: Up 40 seconds

The --filter and --format options trim docker ps down to one line. MySQL needs about 20 seconds on its first start to create the database and the user. After that, a MySQL client on your computer can connect to 127.0.0.1 port 3307 as kabir. The env file format is simple: no export, no spaces around =, and quotes are taken literally. Keep env files with real passwords out of Git. You will learn safer ways to handle passwords in Environment Variables & Secrets.

Overriding the command and working directory

Text after the image name replaces the default command. -w sets the working directory inside the container:

Terminal
docker run --rm node:26-alpine node -e "console.log(2 ** 10)"
docker run --rm -w /tmp alpine:3.22 pwd
Output
1024
/tmp

This is a handy way to use tools without installing them. Need to check something in Node.js 26 but have Node.js 22 installed? Run it in a container and throw the container away.

Restart policies

--restart tells Docker what to do when a container's main process stops:

PolicyBehaviour
noNever restart (the default)
on-failure[:N]Restart only if it exits with a non-zero code, at most N times
alwaysAlways restart; also start again when the Docker daemon starts
unless-stoppedLike always, but not if you stopped it yourself before the reboot

For services on a server, unless-stopped is the usual choice: the app comes back after a crash or a reboot, but docker stop still keeps it down.

A summary of everyday options

OptionExamplePurpose
-d-dRun in the background
--name--name school-dbFixed, unique name
-p-p 127.0.0.1:8080:80Publish a port
-e-e TZ=Asia/KolkataSet one environment variable
--env-file--env-file app.envLoad variables from a file
--rm--rmRemove the container when it stops
-it-itInteractive terminal
-w-w /appWorking directory
--restart--restart unless-stoppedRestart policy
-v-v dbdata:/var/lib/mysqlAttach storage (see Volumes & Bind Mounts)

Common mistakes

  • Options after the image name. docker run nginx:alpine -d does not run detached. Put all Docker options before the image.
  • "port is already allocated". Another container or program already uses that host port. Pick another host port, such as -p 8082:80.
  • Swapping the two port numbers. The order is HOST:CONTAINER. -p 80:8080 sends host port 80 to container port 8080, where Nginx is not listening.
  • Changing -e values on an existing container. Environment variables are fixed when the container is created. Remove it and run a new one with the new values.
  • Publishing a database on 0.0.0.0 on a server. Bind it to 127.0.0.1 or keep it on a private Docker network.

Key points

  • Docker options go before the image name; anything after it is the command inside the container.
  • -p HOST:CONTAINER publishes a port; add 127.0.0.1: in front to keep it private to the machine.
  • Published ports bypass ufw on Linux, so never publish databases to the internet by accident.
  • -e and --env-file pass settings; official images document the variables they read.
  • --restart unless-stopped keeps services running after crashes and reboots.

Exercise

Start a MySQL 8.4 container named library-db for a Jaipur library app. It should run in the background, restart automatically unless you stop it, create a database called library and a user called isha, and be reachable only from your own computer on port 3310. Use an env file for the passwords. Then show which host port is mapped.

Show solution
Text
# library-db.env
MYSQL_ROOT_PASSWORD=Root-Secret-Change-Me
MYSQL_DATABASE=library
MYSQL_USER=isha
MYSQL_PASSWORD=Isha-Secret-Change-Me
Terminal
docker run -d --name library-db --restart unless-stopped --env-file library-db.env -p 127.0.0.1:3310:3306 mysql:8.4
docker port library-db
Output
3306/tcp -> 127.0.0.1:3310

Binding to 127.0.0.1 keeps the database private to your machine, and the env file keeps passwords out of your shell history.

Quick Quiz

1. What does -p 8080:80 do?

2. Which option makes a published port reachable only from the same computer?

3. Which restart policy brings a service back after a reboot but respects a manual docker stop?

4. Why does docker run nginx:alpine -p 8080:80 not publish the port?

Learn Docker & DevOps with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call