100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Docker & Containers
50 minbeginner

Foundations Practice — Container Basics

What You'll Build

You will put Module 1 into practice end to end: pull images from a registry, run a real web server container with a published port, manage its lifecycle (stop, start, restart with a policy), and use the CLI trio (inspect, logs, exec) to observe and debug it — then clean up. By the end you will have run, managed, inspected, and removed containers fluently, the foundational hands-on Docker skills.

This exercise is deliberately practical and uses only the official commands from this module. You will see the client-daemon-registry flow in action, the difference between detached and interactive runs, the container lifecycle, restart policies for resilience, and how to look inside a running container — consolidating the foundations before you start building your own images.

Analogy🏏Cricket
🏏 Think of it like cricket: Just as a player's first practice session puts the fundamental skills together in sequence — take guard, play some shots, run between wickets, review — rather than in isolation, this exercise puts the Docker fundamentals together in sequence: pull, run, manage, inspect, clean up. The insight is that fluency comes from rehearsing the basics as a connected flow: the practice session links the skills into real play, exactly as this exercise links the Docker commands into a real workflow.

Prerequisites

  • Completion of lessons 01–05, or equivalent familiarity with images, running containers, the lifecycle, and the inspect/logs/exec commands.
  • Docker installed and verified (docker run hello-world works).
  • A terminal and a web browser to reach a published port.
  • Basic command-line comfort.
  • Internet access to pull images from Docker Hub.

Setup & Project Structure

You will work entirely from the command line using official images from Docker Hub — nginx (a web server) and alpine (a tiny Linux image) — so there is nothing to install beyond Docker itself. Verify Docker is working, then you will pull images, run containers, and manage them through their lifecycle.

Analogy🏏Cricket
🏏 Think of it like cricket: starting this practice with official nginx and alpine images is like turning up to nets with the ground's standard-issue kit already laid out — you need install nothing of your own. Just as a coach first confirms the nets are booked and the bowling machine is switched on before a session begins, you verify Docker is working before anything else. Just as a player draws the standard bat and pads from the club store rather than crafting gear from scratch, you pull ready-made images from Docker Hub — nginx as your web-server 'all-rounder', alpine as a tiny, nimble twelfth man. Then, just as a session moves methodically from knocking-in to full-pace deliveries to fitness cool-down, you will pull images, run containers, and manage them through their full lifecycle. The payoff: a friction-free, command-line-only foundation session where every fundamental container move gets rehearsed cleanly.

The flow: pull and run nginx as a published background service, observe and debug it with the CLI trio, apply a restart policy for resilience, then run a throwaway interactive container, and finally clean everything up. Each step uses only the commands from this module, reinforcing the foundations.

bash
# Verify Docker and pull the images you'll use
docker version                  # client + daemon talking?
docker run hello-world          # end-to-end check

docker pull nginx:1.25          # pinned web server image (reproducible)
docker pull alpine:3.20         # tiny Linux image for quick experiments
docker images                   # confirm both images are present locally

Step 1 — Run and Publish a Web Server

Run nginx as a detached, named, published container so you can reach it from your browser: detached (-d) to run in the background, published (-p 8080:80) so host port 8080 reaches nginx's port 80, and named (--name web) for easy reference. Visit http://localhost:8080 to confirm the default nginx page loads — proof the container is running and reachable.

This single command exercises the whole foundation: the daemon runs a container from the pinned image, and the port mapping bridges your host to the service inside. Confirm it is running with docker ps, and note that without the -p mapping the service would run but be unreachable.

Analogy🏏Cricket
🏏 Think of it like cricket: Just as starting a match means both fielding the team and opening the gates so spectators can reach the action, running the service means both starting the container and publishing the port so traffic can reach it. The insight is that the service is only useful once the way in is open: players on the field plus open gates, exactly as a running container plus a published port make the application actually reachable.
bash
# Run nginx: detached, published on host 8080 -> container 80, named 'web'
docker run -d -p 8080:80 --name web nginx:1.25

docker ps                        # confirm 'web' is running (status Up, ports 8080->80)
# Open http://localhost:8080 in your browser -> the nginx welcome page

# (If unreachable: check the -p order is HOST:CONTAINER and the port is free)

Step 2 — Observe and Debug with the CLI Trio

Use the three essential commands on the running container. docker logs web shows nginx's output (and docker logs -f web follows it live — refresh the browser and watch requests appear). docker inspect web reveals its configuration; extract its IP or port mapping with --format. docker exec -it web sh drops you into a shell inside the container to look around.

Inside the exec shell, explore: list nginx's config (ls /etc/nginx), check the served files (ls /usr/share/nginx/html), and confirm you are inside the container's isolated environment. Exit the shell. This step makes the container transparent — you have seen its output, its configuration, and its internals live.

Analogy🏏Cricket
🏏 Think of it like cricket: Just as you understand a match through the commentary, the official records, and walking onto the field to look directly, you understand the container through logs, inspect, and exec. The insight is that combining the narrated output, the recorded configuration, and direct live inspection gives a complete picture: the three views together leave nothing opaque, exactly as logs, inspect, and exec together make the container fully understandable.
bash
docker logs web                  # nginx output so far
docker logs -f web               # follow live; refresh the browser to see requests (Ctrl-C to stop)

