Skip to content

Latest commit

 

History

History
969 lines (679 loc) · 32.6 KB

Tutorial.md

File metadata and controls

969 lines (679 loc) · 32.6 KB

Developing with Docker - Tutorial

Overview

This tutorial will teach you how to develop and deploy applications using Docker. You'll build a Docker Quiz Challenge - an interactive web application that tests your Docker knowledge with randomly selected questions and tracks scores in a MongoDB database.

What You'll Build

A full-stack quiz application consisting of:

  • Frontend: Interactive quiz interface with 5 randomly selected Docker MCQ questions from a pool of 10
  • Backend: Node.js Express server handling quiz submissions and scoring
  • Database: MongoDB storing player scores and leaderboard
  • Admin UI: Mongo Express for viewing database records in real-time

Learning Objectives

By the end of this tutorial, you'll understand:

  • How to run containers with docker run
  • How to create custom Docker networks
  • How to link containers together
  • How to use Docker Compose to orchestrate multi-container applications

Repository Structure

Let's explore the files in this repository:

developing-with-docker/
├── app/
│   ├── server.js          # Node.js Express backend server
│   ├── package.json       # Node.js dependencies and scripts
│   ├── index.html         # Quiz application frontend
│   ├── questions.json     # Docker MCQ questions
│   └── images/            # Application assets
├── Dockerfile             # Instructions to build the app container image
├── docker-compose.yaml    # Configuration for multi-container setup
└── README.md              # This tutorial file

Part 1: Getting Started with Docker

In this section, you'll learn Docker basics by manually running containers and connecting them together. We'll introduce Docker concepts and commands as we use them hands-on.

Here is a diagram of the architecture we'll set up:

Part 1 Architecture Diagram

Understanding Docker Images vs Containers

Before we start, let's clarify two fundamental concepts:

  • Docker Image: A read-only template containing application code, dependencies, and configuration. Think of it as a blueprint or snapshot.
  • Docker Container: A running instance of an image. Containers are isolated, lightweight, and portable execution environments.

Analogy: An image is like a class in programming, while a container is an instance of that class.

Step 1: Create a Docker Network

What are Docker Networks?

Docker networks allow containers to communicate securely and in isolation. Containers on the same network can find each other by name, making it easy to connect services together.

Let's create a custom network for our MongoDB and Mongo Express containers:

docker network create mongo-network

This creates an isolated network where our containers can find each other by name.

📝 Note: Creating a Docker network is optional. You can start containers in the default network by omitting the --net flag, but using custom networks provides better isolation and easier container-to-container communication.

Step 2: Start MongoDB Database

Understanding docker run

The docker run command creates and starts a container from an image. If the image doesn't exist locally, Docker automatically downloads it from Docker Hub (a public registry of container images).

Run a MongoDB container with the following configuration:

docker run -d \
  -p 10000:27017 \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=password \
  -v $(pwd)/mongodb-data:/data/db \
  --name mongodb \
  --net mongo-network \
  mongo

What this command does:

  • -d: Runs container in detached mode (background)
  • -p 10000:27017: Maps port 10000 from your host to container port 27017
    • Left side (10000): Port on your host machine - you'll connect to localhost:10000
    • Right side (27017): Port inside the container where MongoDB listens
  • -v $(pwd)/mongodb-data:/data/db: Mounts a volume to persist database data. Without this, all data is lost when the container is deleted!
    • Left side: $(pwd)/mongodb-data - Creates a folder in your current directory
    • Right side: /data/db - Where MongoDB stores data inside the container
  • -e: Sets environment variables for MongoDB credentials
  • --name mongodb: Names the container "mongodb"
  • --net mongo-network: Connects to our custom network
  • mongo: The official MongoDB image from Docker Hub

Understanding Port Mapping (-p flag):

Containers run in isolation with their own network stack. When MongoDB runs inside a container, it listens on port 27017 inside that container's network. Without port mapping, your host machine (or applications running on your host) cannot reach MongoDB because the container's port 27017 is isolated and inaccessible from outside.

Port mapping creates a bridge between your host machine and the container, making the containerized service accessible.

