Docker Basics and Dockerfiles
Lab decks 1 and 2 plus the Docker cheat sheet: installing Docker, image and container commands, the container lifecycle, Dockerfile instructions, port mapping, and ENTRYPOINT versus CMD.
Why Docker in a blockchain courseSearch on YouTube
The syllabus lists Docker under "Base technologies" in the Introduction unit. Blockchain nodes, wallets and dev tools are usually shipped as containers. The lab uses Docker to run tools without installing them on the host.
Docker packages and runs an application in a loosely isolated environment called a container. The isolation lets you run many containers at once on one host. A container holds everything the app needs, so it does not depend on what the host has installed. Everyone who runs the same container gets the same behaviour.
| Term | Meaning |
|---|---|
| Image | A lightweight, standalone, executable package: code, runtime, system tools, libraries and settings. Read-only template. |
| Container | A runtime instance of an image. It runs the same on any infrastructure. |
| Registry | A store of images. Docker Hub (hub.docker.com) is the default. |
| Dockerfile | A text file of instructions that builds an image. |
An image is a class; a container is an object made from it. You can create many containers from one image.
Installing DockerSearch on YouTube
Install Docker Desktop from the Docker docs ("Install Docker Desktop on Mac" or "on Windows"). On Windows it runs on WSL 2. Check both after install:
docker --version # e.g. Docker version 29.6.2, build dfc4efb
wsl --version # Windows only: WSL version, kernel version, Windows versionImage commandsSearch on YouTube
docker pullSearch on YouTube
Syntax: docker pull [OPTIONS] NAME[:TAG|@DIGEST]. It downloads an image from a registry (usually Docker Hub) to the local machine.
docker pull alpine
docker pull ubuntu:24.04
docker pull nginx:latest- It downloads an image, not a container.
- If the image already exists locally, only missing or updated layers are downloaded.
- The default tag is
latestif no tag is given (output: "Using default tag: latest"). - Images are stored in Docker's local image cache.
- The output shows the layer IDs, a
Digest: sha256:...and the full namedocker.io/library/alpine:latest.
Best practice: pin a version tag (for example ubuntu:24.04) instead of relying on latest. Pull only from trusted registries.
docker imagesSearch on YouTube
Syntax: docker images [OPTIONS] [REPOSITORY[:TAG]]. Both forms list local images:
docker images
docker image lsColumns: IMAGE, ID, DISK USAGE, CONTENT SIZE, EXTRA. For example alpine:latest is about 8.42 MB.
Container lifecycleSearch on YouTube
Listing containersSearch on YouTube
docker ps # running containers only
docker ps -a # all containers (running + stopped); same as --all
docker ps -q # container IDs only
docker ps -n 5 # last 5 created containersColumns: CONTAINER ID, IMAGE, COMMAND, CREATED, STATUS, PORTS, NAMES.
Creating a containerSearch on YouTube
docker create makes a container from an image but does not start it. Its STATUS is Created.
docker create --name cont1 alpine
docker create --name myalpine alpine
docker create --name web nginx
docker create --name test ubuntu sleep infinityStarting a containerSearch on YouTube
docker start cont1
docker ps -a # STATUS: Exited (0) 10 seconds agocont1 starts and exits at once. Its default command is /bin/sh, which has no terminal attached, so it ends with exit code 0. A later docker exec cont1 pwd fails with "container ... is not running".
A container lives only as long as its main process (PID 1) is alive. When PID 1 exits, the container stops.
Keeping a container aliveSearch on YouTube
Give it a main process that never ends:
docker run --name cont3 -d alpine sleep infinity
docker ps -a # cont3: COMMAND "sleep infinity", STATUS Up 14 secondsdocker run = create + start in one step. -d runs it detached (in the background).
Running commands inside: docker execSearch on YouTube
docker exec cont3 pwd # prints /
docker exec cont3 ls # bin dev etc home lib media mnt opt proc root ...
docker exec -t cont3 ls # -t allocates a TTY, so output is in columns and coloured
docker exec -it cont3 /bin/sh # interactive shell inside the container| Flag | Meaning |
|---|---|
-i | Interactive: keep STDIN open |
-t | Allocate a pseudo-terminal (TTY) |
-it | Both: you get a usable shell prompt (/ #) |
Inside the Alpine shell, /bin holds BusyBox applets (ls, ping, sh, wget and so on).
Stopping and removingSearch on YouTube
docker stop cont3 # graceful stop; status becomes Exited
docker rm cont3 # fails if running: "stop the container before removing or force remove"
docker rm -f cont3 # force remove a running container
docker rm e67c # a unique prefix of the container ID works
docker rm cont1Removing imagesSearch on YouTube
docker rmi alpine ubuntu # untags and deletes each layer (sha256 lines)
docker images # list is now empty
docker image prune # remove all unused imagesPull, Create, Start, Exec, Stop, Remove: "Please Check Some Extra Stuff Regularly". run = create + start.
Dockerfile basicsSearch on YouTube
A Dockerfile lists the steps that build an image. Each instruction makes a layer.
FROM ubuntu
RUN apt-get update && apt-get install -y
CMD ["echo", "Welcome to Ubuntu Docker container!"]docker build -t mycont1 .-t tags (names) the image. The final . is the build context: the folder whose files are sent to the builder. By default Docker looks for a file named Dockerfile in that folder.
| Instruction | Purpose |
|---|---|
FROM | Base image to start from |
RUN | Run a command at build time; result is saved as a layer |
CMD | Default command (or default arguments) when the container starts |
COPY | Copy files from the build context into the image |
WORKDIR | Set the working directory for later instructions |
ENV | Set environment variables |
LABEL | Add metadata |
EXPOSE | Document the port the app listens on |
ENTRYPOINT | Fixed executable the container runs |
Custom Dockerfile namesSearch on YouTube
Use -f to build from a file with another name, such as customalpine.txt:
FROM alpine
RUN apk update
RUN apk add --no-cache bash
CMD ["echo", "welcome to Custom Alpine"]docker build -f customalpine.txt -t mycont2 .apk is Alpine's package manager. --no-cache skips storing the package index, which keeps the image small.
docker historySearch on YouTube
docker history <image> shows the layers of an image, newest first, with the instruction that created each one and its size.
docker history ubuntu
docker history mycont1
docker history alpine
docker history mycont2For mycont2, the top three layers are the ones the custom file added (CMD 0 B, apk add 2.15 MB, apk update 3.05 MB). Below them sit the base Alpine layers (ADD alpine-minirootfs ... 8.42MB). Metadata instructions like CMD add 0 B layers.
Containerising a simple appSearch on YouTube
The lab builds a single-page HTML site served by nginx.
# Dockerfile2
FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY index.html .
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]docker build -f Dockerfile2 -t mysite .
docker run --name mysite mysitedaemon off; keeps nginx in the foreground, so nginx stays PID 1 and the container keeps running.
Port mappingSearch on YouTube
The container runs and docker ps shows PORTS 80/tcp. The host browser still shows "localhost refused to connect".
EXPOSE is for information only. It does not map a host port to the container port. You must map ports yourself with -p.
docker run -d -p 8080:80 --name mysite1 mysite-p <host_port>:<container_port>. Now docker ps shows 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp, and http://localhost:8080 loads the site.
More Dockerfile instructionsSearch on YouTube
WORKDIRSearch on YouTube
Sets the working directory inside the image for the following instructions. It creates the folder if missing.
FROM ubuntu:24.04
WORKDIR /app
COPY . .
RUN lsCOPY . . copies the build context into /app.
ENVSearch on YouTube
Sets environment variables inside the image and container. Syntax ENV <KEY>=<VALUE>.
FROM ubuntu:24.04
ENV APP_ENV=production
ENV PORT=8080
WORKDIR /appENV suits configuration, not secrets. Database passwords, API keys and tokens stay visible in the image (for example in docker history and docker inspect).
LABELSearch on YouTube
Adds metadata to an image to identify, organise and manage it. Syntax LABEL key="value".
LABEL app="payment-service" \
version="2.1.0" \
team="backend" \
environment="production"ENTRYPOINTSearch on YouTube
Defines the main executable the container is designed to run. Syntax ENTRYPOINT ["executable", "argument"].
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y nmap
ENTRYPOINT ["nmap"]Arguments given to docker run are appended to the entrypoint:
| You type | Container runs |
|---|---|
docker run nmap-container 192.168.1.10 | nmap 192.168.1.10 |
docker run nmap-container -sV 192.168.1.10 | nmap -sV 192.168.1.10 |
ENTRYPOINT fixes the executable. The user supplies the arguments at run time.
ENTRYPOINT vs CMDSearch on YouTube
Use them together: ENTRYPOINT is the fixed program, CMD gives default arguments that the user can override.
FROM python:3.12
WORKDIR /app
COPY . .
ENTRYPOINT ["python"]
CMD ["app.py"]| You type | Container runs |
|---|---|
docker run myapp | python app.py (default) |
docker run myapp test.py | python test.py (CMD overridden, for debugging) |
| ENTRYPOINT | CMD | |
|---|---|---|
| Role | Fixed executable | Default command or default args |
| Run-time args | Appended to it | Replace it |
| Override | Only with --entrypoint | Any args after the image name |
Exec form vs shell formSearch on YouTube
ENTRYPOINT python app.py # shell form
CMD python app.py # shell form
ENTRYPOINT ["python", "app.py"] # exec form (JSON array)Shell form makes Docker run /bin/sh -c "python app.py". The process tree is:
PID 1
└── /bin/sh -c python app.py
└── python app.pyYour Python app is not PID 1. The shell is. Signals such as SIGTERM from docker stop go to the shell, not to your app. Exec form runs the app directly as PID 1:
PID 1
└── python app.pyPrefer the exec (JSON array) form for ENTRYPOINT and CMD. The app becomes PID 1 and receives stop signals.
Docker CLI cheat sheetSearch on YouTube
| Task | Command |
|---|---|
| Start the daemon | docker -d |
Help (also --help on any subcommand) | docker --help |
| System-wide info | docker info |
| Build image | docker build -t <image_name> . |
| Build without cache | docker build -t <image_name> . --no-cache |
| List images | docker images |
| Delete image | docker rmi <image_name> |
| Remove unused images | docker image prune |
| Log in to Docker Hub | docker login -u <username> |
| Push image | docker push <username>/<image_name> |
| Search Hub | docker search <image_name> |
| Pull image | docker pull <image_name> |
| Run with a name | docker run --name <container_name> <image_name> |
| Run and publish port | docker run -p <host_port>:<container_port> <image_name> |
| Run in background | docker run -d <image_name> |
| Start or stop | docker start or docker stop <container_name> (or ID) |
| Remove stopped container | docker rm <container_name> |
| Shell in running container | docker exec -it <container_name> sh |
| Follow logs | docker logs -f <container_name> |
| Inspect container | docker inspect <container_name> (or ID) |
| Running containers | docker ps |
| All containers | docker ps --all |
| Resource usage | docker container stats |
Useful links from the sheet: docs.docker.com/desktop (install), github.com/docker/awesome-compose (example projects), docs.docker.com (docs).
What to do and check in the lab:
- Run
docker --version(andwsl --versionon Windows). - Pull
alpine, list it withdocker images. docker create --name cont1 alpine, start it, and see it exit withExited (0). Explain why with PID 1.docker run --name cont3 -d alpine sleep infinity; confirmUpindocker ps -a; rundocker exec -it cont3 /bin/sh.- Stop, remove (try
rmon a running container, thenrm -f), andrmithe images. - Write a Dockerfile, build it with
-t, then a second one with-f customalpine.txt. Compare layers withdocker history. - Build the nginx site image. Show that
EXPOSEalone gives "refused to connect" and-p 8080:80fixes it.
Quick self-testSearch on YouTube
What is the difference between a Docker image and a container?
An image is a read-only package with code, runtime, libraries and settings. A container is a running instance of an image. Many containers can be made from one image.
Which tag does docker pull nginx use, and why should you avoid relying on it?
latest. It can change over time, so builds are not reproducible. Pin a version such as ubuntu:24.04.
What is the difference between docker create, docker start and docker run?
create makes a container but does not start it (status Created). start starts an existing container. run does both in one step.
Viva: you start an Alpine container and it immediately shows Exited (0). Why? How do you keep it running?
A container lives only as long as its PID 1. Alpine's default /bin/sh has no terminal and exits at once. Give it a long-running main process, such as docker run -d alpine sleep infinity.
What do the -i and -t flags of docker exec do?
-i keeps STDIN open (interactive). -t allocates a pseudo-terminal. Together (-it) they give a usable shell, as in docker exec -it cont3 /bin/sh.
Viva: your nginx Dockerfile has EXPOSE 80 but the browser cannot reach localhost. Why?
EXPOSE only documents the port. It does not publish it. Run with -p 8080:80 to map host port 8080 to container port 80.
How do you build an image from a file not named Dockerfile?
Use -f: docker build -f customalpine.txt -t mycont2 .. The trailing . is the build context.
What does docker history show?
The layers of an image, newest first, with the instruction that created each layer and its size. Metadata instructions such as CMD show 0 B.
Viva: difference between ENTRYPOINT and CMD?
ENTRYPOINT sets the fixed executable; run-time arguments are appended to it. CMD sets default arguments or a default command; run-time arguments replace it. With ENTRYPOINT ["python"] and CMD ["app.py"], docker run myapp test.py runs python test.py.
Why prefer ENTRYPOINT ["python", "app.py"] over ENTRYPOINT python app.py?
Shell form runs /bin/sh -c "python app.py", so the shell is PID 1 and the app is a child that may not get stop signals. Exec form makes the app PID 1.
Why should secrets not go in ENV?
ENV values are stored in the image metadata. Anyone with the image can read them, for example with docker inspect or docker history.
- An image is a read-only package; a container is its running instance; Docker Hub is the default registry.
- Lifecycle:
pull,create,start,exec,stop,rm,rmi;run= create + start,-d= background. - A container lives only while its PID 1 runs;
sleep infinitykeeps a test container alive. - Dockerfile:
FROM,RUN,COPY,WORKDIR,ENV,LABEL,EXPOSE,ENTRYPOINT,CMD; build withdocker build -t name ., use-ffor other file names. docker historyshows the layers each instruction made.EXPOSEdocuments a port;-p host:containerpublishes it.- ENTRYPOINT is the fixed executable; CMD gives overridable defaults. Use exec form so the app is PID 1.
- Do not put secrets in
ENV; pin image tags; pull only from trusted registries.