docker inspect -f '{{.NetworkSettings.IPAddress}}' web   # container IP
docker inspect -f '{{.State.Status}}' web                # running?

docker exec -it web sh           # shell INSIDE the running container
#   inside:  ls /etc/nginx           (config)
#            ls /usr/share/nginx/html (served files)
#            exit                     (leave the shell)

Step 3 — Lifecycle and Restart Policy

Manage the container's lifecycle: stop it (docker stop web) and confirm it shows as Exited in docker ps -a, then start it again (docker start web) and confirm it is back. Then replace it with a resilient version using a restart policy: remove the old one and run a new one with --restart unless-stopped, so it would auto-recover from crashes and reboots.

Demonstrate the policy's intent: a container with --restart unless-stopped auto-restarts on failure but respects a deliberate stop. Also run a quick throwaway interactive container (docker run -it --rm alpine sh) to contrast the ephemeral pattern with the long-lived service — and notice it cleans itself up on exit thanks to --rm.

Analogy🏏Cricket
🏏 Think of it like cricket: Just as a captain benches and recalls players and sets substitution rules for resilience — automatically replacing an injured player but respecting a planned rest, you stop and start containers and set restart policies that recover from crashes but respect deliberate stops. The insight is that lifecycle control plus smart automatic-recovery rules keep the system running: bench, recall, and substitution rules, exactly as stop, start, and restart policies manage and protect your containers.
bash
# Lifecycle: stop, see it persisted, start again
docker stop web && docker ps -a   # 'web' now Exited (still listed)
docker start web && docker ps     # back to Up

# Replace with a resilient version (auto-restart unless deliberately stopped)
docker rm -f web
docker run -d -p 8080:80 --restart unless-stopped --name web nginx:1.25

# Contrast: a throwaway interactive container that cleans itself up
docker run -it --rm alpine:3.20 sh
#   inside:  echo "ephemeral!"; exit   -> container is auto-removed (--rm)

Step 4 — Testing & Verification

Confirm the full workflow: nginx is reachable at http://localhost:8080, you observed it via logs/inspect/exec, you stopped and started it, replaced it with a restart-policy version, and ran a self-cleaning interactive container. Finally, clean up completely — stop and remove the web container and prune any leftover stopped containers — and verify with docker ps -a that nothing remains.

Analogy🏏Cricket
🏏 Think of it like cricket: this verification step is the post-match review that confirms every part of the game went to plan. Just as a captain checks the scorecard to confirm the total was reached, the wickets fell as expected, and every fielder did his job, you confirm nginx is reachable at localhost:8080, that you observed it via logs, inspect and exec, and that you stopped and restarted it cleanly. Just as a rolling substitution is checked to have swapped a fresh player in without stopping play, you verify the restart-policy version took over and the self-cleaning interactive container left no trace. And just as a diligent groundsman clears the pitch and confirms nothing is left behind before locking up, you stop and remove the web container, prune leftover stopped ones, and check with `docker ps -a` that the field is truly empty. The payoff: proof the full lifecycle worked and the host is left spotless.
bash
# Final verification + cleanup
docker ps                         # 'web' running with restart policy
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080   # expect 200

# Clean up everything from this exercise
docker rm -f web                  # stop + remove the service
docker container prune -f         # remove any leftover stopped containers
docker ps -a                      # verify: no exercise containers remain

# (Optionally remove the pulled images too)
# docker rmi nginx:1.25 alpine:3.20

Warning: Remember that anything you changed inside the nginx container via docker exec (editing a config, creating a file) lives only in that container's writable layer and is lost the moment you docker rm it — as you do in cleanup. That is expected here, but it underscores the rule: never rely on a container's own filesystem for changes you need to keep. Permanent changes belong in an image (next module); persistent data belongs in a volume (covered later).

Extension Challenge: Run a second nginx container on a different host port (e.g. -p 8081:80 --name web2) from the same image, and confirm both run independently from shared image layers. Then deliberately give a container a bad command so it exits with a non-zero code, find that exit code with docker ps -a, read docker logs to see why, and observe how --restart on-failure reacts. Finally, compare docker stats across your running containers to see their resource use.

  • Pull pinned images (nginx:1.25, alpine:3.20) and run a detached, published, named web server: docker run -d -p 8080:80 --name web.
  • Reach a published service in the browser; without -p the service runs but is unreachable (mind HOST:CONTAINER order).
  • Debug with the trio: docker logs (output, -f to follow), docker inspect (config/state, --format to extract), docker exec -it (shell inside).
  • Manage the lifecycle: stop/start (stopped containers persist), and use --restart unless-stopped for a resilient service.
  • Use docker run -it --rm for throwaway interactive containers that clean themselves up on exit.
  • Clean up with docker rm -f and docker container prune; changes inside a container's writable layer are lost on removal.
Lesson 6 of 35
0% complete