Format: -p HOST_PORT:CONTAINER_PORT

  • HOST_PORT (left side): The port on your computer/host machine
  • CONTAINER_PORT (right side): The port inside the container where the application listens

By mapping to different host ports, you can run multiple MongoDB containers simultaneously:

  • Development database: docker run -p 10000:27017 --name mongodb-dev mongo
  • Testing database: docker run -p 10001:27017 --name mongodb-test mongo
  • Production-like database: docker run -p 10002:27017 --name mongodb-prod mongo

Each container runs MongoDB on port 27017 internally, but they're accessible on your host at different ports!

Understanding Volume Mounting (-v flag):

Docker containers are ephemeral by default - when you delete a container, all data inside it is lost. To persist data, we use volume mounts to store data on the host machine.

The -v flag mounts a directory from your host machine into the container, creating persistent storage.

Format: -v HOST_PATH:CONTAINER_PATH

  • HOST_PATH: Directory on your computer (creates if doesn't exist)
  • CONTAINER_PATH: Directory inside the container

After running this command, check your current directory - you'll see a new mongodb-data folder containing all MongoDB files. This ensures that your database data persists even if the container is deleted or recreated.

Step 3: Start Mongo Express (Database Admin UI)

Now run Mongo Express, a web-based MongoDB admin interface:

docker run -d \
  -p 10001:8081 \
  -e ME_CONFIG_MONGODB_ADMINUSERNAME=admin \
  -e ME_CONFIG_MONGODB_ADMINPASSWORD=password \
  -e ME_CONFIG_MONGODB_SERVER=mongodb \
  --net mongo-network \
  --name mongo-express \
  mongo-express

What this command does:

  • -p 10001:8081: Maps port 10001 from your host to container port 8081. This allows your browser on the host machine to access the web interface running inside the isolated container
    • Left side (10001): Port on your host - access the web UI at localhost:10001
    • Right side (8081): Port inside the container where Mongo Express web server listens
    • Why 10001? We're using a custom port range (10000-series) for Part 1 to avoid conflicts with other parts of the tutorial
    • -e ME_CONFIG_MONGODB_SERVER=mongodb: Connects to the MongoDB container using the name "mongodb"
    • Note: Mongo Express doesn't need to connect to MongoDB through localhost:10000 because both containers are on the same Docker network. Container-to-container communication uses service names directly and the default container port (27017)!
  • Uses the same credentials we set for MongoDB

Understanding Port Mapping in This Setup:

  • Your browser → localhost:10001 → (port mapping) → Container's Mongo Express on port 8081
  • Mongo Express → mongodb:27017 → (Docker network) → Container's MongoDB on port 27017
  • Your Node.js app (on host) → localhost:10000 → (port mapping) → Container's MongoDB on port 27017

📝 Note: Notice that Mongo Express connects to MongoDB using the container name mongodb, not localhost:27017. This is because both containers are on the same Docker network, so they can communicate directly using container names as hostnames. Port mapping is only needed for host-to-container communication!

Step 4: Examine Docker Images and Containers

Understanding docker images and docker ps

Now let's inspect what Docker has created using two essential commands:

  • docker images: Lists all downloaded/built Docker images on your system
  • docker ps: Lists running containers (add -a to see all containers, including stopped ones)

List all downloaded Docker images:

docker images

You should see:

  • mongo - The MongoDB database image
  • mongo-express - The Mongo Express web UI image

Docker images list

List running containers:

docker ps

You should see both containers running:

  • mongodb - Running on port 10000 (host) → 27017 (container)
  • mongo-express - Running on port 10001 (host) → 8081 (container)

Docker ps output showing running containers

What information does docker ps show?

  • CONTAINER ID - Unique identifier for the container
  • IMAGE - Which image the container was created from
  • COMMAND - The command running inside the container
  • CREATED - When the container was created
  • STATUS - Whether it's running and for how long
  • PORTS - Port mappings (host:container)
  • NAMES - The name you assigned (or Docker generated)

Step 5: Access Mongo Express

Open Mongo Express in your browser:

http://localhost:10001

☁️ Using GitHub Codespaces? If you're working in Codespaces, don't use localhost. Instead, go to the Ports tab in VS Code, find port 10001, and click the URL link to access the forwarded port. Note: If you see HTTP ERROR 404, wait 5-10 seconds for port forwarding to initialize, then refresh.

alt text

Login with the username "admin" and password "pass"

alt text

Step 6: Run the Quiz Application

Navigate to the app directory and start the server:

cd app
npm install
node server.js

The application will start on port 3000 and automatically connect to MongoDB at localhost:10000. The application automatically creates the database and collection if they don't exist!

Step 7: Play the Quiz Game

Open your web browser and navigate to:

http://localhost:3000

☁️ Using GitHub Codespaces?

  1. Go to the Ports tab in VS Code (bottom panel)
  2. Find port 3000 in the list
  3. Click the globe icon 🌐 or the forwarded URL to open the app
  4. If you see "HTTP ERROR 404": This is normal! Codespaces port forwarding takes 5-10 seconds to initialize. Simply wait 10 seconds and refresh the page.
  5. Tip: You can check if the server is ready by adding /health to the URL (e.g., https://your-url.app.github.dev/health). When it returns {"status":"ok"}, the app is ready.

alt text

Now play the Docker Quiz Challenge.

Step 9: Observe Database Updates in Real-Time

While the quiz is open in one browser tab, keep Mongo Express open in another tab:

  1. Go back to Mongo Express at http://localhost:10001 (or your Codespaces forwarded URL)
  2. Navigate to: quiz-db → scores collection
  3. Click the "scores" collection to view documents
  4. Submit a quiz in your quiz app
  5. Refresh the Mongo Express page
  6. You'll see a new document with your score, name, and timestamp!

Mongo Express showing quiz scores

Quiz results and leaderboard

  • Check the mongodb-data folder in your project directory - you'll see the actual database files!

Step 10: Verify Data Persistence

Let's demonstrate that your data is truly persistent thanks to the volume mount.

Stop the Node.js application:

Go to the terminal where node server.js is running and press Ctrl+C to stop it.

Stop and remove the MongoDB container (but NOT Mongo Express yet):

docker stop mongodb
docker rm mongodb

Check the data folder - it's still there!

ls -la mongodb-data/

You'll see all the MongoDB data files are preserved on your host machine.

Restart MongoDB with the same volume:

docker run -d \
  -p 10000:27017 \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=password \
  -v $(pwd)/mongodb-data:/data/db \
  --name mongodb \
  --net mongo-network \
  mongo

Access Mongo Express again at http://localhost:10001 and navigate to quiz-db → scores.

All your quiz scores are still there! This demonstrates the power of volume mounting - your data survived the container being deleted and recreated.

Step 11: Prepare for Part 2

Keep the database and Mongo Express running - we'll use them in Part 2 when we containerize the quiz application.

Stop the Node.js app if it's still running (press Ctrl+C).


Part 2: Building Custom Docker Images

In Part 1, you ran pre-built images from Docker Hub (mongo and mongo-express). Now let's learn how to create your own custom Docker image for the quiz application.

Here is the diagram of the architecture we'll set up:

Part 2 Architecture Diagram

Understanding Dockerfiles

A Dockerfile is a text file containing instructions to build a Docker image. It's like a recipe that tells Docker how to package your application.

Step 1: Examine the Dockerfile

Let's look at the Dockerfile in the repository root. Open it and you'll see:

FROM node:13-alpine

ENV MONGO_DB_USERNAME=admin \
    MONGO_DB_PWD=password

RUN mkdir -p /home/app

COPY ./app /home/app

WORKDIR /home/app

RUN npm install

CMD ["node", "server.js"]

Understanding Each Instruction:

FROM node:13-alpine

Specifies the base image. Every Dockerfile must start with FROM.

  • node:13-alpine is a Node.js 13 image based on Alpine Linux
  • Alpine Linux is a minimal Linux distribution (~5MB) that creates smaller images
  • Example: node:13-alpine is ~50MB vs node:13 at ~500MB

ENV MONGO_DB_USERNAME=admin MONGO_DB_PWD=password

Sets environment variables inside the container.

  • These variables can be used by the application at runtime
  • Format: ENV KEY=value

RUN mkdir -p /home/app

Executes commands during the build process.

  • Creates a directory in the image
  • RUN is used for installing packages, creating directories, etc.
  • Each RUN creates a new layer in the image

COPY ./app /home/app

Copies files from your host machine into the image.

  • Left side (./app): Source path on your host
  • Right side (/home/app): Destination path in the image

WORKDIR /home/app

Sets the working directory for subsequent instructions.

  • All following commands (RUN, CMD) will execute from this directory
  • Like doing cd /home/app in a terminal

RUN npm install

Installs Node.js dependencies during the build.

  • Reads package.json and installs all required packages
  • Key point: This happens at build time, not when the container starts

CMD ["node", "server.js"]

Specifies the command to run when the container starts.

  • Only the last CMD in a Dockerfile takes effect
  • This is the entry point for your application
  • Key Difference: RUN executes at build time, CMD executes at container runtime

Step 2: Build the Docker Image

Understanding docker build

The docker build command builds a Docker image from a Dockerfile. It reads the instructions and creates an image layer by layer.

From the repository root folder, run:

docker build -t my-quiz-app:1.0 .

What this command does:

  • docker build: The build command
  • -t my-quiz-app:1.0: Tags the image with a name and version
    • my-quiz-app: Image name
    • 1.0: Version tag (you can use any versioning scheme)
  • .: Build context (current directory) - Docker looks for the Dockerfile here

Watch the build process: You'll see Docker executing each instruction in the Dockerfile:

  1. Pulling the base image (node:13-alpine)
  2. Creating directories
  3. Copying files
  4. Installing dependencies
  5. Setting the startup command

Step 3: Verify the Image

List all Docker images on your system:

docker images

You should see your newly built image:

  • my-quiz-app with tag 1.0
  • Notice the image size - Alpine-based images are much smaller!

Docker images showing custom built image

Step 4: Verify Your Database Setup

Check that MongoDB and Mongo Express are still running from Part 1:

docker ps

You should see:

  • mongodb - Running on port 10000
  • mongo-express - Running on port 10001

If they're not running, start them again:

docker run -d \
  -p 10000:27017 \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=password \
  -v $(pwd)/mongodb-data:/data/db \
  --name mongodb \
  --net mongo-network \
  mongo

docker run -d \
  -p 10001:8081 \
  -e ME_CONFIG_MONGODB_ADMINUSERNAME=admin \
  -e ME_CONFIG_MONGODB_ADMINPASSWORD=password \
  -e ME_CONFIG_MONGODB_SERVER=mongodb \
  --net mongo-network \
  --name mongo-express \
  mongo-express

Your database and collection from Part 1 should still exist! If you visit Mongo Express at http://localhost:10001:

  • Database quiz-db should exist
  • Collection scores should exist (may already have data from Part 1)

Step 5: Run Your Custom Container

Now run a container from your custom image:

docker run -d \
  -p 3000:3000 \
  -e MONGO_HOST=mongodb \
  -e MONGO_PORT=27017 \
  -e MONGO_USER=admin \
  -e MONGO_PASSWORD=password \
  --name quiz-app \
  --net mongo-network \
  my-quiz-app:1.0

What this command does:

  • Runs your custom my-quiz-app:1.0 image
  • -p 3000:3000: Maps port 3000 (the app listens on port 3000 internally)
  • Environment variables configure MongoDB connection:
    • MONGO_HOST=mongodb - Uses the container name on the Docker network
    • MONGO_PORT=27017 - Uses the internal container port (not the host port!)
    • MONGO_USER=admin and MONGO_PASSWORD=password - MongoDB credentials
  • --net mongo-network: Connects to the same network as MongoDB
  • Key Concept: Containers on the same network communicate using service names and internal ports, not host ports!
  • The CMD ["node", "server.js"] from your Dockerfile runs automatically

Step 6: Access the Quiz Application

Open your browser to:

http://localhost:3000

☁️ Using GitHub Codespaces? Use the forwarded URL from the Ports tab for port 3000.

Congratulations! Your application now runs entirely in containers:

  • ✅ MongoDB in a container (port 10000 on host)
  • ✅ Mongo Express in a container (port 10001 on host)
  • ✅ Quiz app in a container (port 3000 on host) - your custom image!

All three containers communicate via the mongo-network Docker network!

Step 7: Check Running Containers

Verify all three containers are running:

docker ps

You should see:

  • mongodb - Port mapping: 10000→27017
  • mongo-express - Port mapping: 10001→8081
  • quiz-app - Port mapping: 3000→3000

Step 8: View Container Logs

Understanding docker logs

To troubleshoot or monitor your containers, use docker logs:

docker logs quiz-app

This shows all output from your application, including:

  • Startup messages
  • MongoDB connection status (should show "✅ MongoDB Connection Successful")
  • Database and collection creation confirmation
  • Any errors or warnings

Check the logs to confirm database setup:

✅ MongoDB Connection Successful
✅ Database 'quiz-db' and collection 'scores' created successfully

Step 9: Verify Database in Mongo Express

Open Mongo Express at http://localhost:10001 and verify:

  1. Navigate to the databases list
  2. You should see quiz-db database (created automatically by the app)
  3. Click on quiz-db
  4. You should see scores collection (created automatically by the app)

💡 Note: The containerized app automatically created these on startup. No manual setup required!

Step 10: Play the Quiz and See Your Data

Access the quiz at http://localhost:3000 and play a few rounds. Then:

  1. Open Mongo Express at http://localhost:10001
  2. Navigate to quiz-db → scores
  3. See all your scores (including any from Part 1!)

All data is persisted in the mongodb-data folder on your host.

Step 10: Cleaning up

Now let's clean up the containers to prepare for Docker Compose (which will manage everything for us).

Stop and remove all containers:

docker rm -f mongodb mongo-express quiz-app

Remove the network:

docker network rm mongo-network

💡 Important: Notice we're NOT deleting the mongodb-data folder! Your quiz scores are safely stored there. In Part 3, we'll use Docker Compose with different ports to show how you can run multiple environments simultaneously.

Key Takeaways from Part 2:

  • Dockerfiles define how to build custom images
  • docker build creates images from Dockerfiles
  • RUN executes at build time, CMD executes at runtime
  • Alpine-based images are smaller and faster
  • Containerized apps connect to other containers using service names (not localhost)
  • All three parts of our application (database, admin UI, app) can run in containers

Part 3: Docker Compose

In Parts 1 and 2, you manually ran multiple docker run commands for each container. This works, but it's tedious and error-prone. Docker Compose solves this by letting you define all services in a single file and manage them with simple commands.

Here is the diagram of the architecture we'll set up.

Docker Compose diagram

What is Docker Compose?

Docker Compose is a tool for defining and running multi-container applications. Instead of multiple docker run commands, you write a YAML file that describes your entire application stack.

Key Benefits:

  • Define multiple services in one file
  • Start/stop all services with one command
  • Automatic network creation
  • Automatic service name resolution (DNS)
  • Environment variables and volumes managed in one place

Understanding docker-compose up

The docker-compose up command reads your docker-compose.yaml file and:

  1. Creates networks automatically
  2. Starts all services in the correct order
  3. Links them together
  4. Shows combined logs from all services

🔍 Examine the docker-compose.yaml File

Before we run Docker Compose, let's examine the docker-compose.yaml file in the repository root. The file has been configured to use different ports for Part 3.

Here's what the docker-compose.yaml file looks like:

version: '3'
services:
  my-app:
    build: .
    ports:
     - 3001:3000
    environment:
     - MONGO_HOST=mongodb
     - MONGO_PORT=27017
     - MONGO_USER=admin
     - MONGO_PASSWORD=password
    depends_on:
     - mongodb
  mongodb:
    image: mongo
    ports:
     - 20000:27017
    environment:
     - MONGO_INITDB_ROOT_USERNAME=admin
     - MONGO_INITDB_ROOT_PASSWORD=password
    volumes:
     - mongo-data:/data/db
  mongo-express:
    image: mongo-express
    restart: always
    ports:
     - 20001:8081
    environment:
     - ME_CONFIG_MONGODB_ADMINUSERNAME=admin
     - ME_CONFIG_MONGODB_ADMINPASSWORD=password
     - ME_CONFIG_MONGODB_SERVER=mongodb
    depends_on:
     - "mongodb"
volumes:
  mongo-data:
    driver: local

Line-by-Line Explanation

version: '3'

  • Specifies the Docker Compose file format version

services:

  • Defines all the containers (services) in our application

mongodb: service

  • image: mongo - Uses the official MongoDB image from Docker Hub
  • ports: - 20000:27017 - Maps host port 20000 to container port 27017
    • Left (20000): Port on your host machine - access at localhost:20000
    • Right (27017): Port inside the MongoDB container
    • Why 20000? We use port 20000 for Part 3 to avoid conflicts with Part 1 (which used 10000). This allows you to run both setups simultaneously!
    • Why needed? If you want to connect to MongoDB from your host machine (e.g., using a database client)
  • environment: - Sets environment variables inside the container
    • MONGO_INITDB_ROOT_USERNAME=admin - Creates admin user
    • MONGO_INITDB_ROOT_PASSWORD=password - Sets admin password
  • volumes: - mongo-data:/data/db - Mounts a named volume for data persistence
    • Left side (mongo-data): The volume name
    • Right side (/data/db): Where MongoDB stores data inside the container

mongo-express: service

  • image: mongo-express - Uses the Mongo Express web UI image
  • restart: always - Automatically restarts if the container crashes
  • ports: - 20001:8081 - Maps host port 20001 to container port 8081
    • Left (20001): Port on your host machine - access at localhost:20001
    • Right (8081): Port inside the container where Mongo Express web server listens
    • Why 20001? Consistent with Part 3's port range (20000-series), avoiding conflicts with Part 1 (10000-series)
  • environment: - Configuration to connect to MongoDB
    • ME_CONFIG_MONGODB_ADMINUSERNAME=admin - MongoDB credentials
    • ME_CONFIG_MONGODB_ADMINPASSWORD=password - Must match MongoDB settings
    • ME_CONFIG_MONGODB_SERVER=mongodb - Uses service name for connection
      • Important: Containers communicate using service names as hostnames!
  • depends_on: - "mongodb" - Ensures MongoDB starts before Mongo Express

my-app: service (NEW!)

  • image: my-quiz-app:1.0 - Uses the custom image we built in Part 2
  • ports: - 3001:3000 - Maps host port 3001 to container port 3000
    • Left (3001): Port on your host - access the quiz at localhost:3001
    • Right (3000): Port inside the container where the Node.js app listens
    • Why 3001? Different from Part 2's port 3000, allowing both to run simultaneously if needed
  • environment: - Configuration for the app
    • MONGO_HOST=mongodb - MongoDB service name (container-to-container communication)
    • MONGO_PORT=27017 - Internal MongoDB port (not the host port 20000!)
    • MONGO_USER=admin - MongoDB username
    • MONGO_PASSWORD=password - MongoDB password
    • The app will connect to MongoDB using mongodb:27017 (service name and internal port)
  • depends_on: - "mongodb" - Ensures MongoDB starts before the app

volumes: (at the bottom)

  • mongo-data: - Defines a named volume
  • driver: local - Stores data on the local filesystem
  • Why volumes? Data persists even when containers are deleted

Understanding Docker Volumes:

Volumes persist data beyond the container lifecycle. Without volumes, data is lost when a container is deleted.

Benefits:

  • Store database data permanently
  • Share files between host and container
  • Share data between containers

In our example, MongoDB data is stored in the mongo-data volume, so even if you delete the container, your quiz scores remain safe!

Note: This is a Docker-managed named volume (different from the -v $(pwd)/mongodb-data:/data/db bind mount we used in Part 1). Named volumes are stored in Docker's data directory, not your current folder.

Step 1: Start Services with Docker Compose

From the repository root, run:

docker-compose up

This single command:

  • Creates a network automatically
  • Starts MongoDB (port 20000)
  • Starts Mongo Express (port 20001)
  • Starts the Quiz App (port 3001)
  • Links them all together

💡 Tip: Add -d flag to run in detached mode: docker-compose -f docker-compose.yaml up -d

Start a new terminal. Examine the containers created by Docker Compose:

docker ps

Notice the container names now have a prefix

  • Names containers based on the project directory and service name
  • Creates a default network for all services
  • Manages the lifecycle of all services together

You should see three containers:

  • developing-with-docker_mongodb_1 - Port mapping: 20000→27017
  • developing-with-docker_mongo-express_1 - Port mapping: 20001→8081
  • developing-with-docker_my-app_1 - Port mapping: 3001→3000

Docker Compose containers running - showing all three services with port mappings

Check the network created by Docker Compose:

docker network ls

You'll see a network named something like developing-with-docker_default - Docker Compose created this automatically!

Step 2: Access Mongo Express

Open your browser to:

http://localhost:20001

☁️ Using GitHub Codespaces? Find the forwarded URL for port 20001 in the Ports tab. If you see HTTP ERROR 404, wait a few seconds and refresh.

Notice the port is 20001 (Part 3 uses the 20000-series ports).

Mongo Express login screen on port 20001

Login with the username "admin" and password "pass".

Step 3: Access the Quiz Application

The quiz application is already running! Docker Compose started all three services together.

Access the application at:

http://localhost:3001

☁️ Using GitHub Codespaces? Use the forwarded URL from the Ports tab for port 3001. If you see HTTP ERROR 404, wait 5-10 seconds and refresh the page.

Notice the port is 3001 (not 3000 like in Part 2). This is Part 3's port configuration!

When the app starts, it will automatically create the quiz-db database and scores collection if they don't exist.

Step 4: Verify Database Creation

Check the application logs to see the database initialization:

docker logs developing-with-docker_my-app_1

Open Mongo Express at http://localhost:20001 and verify:

  1. Navigate to the databases list
  2. You should see quiz-db database (created automatically)
  3. Click on quiz-db
  4. You should see scores collection (created automatically)

💡 Note: The app automatically creates the database and collection on startup. Since we're using a new Docker-managed volume, this is a fresh database separate from Part 1's data.

Step 5: Play the Quiz and Watch the Database

Interactive Learning Exercise:

  1. Open two browser tabs:

    • Tab 1: Quiz app at localhost:3001 (or forwarded URL)
    • Tab 2: Mongo Express at localhost:20001 (or forwarded URL)
  2. In Mongo Express (Tab 2):

    • Navigate to quiz-db → scores collection
    • Keep this tab open to watch database changes
  3. In Quiz App (Tab 1):

    • Enter your name and start the quiz
    • Answer all 10 Docker questions
    • Submit your quiz
  4. Back to Mongo Express (Tab 2):

    • Refresh the page
    • Observe: A new document appears with your score!
    • Notice the fields: name, score, totalQuestions, percentage, timestamp
  5. Repeat the process:

    • Play the quiz again with a different name
    • Watch new score documents appear in real-time
    • See how the leaderboard ranks players by score

This hands-on exercise demonstrates how Docker containers work together: the Node.js app processes quiz submissions and writes data to MongoDB, all while you observe the changes through Mongo Express!

Stopping Services

To stop all services:

docker-compose  down

This stops and removes all containers. Your data persists in the Docker volume.

Key Takeaways from Part 3:

  • Docker Compose manages multi-container applications with a single YAML file
  • docker-compose up starts all services; docker-compose down stops them
  • Services communicate using service names as hostnames
  • Port mapping format: HOST_PORT:CONTAINER_PORT
  • Volumes preserve data across container lifecycles
  • depends_on controls startup order
  • Docker Compose automatically creates networks and manages container lifecycle

Complete Docker Command Reference

Here's a quick reference of all Docker commands covered in this tutorial:

Essential Commands

docker run - Create and start a container

docker run -d -p 3000:3000 --name my-app --net my-network my-image:1.0

docker build - Build an image from a Dockerfile

docker build -t my-app:1.0 .

docker ps - List running containers (use -a for all)

docker ps
docker ps -a

docker images - List all images

docker images

docker logs - View container logs

docker logs container-name

docker rm - Remove containers (use -f to force)

docker rm -f container-name

docker network create - Create a network

docker network create my-network

docker network ls - List networks

docker network ls

docker-compose up - Start services from docker-compose.yaml

docker-compose up
docker-compose up -d  # detached mode

docker-compose down - Stop and remove all services

docker-compose down

Additional Resources