Contents

Run Your First Docker Container: docker run Tutorial

In this section, you'll run an nginx web server and learn how to inspect, enter, stop, and remove a container. Each step includes the actual output.

Start nginx

docker run -d -p 8080:80 --name web nginx:1.30
$ docker run -d -p 8080:80 --name web nginx:1.30
Unable to find image 'nginx:1.30' locally
1.30: Pulling from library/nginx
ecc510c1e359: Pull complete
f4616bb1be1c: Pull complete
f1a6a629b824: Pull complete
3d774efa34e3: Pull complete
cb44c3fe542b: Pull complete
121907e00c1c: Pull complete
220a6ac36368: Pull complete
Digest: sha256:9bf97bd7714f5e24c1ccd545ecb9eb5435cb6d109c97cebb15e7e455e0239edb
Status: Downloaded newer image for nginx:1.30
b470f4d4563701824e59040cdfcd2154b4bbcca581e6d2d75cae5f2c23dbcfda

Here's what happened, in order:

  1. The nginx:1.30 image isn't on your machine (Unable to find image ... locally), so Docker first downloads it from Docker Hub.
  2. An image consists of multiple layers. Each Pull complete line means one layer has finished downloading.
  3. After the download, Docker uses this image to create a new container named web.
  4. -d runs the container in the background, so the command exits immediately and prints only the container ID (a long string of hexadecimal characters).

Parameter overview

ParameterMeaning
-ddetached; run in the background
-p 8080:80Map port 8080 on the host to port 80 in the container
--name webName the container web so you can refer to it by name later
nginx:1.30Image name and version (tag)

Open it in a browser

Open a browser and enter http://localhost:8080:

nginx default welcome page
Open http://localhost:8080 in a browser to see the nginx welcome page

Traffic flows like this: browser → host port 8080 → Docker forwards it to container port 80 → nginx in the container.

You can also test with curl in a terminal:

$ curl -s http://localhost:8080 | head -n 4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

List running containers: docker ps

