University of Wales Trinity Saint David
ACCA7023 - Agile and DevOps

Docker Command Line Essentials

Overview

This lab builds fluency with the Docker commands you will use every day. You will run a real web server in a container, watch its logs, work inside it, stop and restart it, configure it, and give it storage that outlives it. Every command is something you will meet again in the rest of the series and in industry.

Learning Objectives

By the end of this lab you will be able to:

Concept

Most Docker commands follow the pattern docker <object> <action>, for example docker container ls or docker image rm. The most common ones also have short forms, so docker ps means docker container ls and docker run means docker container run. docker <command> --help lists every option for any command.

A container is isolated from your machine by default, including its network. A web server listening on port 80 inside a container cannot be reached from your browser until you publish the port, which tells Docker to forward a port on your machine to a port in the container.

Every container gets a thin writable layer on top of its read-only image. Anything the application writes goes there. The layer survives stopping and starting the container, but it is deleted with the container. That is deliberate. Containers are meant to be disposable, replaced instead of repaired, so data that matters must live outside them. Docker offers two ways to do that. A bind mount maps a folder on your machine into the container. A named volume is storage that Docker creates and manages itself, independent of any container.

Tasks

Task 1 - Run a web server in the background

nginx is a popular web server. Start it in a container.

docker run -d --name web -p 8080:80 nginx:1.29-alpine
PartMeaning
-dDetached. Run in the background and give the terminal back.
--name webA name to refer to the container by. Without it Docker invents one.
-p 8080:80Publish a port. Always your machine first, container second.
nginx:1.29-alpineThe image. The part after the colon is the tag, which picks a version.

Success condition: Docker prints a long ID, and http://localhost:8080 in your browser shows Welcome to nginx!

Task 2 - See what a container is doing

docker ps
docker logs web

docker ps shows the container, its image, its status and its port mapping. docker logs shows everything the main process has written to its output. Now follow the logs live with -f, refresh the browser a few times, and press Ctrl+C to stop following. Stopping the follow does not stop the container.

docker logs -f web

Success condition: one new GET / line appears for each refresh. Containerised applications are expected to log to their output instead of to files, so that docker logs, and the platforms you will meet later, can collect them.

docker stats --no-stream
docker inspect web

docker stats shows live CPU and memory use. docker inspect prints every detail Docker knows about the container as JSON. Scroll through it and find the IP address the container was given on Docker's internal network.

Task 3 - Work inside a running container

docker exec runs an extra command inside a container that is already running. With -it and a shell you get an interactive session, which is the usual way to investigate a problem.

docker exec web nginx -v
docker exec -it web sh

Inside the container, replace the home page, then leave.

echo "<h1>Changed inside the container</h1>" > /usr/share/nginx/html/index.html
exit

Success condition: refreshing the browser shows your heading. You changed the container's writable layer. The image is unchanged.

Task 4 - Explore the container lifecycle

Use the buttons to drive a container through its lifecycle. Try commands that are not allowed in the current state as well, and read the explanations.

Now do it for real. Stop the container, show that it still exists, start it again and check the page.

docker stop web
docker ps
docker ps -a
docker start web

Success condition: after docker start the browser still shows Changed inside the container. Stopping kept the writable layer. Now remove the container and create a fresh one from the same image.

docker rm -f web
docker run -d --name web -p 8080:80 nginx:1.29-alpine

Success condition: the page is back to Welcome to nginx! Your change was in the writable layer of the old container, and docker rm deleted it. -f forces removal of a running container by stopping it first.

Task 5 - Configure containers with environment variables

Images are built once and configured when they run, usually with environment variables passed using -e. The single quotes stop your own shell from replacing $NAME before Docker sees it.

docker run --rm -e NAME=Sam alpine sh -c 'echo Hello $NAME'

Many official images refuse to start without required settings. Try to start the Postgres database without a password.

docker run --name broken-db postgres:17-alpine
docker ps -a
docker logs broken-db

Success condition: the container status is Exited (1), and the logs explain that POSTGRES_PASSWORD must be set. When a container stops unexpectedly, docker ps -a and docker logs are always the first two things to check. The image page on Docker Hub documents which variables an image expects. Remove the broken container.

docker rm broken-db

Task 6 - Keep data outside the container

A bind mount shares a folder on your machine with a container. Create a folder with a web page in it, then run nginx with that folder mounted over its web root. $(pwd) is the folder you are currently in.

mkdir -p site
echo "<h1>Served from my own folder</h1>" > site/index.html
docker rm -f web
docker run -d --name web -p 8080:80 -v "$(pwd)/site:/usr/share/nginx/html" nginx:1.29-alpine

Success condition: the browser shows Served from my own folder. Edit site/index.html in any editor, save, and refresh. The change appears immediately with no rebuild and no restart. Bind mounts are ideal during development, which you will use in the dev container lab.

A named volume is storage that Docker manages. It is the normal choice for data a container produces, such as a database. Two containers that never run at the same time can share it.

docker volume create notes
docker run --rm -v notes:/data alpine sh -c 'echo "written by the first container" >> /data/log.txt'
docker run --rm -v notes:/data alpine cat /data/log.txt
docker volume ls

Success condition: the second container prints the line written by the first, even though both containers were deleted the moment they finished. The data lives in the volume, not in any container.

-v "$(pwd)/site:/usr/share/nginx/html"   # bind mount: a path on your machine
-v notes:/data                             # named volume: just a name

Task 7 - Tidy up

Images, stopped containers and unused volumes take disk space. See how much, then clean up.

docker system df
docker rm -f web
docker volume rm notes
docker system prune

docker system prune removes stopped containers, unused networks and dangling images, and asks first. It does not remove volumes unless you add --volumes, because volumes usually hold data you care about.

Task 8 - Command drill

Fill in each blank with the missing command or option, then check your answers.

Run nginx in the background: docker run nginx

Make port 3000 in the container reachable as port 5000 on your machine: -p

Show stopped containers as well as running ones: docker ps

Open an interactive shell in a running container called api: docker -it api sh

Watch the output of a container called api as it happens: docker logs api

Pass a setting into a container: docker run PORT=8080 myimage

Delete a container automatically when it exits: docker run alpine echo hi

Mount a named volume called pgdata at /var/lib/postgresql/data: -v

You run docker run -p 80:8080 myapp and the app listens on port 8080. Which address works in your browser?

The first number is your machine, so port 80 on your machine forwards to 8080 in the container.

A container was stopped with docker stop. What happened to files written inside it?

Stopping ends the process but keeps the container and its writable layer. Only docker rm deletes it.

Task 9 - Evaluate

Explain why the change you made in Task 3 disappeared in Task 4, and why Docker is designed to behave this way.

The edit was written to the writable layer of that particular container. docker rm deleted the container, and its writable layer with it, and the new container started with a fresh layer on top of the unchanged image. This is deliberate, because containers are meant to be disposable and identical. If every container could drift by being edited in place, you could no longer trust that two containers from the same image behave the same, and replacing a broken container would lose whatever had been changed. Changes belong in the image, made by rebuilding it, and data belongs in volumes.

When would you choose a bind mount and when a named volume?

A bind mount when you want the container to see files you are editing on your own machine, such as source code during development, or to read configuration from a known host path. A named volume for data the container itself produces and must keep, such as a database, because Docker manages its location, it works the same on every operating system, it performs better on Windows and macOS, and it cannot be broken by someone moving or deleting a folder.