Install and verify
- Lesson 3
- 50 min
- Module 1: Understand containers
Lesson 3 of 15 · Docker and Cloud Essentials for AI DevelopersInstall and verify
1. Understand containers
2. Build the application image
3. Networking, configuration and persistence
4. Cloud building blocks
5. Deploy and clean up
What you will be able to do
Get a Docker installation that you know works before you build anything with it. You install Docker, prove it with a known first container, and run a web server in a container. Then you remove everything again, so you also know how to get back to a clean state.
Before you startWhat to know and to have ready
What you need to know
- Image, container and registry, from Environment consistency: Images, containers and Docker.
docker run,ps -a,logs,exec,diffandrm, and the writable layer, from Images and running containers: The writable layer.- Ports and the address
127.0.0.1, from Browser-to-server basics: Ports: which program in Build and Deploy Your First API.
What you need on your computer
- A computer where you can install programs: administrator rights on macOS, a Windows account that can approve an installation, or
sudoon Linux. - At least 4 GB of memory on a Mac, or 8 GB on Windows (Docker’s own minimums). About 2 GB of free disk space for Docker and the images in this module.
- An internet connection. The images in this module come from Docker Hub.
- A web browser, and
curl(Windows:curl.exein PowerShell, as in the API course).
Tested with Docker Desktop 4.81.0 (Docker Engine 29.6.1) on macOS, Apple silicon. The Windows and Linux steps are not tested: they follow Docker’s installation pages.
Docker Desktop or Docker Engine
Section titled “Docker Desktop or Docker Engine”Docker has two forms:
- Docker Engine is the program that runs containers, with the
dockercommand. It runs on Linux. It is free and open source. - Docker Desktop is an app for macOS, Windows and Linux. It contains Docker Engine, inside a small Linux virtual machine, with a window to manage it. On macOS and Windows, you need it, because containers need a Linux kernel.
This lesson installs Docker Desktop on macOS and Windows, and Docker Engine on Linux (Ubuntu).
The licence of Docker Desktop. Docker Desktop is free for personal use, education, non-commercial open-source projects, and small businesses. A small business has fewer than 250 employees and less than US$10 million in annual revenue. Larger companies need a paid subscription. If you use a work computer, ask your company which one applies. Docker Engine on Linux is free for everyone. (Source: Docker Desktop license agreement.)
Other tools can also run the same images, for example Podman Desktop, Rancher Desktop and Colima. This course tests only Docker.
Your computer’s processor architecture
Section titled “Your computer’s processor architecture”An image is built for one CPU architecture: the kind of processor that can run its programs. Two are common:
| Architecture | Linux name | Computers |
|---|---|---|
linux/arm64 |
aarch64 |
Macs with Apple silicon, many new Windows laptops, some cloud servers |
linux/amd64 |
x86_64 |
Most Windows and Linux PCs (Intel and AMD processors), most cloud servers |
Find yours:
uname -marm64 means Apple silicon. x86_64 means Intel.
$env:PROCESSOR_ARCHITECTUREAMD64 means an Intel or AMD processor. ARM64 means an Arm processor.
uname -mx86_64 means an Intel or AMD processor. aarch64 means Arm.
On Amira’s Mac, uname -m prints arm64.
Most official images are multi-platform: one name and tag, with one variant for each architecture. Docker downloads the variant for your computer. This command lists the variants (shortened):
docker buildx imagetools inspect python:3.14.8-slim-trixieName: docker.io/library/python:3.14.8-slim-trixieMediaType: application/vnd.oci.image.index.v1+jsonDigest: sha256:f85c5697265c178cc6887276c55fe16cf3d14ca35c3df6a5eab3b360534a55d2... Platform: linux/amd64... Platform: linux/arm/v5 Platform: linux/arm/v7 Platform: linux/arm64/v8 Platform: linux/386 Platform: linux/ppc64le Platform: linux/riscv64 Platform: linux/s390x(The output also has entries with Platform: unknown/unknown. They hold information about how the image was built, not programs.)
Docker Desktop can also run the other architecture, slowly, with emulation: software that imitates the other processor. --platform chooses the variant:
docker run --rm --platform linux/amd64 python:3.14.8-slim-trixie python -c "import platform; print(platform.machine())"x86_64The architecture matters again in Module 5. Azure Container Apps runs only linux/amd64 images, so on an Apple silicon Mac you build the API’s image for linux/amd64.
Install Docker
Section titled “Install Docker”Choose your system. Follow only one tab.
Docker Desktop supports the current version of macOS and the two versions before it.
- Find your processor with
uname -m, as in the section before. - Open Install Docker Desktop on Mac, and download the installer for your processor: Apple silicon or Intel chip.
- Open the downloaded
Docker.dmgfile. - Drag the Docker icon to the Applications folder.
- Open Docker from the Applications folder.
- Read the Docker Subscription Service Agreement, and accept it if the licence terms in Docker Desktop or Docker Engine allow your use.
- Wait until Docker Desktop says that the engine is running. You do not need to sign in to Docker Hub for this course. If Docker Desktop asks, skip the sign-in.
Docker Desktop must run when you use the docker command. After a restart of the Mac, open it again, or turn on its setting to start at sign-in.
Docker Desktop runs its Linux virtual machine with WSL 2, the Windows Subsystem for Linux. Docker’s page lists these requirements: 64-bit Windows 11 version 23H2 or later, or Windows 10 version 22H2 (build 19045). You also need 8 GB of memory, and hardware virtualization turned on in the computer’s BIOS or UEFI settings. Check the exact editions on Install Docker Desktop on Windows.
- Open PowerShell.
- Check the version of WSL with
wsl --version. You need version 2.1.5 or later. - If WSL is missing, install it with
wsl --install. If it is older than 2.1.5, update it withwsl --update. Restart the computer if Windows asks. - Open Install Docker Desktop on Windows, and download the installer for x86_64 (Intel and AMD processors). For an Arm laptop, Docker offers an Arm version, marked “Early Access”.
- Run
Docker Desktop Installer.exe. - On the Configuration page, select Use WSL 2 instead of Hyper-V.
- When the installation ends, select Close.
- Start Docker Desktop from the Start menu.
- Read the Docker Subscription Service Agreement, and accept it if the licence terms in Docker Desktop or Docker Engine allow your use.
- Wait until Docker Desktop says that the engine is running. Skip the sign-in if Docker Desktop asks.
Docker Desktop runs Linux containers by default. This course uses only Linux containers. Do not select “Switch to Windows containers”.
These steps install Docker Engine on Ubuntu 26.04, 24.04 or 22.04 from Docker’s own package repository. They follow Install Docker Engine on Ubuntu. For another Linux system, use its page on that site.
-
Remove the packages that conflict:
Terminal window sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)If none of them is installed,
aptsays so. That is normal. -
Add Docker’s signing key, so that
aptcan check the packages:Terminal window sudo apt updatesudo apt install ca-certificates curlsudo install -m 0755 -d /etc/apt/keyringssudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.ascsudo chmod a+r /etc/apt/keyrings/docker.asc -
Add Docker’s repository to the list of package sources:
Terminal window sudo tee /etc/apt/sources.list.d/docker.sources <<EOFTypes: debURIs: https://download.docker.com/linux/ubuntuSuites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")Components: stableArchitectures: $(dpkg --print-architecture)Signed-By: /etc/apt/keyrings/docker.ascEOFsudo apt update -
Install Docker Engine, the
dockercommand, and the Buildx and Compose plugins:Terminal window sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -
Check that the Docker service runs with
sudo systemctl status docker. On Ubuntu, it starts automatically. Press q to leave.
Now docker works with sudo. To use it without sudo, you can add your user to the docker group.
- If you accept the risk, add your user to the group with
sudo usermod -aG docker $USER. - Sign out and sign in again, so that the new group applies. (
newgrp dockerapplies it only in the current terminal.)
One more Linux fact for Module 3: Docker’s page warns about the ufw firewall. Ports that you publish with Docker bypass its rules.
Check the installation
Section titled “Check the installation”Run three checks, in this order. Each one tests one more part.
1. The docker command can reach the engine.
docker versionThe output (shortened):
Client: Version: 29.6.1 API version: 1.55 OS/Arch: darwin/arm64 Context: desktop-linux
Server: Docker Desktop 4.81.0 (232925) Engine: Version: 29.6.1 API version: 1.55 (minimum version 1.40) OS/Arch: linux/arm64...There are two parts. The Client is the docker command on your computer (darwin/arm64: macOS on Apple silicon). The Server is the engine. Its OS/Arch is linux/arm64, because it runs in Docker Desktop’s Linux virtual machine. If the Server part is missing, see the Aside after the third check.
2. The engine runs the system and the architecture that you expect.
docker info --format '{{.OperatingSystem}} {{.Architecture}}'Docker Desktop aarch64On Windows, the first words are also Docker Desktop. On Linux, you see the name of your Linux system instead.
3. The engine can pull an image from a registry and run a container.
docker run hello-worldThe output, on a computer that does not have the image yet:
Unable to find image 'hello-world:latest' locallylatest: Pulling from library/hello-world58dee6a49ef1: Pulling fs layer58dee6a49ef1: Download completec3bdf82c34d1: Download complete58dee6a49ef1: Pull completeDigest: sha256:5e23090353324d887c48ad5e5c56d294eab81588df9605b07d1afe895f9cc8f8Status: Downloaded newer image for hello-world:latest
Hello from Docker!This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (arm64v8) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal....The message itself lists what worked. The daemon is the engine: a program that runs in the background. hello-world:latest: you gave no tag, so Docker used the tag latest. (arm64v8) is the architecture of the variant that it pulled. On an Intel or AMD computer, it is amd64. The hello-world image is 22.6 kB, so this check is fast.
Worked example: a web server in a container
Section titled “Worked example: a web server in a container”Now a container that you can reach from the browser. nginx is a web server. Amira runs the official nginx image, with a pinned version, 1.30.5, the stable version when the course was written:
docker run -d --name c18m01-web -p 127.0.0.1:8080:80 nginx:1.30.5| Part | Meaning |
|---|---|
-d |
Run in the background. |
--name c18m01-web |
The container’s name. |
-p 127.0.0.1:8080:80 |
Publish a port. A port is a number that chooses which program on a computer gets a connection (as in the API course). Connections to port 8080 on this computer go to port 80 in the container, where nginx listens. 127.0.0.1 means that only this computer can connect. Module 3 explains every part. |
nginx:1.30.5 |
The image and an exact version. |
The output:
Unable to find image 'nginx:1.30.5' locally1.30.5: Pulling from library/nginxa36cd258528a: Pulling fs layer...Digest: sha256:9bf97bd7714f5e24c1ccd545ecb9eb5435cb6d109c97cebb15e7e455e0239edbStatus: Downloaded newer image for nginx:1.30.5de15a5783a027b866ca221ac078b172bb11af74007f11fe8f6f459d2e4b06c3bdocker ps shows the published port in the PORTS column:
docker ps --filter name=c18m01-webCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESde15a5783a02 nginx:1.30.5 "/docker-entrypoint.…" Less than a second ago Up Less than a second 127.0.0.1:8080->80/tcp c18m01-webOpen http://127.0.0.1:8080 in the browser. The page has the title Welcome to nginx! and a few lines of text. From the terminal, curl shows the start of the same page:
curl -s http://127.0.0.1:8080 | head -4curl.exe -s http://127.0.0.1:8080 | Select-Object -First 4curl -s http://127.0.0.1:8080 | head -4<!DOCTYPE html><html><head><title>Welcome to nginx!</title>nginx writes one log line for each request. docker logs shows them (the last lines):
docker logs c18m01-web...2026/10/07 16:52:59 [notice] 1#1: start worker process 372026/10/07 16:52:59 [notice] 1#1: start worker process 38172.17.0.1 - - [07/Oct/2026:16:52:59 +0000] "GET / HTTP/1.1" 200 896 "-" "curl/8.7.1" "-"The request came from an address like 172.17.0.1, not from 127.0.0.1. Docker forwarded the connection to the container through its own small network, and that address is Docker’s side of it. Module 3 explains it.
Then Amira looks inside the container. She asks nginx for its version, and changes the start page in the container:
docker exec c18m01-web nginx -vdocker exec c18m01-web sh -c 'echo "<h1>Changed in the container</h1>" > /usr/share/nginx/html/index.html'curl -s http://127.0.0.1:8080nginx version: nginx/1.30.5<h1>Changed in the container</h1>docker diff c18m01-web lists the changed page, C /usr/share/nginx/html/index.html, together with files that nginx itself made when it started, such as A /run/nginx.pid. docker inspect shows the container’s state and configuration:
docker inspect c18m01-web --format '{{.State.Status}} {{.Config.Image}} {{json .NetworkSettings.Ports}}'running nginx:1.30.5 {"80/tcp":[{"HostIp":"127.0.0.1","HostPort":"8080"}]}Predict: Amira removes the container and starts a new one with the same command. Which page does the browser show?
docker stop c18m01-webdocker rm c18m01-webdocker run -d --name c18m01-web -p 127.0.0.1:8080:80 nginx:1.30.5curl -s http://127.0.0.1:8080 | grep titlec18m01-webc18m01-web862f869879fcebb37848ce3029080ce6c73f35cfa4ef893db53f85426a2ba799<title>Welcome to nginx!</title>The original page is back. The changed page was in the writable layer of the old container. nginx stopped at once (0.3 s here), because nginx reacts to the stop request. At the end, she removes the container:
docker rm -f c18m01-webReset: get back to a clean state
Section titled “Reset: get back to a clean state”You can always return to the state before this module. Do these steps at the end of the module, or when something is confusing:
- List all your containers, also the stopped ones:
docker ps -a. Look for the names that start withc18m01-, and for thehello-worldcontainer. A container that you started without--namehas a random name, such aslaughing_pare. - Remove each container that you made, by name:
docker rm -f c18m01-web. For thehello-worldcontainer, use its random name:docker rm laughing_pare. - Remove the images that you do not need any more:
docker image rm nginx:1.30.5 hello-world. Keeppython:3.14.8-slim-trixie: Module 2 uses it. - Check the space that Docker uses:
docker system df.
On a test computer with only the Python image left, docker system df printed:
TYPE TOTAL ACTIVE SIZE RECLAIMABLEImages 1 0 205.8MB 205.8MB (99%)Containers 0 0 0B 0BLocal Volumes 0 0 0B 0BBuild Cache 0 0 0B 0BYour numbers are larger if you have other images. Remove only images and containers that you know. Do not use docker system prune: it deletes things from other projects too.
Common mistake
Port is already allocated
Symptom: Amira starts a second web container on the same port. docker run fails:
docker run -d --name c18m01-web2 -p 127.0.0.1:8080:80 nginx:1.30.5ec0cb79f15b8a3b44612760dfb5ef515368fef837982ed3ff449e113a6e496b0docker: Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint c18m01-web2 (28a55085448d341d9cc02dcf57bb2836ec659d84d0e1b8db9b43e377685a49f3): Bind for 127.0.0.1:8080 failed: port is already allocated
Run 'docker run --help' for more informationThe exit code is 125: Docker itself failed, not the program in the container. Then docker ps -a shows the new container with the status Created: it exists, but it never started.
Cause: only one program can use a port on one address. c18m01-web already has 127.0.0.1:8080. The container port, 80, is not the problem: every container has its own port 80.
Fix:
- Find what uses the port:
docker psshows127.0.0.1:8080->80/tcpin thePORTScolumn ofc18m01-web. If no container uses it, another program on your computer does. - Remove the container that was only created:
docker rm c18m01-web2. - Either stop the first container, or use another host port:
docker run -d --name c18m01-web2 -p 127.0.0.1:8081:80 nginx:1.30.5. - Remove both containers when you are done:
docker rm -f c18m01-web c18m01-web2.
For a real AI service: two copies of a service on one computer need two host ports, but they can use the same container port. In the cloud, the platform chooses the host side for you (Module 5).
Check your understanding
docker version shows a Client part but no Server part. What does that tell you, and what do you do?
Show the answer
The docker command is installed, but it cannot reach the engine. The client and the engine are two programs, and only the client answered. On macOS and Windows, start Docker Desktop and wait until the engine runs. On Linux, start the service with sudo systemctl start docker, and check that you use sudo or are in the docker group. Then run docker version again: both parts must appear.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Check your understanding
Amira's Mac is arm64. A cloud server is amd64. Why does docker run python:3.14.8-slim-trixie work on both, and what must she remember for her own image later?
Show the answer
The Python image is multi-platform: one name and tag, with a variant for linux/arm64, one for linux/amd64, and others. Each computer pulls the variant for its own processor. An image that she builds on her Mac is, by default, only for linux/arm64. A server that needs linux/amd64, such as Azure Container Apps, cannot run it, so in Module 5 she builds it for linux/amd64.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Module practice
Section titled “Module practice”Guided: the worked example above is the guided part. You ran nginx, published its port, read its page and its logs, looked inside with exec, diff and inspect, and replaced the container. Do it again yourself if you only read it.
Your task: run and inspect a small container of your own, then stop and remove it. Use the Python image as a small web server, which lists the files of a folder:
- Start
python -m http.server 8000in a container namedc18m01-files, in the background, with port 8000 of the container published on127.0.0.1:8081. - Open
http://127.0.0.1:8081in the browser, and find the request in the container’s logs. - Write a file
/practice.txtinto the container, and get it with the browser orcurlathttp://127.0.0.1:8081/practice.txt. - Show which files changed in the container, its main process, and its image and command with
docker inspect. - Stop the container, and look at its status.
- Remove it, and check that it is gone.
Show a model answer
docker run -d --name c18m01-files -p 127.0.0.1:8081:8000 python:3.14.8-slim-trixie python -m http.server 8000curl -s http://127.0.0.1:8081/ | grep titledocker logs c18m01-files8d637cfef539aca7ea7eda064f85ce136529f1cfe31e632d9e5ab4d3024fe0ba<title>Directory listing for /</title>172.17.0.1 - - [07/Oct/2026 17:01:06] "GET / HTTP/1.1" 200 -The page lists the folders of the container’s file system (bin/, etc/, tmp/ and others), because the server started in /.
docker exec c18m01-files sh -c 'echo "practice" > /practice.txt'curl -s http://127.0.0.1:8081/practice.txtdocker diff c18m01-filesdocker top c18m01-filesdocker inspect c18m01-files --format 'Image={{.Config.Image}} Cmd={{.Config.Cmd}} Status={{.State.Status}}'practiceC /usrC /usr/localC /usr/local/libC /usr/local/lib/python3.14...A /usr/local/lib/python3.14/http/__pycache__/server.cpython-314.pyc...A /practice.txtUID PID PPID C STIME TTY TIME CMDroot 17450 17427 15 17:01 ? 00:00:00 python -m http.server 8000Image=python:3.14.8-slim-trixie Cmd=[python -m http.server 8000] Status=runningdocker diff shows /practice.txt, and also many __pycache__ files. Python saved compiled copies of the modules that it imported, in the writable layer. Module 2 turns this off for the ticket API with PYTHONDONTWRITEBYTECODE=1.
docker stop c18m01-filesdocker ps -a --filter name=c18m01-filesdocker rm c18m01-filesdocker ps -a --filter name=c18m01-filesc18m01-filesCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES8d637cfef539 python:3.14.8-slim-trixie "python -m http.serv…" 14 seconds ago Exited (137) 1 second ago c18m01-filesc18m01-filesCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESdocker stop took about 11 seconds and ended with Exited (137): like sleep, this Python server does not react to the stop request, so Docker forced it after 10 seconds. The last list is empty: the container is gone, with its writable layer and /practice.txt. The image is still there.
Module check
Section titled “Module check”Check your understanding
Retrieval: which command lists stopped containers too, and which command removes a stopped container?
Show the answer
docker ps -a lists every container, also the stopped ones (docker ps lists only the running ones). docker rm <name> removes a stopped container. For a running container, use docker rm -f <name>, or docker stop first.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Check your understanding
Predict: Sam changes the nginx start page with docker exec in c18m01-web, then runs docker stop c18m01-web and docker start c18m01-web. Which page does the browser show? And after docker rm -f and a new docker run?
Show the answer
After stop and start: the changed page. The container still exists, so its writable layer, with the changed index.html, is still there. After docker rm -f and a new docker run: Welcome to nginx! again. docker rm deleted the old writable layer, and the new container sees only the image’s read-only layers, which never changed.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Check your understanding
Completion check: explain which state belongs to the image and which belongs to the running container. Use the module practice as the example.
Show the answer
The image holds everything that is the same for every container. That is the read-only layers with the files: Debian 13, Python 3.14.8 and its standard library. It is also the metadata: the start command python3 and the ENV values. It does not change when a container runs. docker history and docker image inspect show it.
The container holds what belongs to one running copy:
- its process,
python -m http.server 8000(docker top); - its writable layer with every change:
/practice.txtand the__pycache__files (docker diff); - its name, its network address and its published port,
127.0.0.1:8081; - its logs (
docker logs) and its state:running, thenExited (137).
You choose the command and the port with docker run, and docker inspect shows them. All of this goes away with docker rm. So data that must survive cannot stay in a container’s writable layer: Module 3 shows where it goes instead.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Recap
In this module, you:
- saw why a project works on one computer and fails on another. The environment is more than the packages, and a lock file fixes only the packages;
- learned that an image is read-only layers plus metadata. A container is a process with a writable layer, on a shared Linux kernel. A virtual machine has its own kernel;
- installed Docker, checked it with
docker version,docker infoandhello-world, and found your CPU architecture; - ran, inspected, changed, stopped and removed containers, and proved that
docker rmdeletes the writable layer.
Reset procedure: list your containers with docker ps -a, and remove every c18m01- container and the hello-world container with docker rm -f <name>. Remove nginx:1.30.5 and hello-world with docker image rm. Keep python:3.14.8-slim-trixie. Check with docker system df. The ticket-api project did not change in this module.
Next, you write a Dockerfile for the ticket API, and build its first image.
Next: Dockerfile essentials