$ docker ps
CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS         PORTS                                     NAMES
b470f4d45637   nginx:1.30   "/docker-entrypoint.…"   7 seconds ago   Up 7 seconds   0.0.0.0:8080->80/tcp, [::]:8080->80/tcp   web
ColumnMeaning
CONTAINER IDThe first 12 characters of the container ID; you can use it instead of the name in commands
IMAGEThe image in use
COMMANDThe command run when the container starts
CREATED / STATUSWhen it was created and its current status (Up means it's running)
PORTSPort mapping; 0.0.0.0:8080->80/tcp is the -p 8080:80 mapping from earlier
NAMESThe container name

View logs: docker logs

nginx records every request in its logs. You just made one request from the browser and another with curl:

$ docker logs web
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2026/10/12 14:38:25 [notice] 1#1: using the "epoll" event method
2026/10/12 14:38:25 [notice] 1#1: nginx/1.30.5
2026/10/12 14:38:25 [notice] 1#1: built by gcc 14.2.0 (Debian 14.2.0-19)
2026/10/12 14:38:25 [notice] 1#1: OS: Linux 5.4.0-216-generic
2026/10/12 14:38:25 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2026/10/12 14:38:25 [notice] 1#1: start worker processes
2026/10/12 14:38:25 [notice] 1#1: start worker process 28
172.17.0.1 - - [12/Oct/2026:14:38:29 +0000] "GET / HTTP/1.1" 200 896 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/130.0.6723.31 Safari/537.36" "-"
172.18.0.1 - - [12/Oct/2026:14:38:31 +0000] "GET / HTTP/1.1" 200 896 "-" "curl/8.22.0" "-"

The first lines are startup messages from nginx; you can see the version is nginx/1.30.5. The last two lines are the requests you just made: one from the browser (the User-Agent identifies the browser) and one from curl. Add -f to keep following new logs (press Ctrl + C to exit):

docker logs -f web

Enter a container: docker exec

docker exec runs a command inside a running container:

$ docker exec web nginx -v
nginx version: nginx/1.30.5

$ docker exec web ls /usr/share/nginx/html
50x.html
index.html

Add -it to open an interactive shell and look around:

$ docker exec -it web bash
root@b470f4d45637:/# cat /etc/os-release | head -n 2
PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
NAME="Debian GNU/Linux"
root@b470f4d45637:/# ls /etc/nginx
conf.d
fastcgi_params
mime.types
modules
nginx.conf
scgi_params
uwsgi_params
root@b470f4d45637:/# ps aux
bash: line 1: ps: command not found
root@b470f4d45637:/# exit
exit

The output shows a few things:

  • The official nginx image is based on Debian 13 (trixie).
  • b470f4d45637 in the prompt is the container's hostname; by default, it's the container ID.
  • The container doesn't have the ps command. The official image includes only what's needed to run nginx, so many common tools are omitted. That's normal. If you need debugging tools, you can install them temporarily (apt-get update && apt-get install -y procps), or use docker top to inspect the container from outside.

Type exit to leave; the container keeps running.

Change the web page

Change the home page inside the container, then refresh the browser:

$ docker exec web sh -c 'echo "<h1>Hello from my first container</h1>" > /usr/share/nginx/html/index.html'

$ curl -s http://localhost:8080
<h1>Hello from my first container</h1>
nginx home page after editing
After changing index.html in the container, the browser immediately shows the new content

Changing files directly inside a container is suitable only for temporary testing. When you remove the container, the changes disappear. The right approach is to mount the web files into the container (see Volume) or add them to a Dockerfile.

Stop, start, and remove

$ docker stop web
web

$ docker ps -a
CONTAINER ID   IMAGE         COMMAND                  CREATED              STATUS                              PORTS     NAMES
b470f4d45637   nginx:1.30    "/docker-entrypoint.…"   12 seconds ago       Exited (0) Less than a second ago             web
786dd898c04b   hello-world   "/hello"                 About a minute ago   Exited (0) About a minute ago                 frosty_shirley

$ docker start web
web

$ curl -s http://localhost:8080
<h1>Hello from my first container</h1>

$ docker rm web
Error response from daemon: cannot remove container "web": container is running: stop the container before removing or force remove

$ docker rm -f web
web

$ docker ps -a
CONTAINER ID   IMAGE         COMMAND    CREATED              STATUS                          PORTS     NAMES
786dd898c04b   hello-world   "/hello"   About a minute ago   Exited (0) About a minute ago             frosty_shirley
  • docker stop: Stops the container. It still exists, appears in docker ps -a, and has the status Exited.
  • docker start: Restarts an existing container. The home page you changed is still there.
  • docker rm: Removes a stopped container. If you use it on a running container, you'll get the container is running error shown above. Stop it first with docker stop or force removal with -f.

The docker ps -a list still includes the hello-world container you ran in the previous section: it finished running (Exited (0)) but wasn't removed. Containers started without --name get a random name, such as frosty_shirley.

After you remove the container, the home page you changed disappears too. Start a new container from the same image, and you'll see the original nginx welcome page again.

Run a one-off command

Not every container needs to run long term. This container runs one command and exits; --rm removes it automatically when it finishes:

$ docker run --rm ubuntu:26.04 cat /etc/os-release
Unable to find image 'ubuntu:26.04' locally
26.04: Pulling from library/ubuntu
06ad70e463aa: Pull complete
4e07a0f12b2c: Pull complete
Digest: sha256:f144425ff09be612d6d9ad965196e9cdc23dae1f42110a8a11a3e9a8198759f7
Status: Downloaded newer image for ubuntu:26.04
PRETTY_NAME="Ubuntu 26.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="26.04"
VERSION="26.04.1 LTS (Resolute Raccoon)"
VERSION_CODENAME=resolute
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=resolute
LOGO=ubuntu-logo

This Ubuntu container has only the Ubuntu user-space files. It has no boot process or desktop, and it takes only a moment to start and exit.

Use the Docker Desktop GUI

On Windows and macOS, you can also complete each step above with the mouse in Docker Desktop.

Search for and pull an image

Enter the image name in the search box at the top, choose the version (Tag), and click Pull to download it, or click Run directly:

Search for an image in Docker Desktop
Search for an image in Docker Desktop, then choose a Tag on the right and click Pull or Run. Image: Docker documentation (Apache-2.0)

Configure and run a container

After clicking Run, expand Optional settings to set the container name, ports, Volume, and environment variables. These correspond to --name, -p, -v, and -e in docker run:

Docker Desktop settings dialog for running a new container
Set Host port to 8080, equivalent to docker run -p 8080:80. Image: Docker documentation (Apache-2.0)

View and manage containers

The Containers item in the left menu lists all containers, equivalent to docker ps -a. Click the link in the Port(s) column to open it in a browser. Use the buttons on the right to stop, restart, or remove a container:

Docker Desktop container list
Containers screen: status, ports, and action buttons. Image: Docker documentation (Apache-2.0)

Open a container to see tabs such as Logs (equivalent to docker logs), Exec (equivalent to docker exec), and Files (browse files inside the container):

Files tab for a container in Docker Desktop
The Files tab lets you browse and edit files in the container. Image: Docker documentation (Apache-2.0)

The GUI is convenient for checking status, but in practice you'll mainly use commands: you can put commands in documentation and scripts, and Linux servers can only be managed with commands.

Windows users: command syntax differences

The commands in this tutorial use bash on Linux and macOS. On Windows, the Docker commands themselves are identical, but the terminal (shell) syntax differs:

SyntaxLinux / macOS (bash)Windows PowerShellWindows CMD
Current directory$(pwd)${PWD}%cd%
Line continuation\ at end of line` at end of line^ at end of line
Environment variable$HOME$env:USERPROFILE%USERPROFILE%
Read input from a file <YesNot supportedYes

Commands in this tutorial that need different syntax are shown in three tabs: Linux / macOS, Windows PowerShell, and Windows CMD. Select a tab to switch the whole page to that syntax. For example, mount the site directory in the current directory into nginx:

docker run -d -p 8080:80 -v "$(pwd)/site:/usr/share/nginx/html:ro" nginx:1.30
docker run -d -p 8080:80 -v "${PWD}/site:/usr/share/nginx/html:ro" nginx:1.30
docker run -d -p 8080:80 -v "%cd%/site:/usr/share/nginx/html:ro" nginx:1.30

A few common pitfalls (see Common error messages for the actual error messages):

  • In PowerShell, "$PWD:/app" causes an error; you must write "${PWD}:/app".
  • PowerShell doesn't support <. To restore a database, use docker cp; the database chapters include examples.
  • If you edit a shell script on Windows and it uses CRLF line endings, running it in a container can produce no such file or directory.

You can also use Docker Desktop from a terminal in WSL (for example, Ubuntu). In that case, use the Linux syntax from this tutorial.

Recap

  • docker run -d -p host-port:container-port --name name image starts a container in the background and maps ports.
  • Use docker ps to list running containers; add -a to include stopped ones.
  • Use docker logs to view output and docker exec -it container sh to enter a container.
  • Use docker stop / start / rm -f to control and remove containers; changing files inside a container directly is suitable only for temporary testing.

Exercises

  1. Start another nginx container and map it to host port 8081: docker run -d -p 8081:80 --name web2 nginx:1.30. Open http://localhost:8081 to check it.
  2. Use docker ps to confirm that both containers are running at the same time, each mapped to a different host port.
  3. Remove both containers: docker rm -f web web2.

The next section takes a closer look at images: naming and tag rules, and how to pull, inspect, and remove them.

FAQ

Can't open http://localhost:8080?

First, use docker ps to confirm the container is running and the PORTS column shows 0.0.0.0:8080->80/tcp. If another program is using 8080, docker run reports port is already allocated; use -p 8081:80 instead.

Why aren't commands like ps and vim available in the container?

Official images contain only what's needed to run the service, keeping the image small and reducing vulnerabilities. For debugging, use docker exec to run tools already in the image, or start another container that includes the tools.

Can I use a container ID and name interchangeably?

Yes. You can use the name or the first few characters of the ID in commands, as long as they don't match another container.

Updated October 14, 2026