Containerize a Website with Docker

Package a website into a Docker container using nginx as a web server.

Introduction

30 Second Summary

Every time you set up an app on a new computer, you deal with missing files, wrong versions, and broken configurations. Containers solve this by packaging everything an application needs into a single portable unit that runs the same way everywhere.

In this project, you will create an HTML page, write a Dockerfile that packages it with a web server, build it into a Docker image, and run it as a container you can access in your browser.

What You'll Build

A containerized website served by nginx running inside a Docker container, built from a custom Dockerfile you write from scratch.

By the end of this project, you'll have:

  • Docker Desktop installed and running on your machine with a verified working setup.
  • A custom Docker image built from a Dockerfile that packages your website with an nginx web server.
  • A running container serving your website locally, with full understanding of how to start, stop, and manage containers.
  • Secret Mission: Add a custom nginx configuration to serve a second page and explore Docker image layers.

Are there any prerequisites?

None. This project includes setup instructions for every tool you need, including Docker Desktop and VS Code. No programming experience is required.

Install Docker Desktop

To containerize your website, you first need Docker installed on your machine. Docker Desktop gives you the Docker engine plus a graphical interface for managing containers.

In this step, you'll install Docker Desktop on Windows and verify it works by running your first container.

In this step, get ready to:
  • Install Docker Desktop for Windows.
  • Verify Docker works with the hello-world container.
  • Understand the basic container lifecycle.
Download and install Docker Desktop

Docker Desktop is a one-click installer that sets up the Docker engine, CLI tools, and a management GUI on your machine. Let's check if you already have it installed.

  • Open a terminal in VS Code by pressing Ctrl+` (Windows) or Cmd+` (macOS).
  • Check if Docker is already installed by running this command:
docker --version

✔️ I see a version number

Docker is already installed on your machine. Skip ahead to the verification substep below where you'll run the hello-world container.

ⓧ Command not found

Docker is not installed yet. Follow the steps below to install Docker Desktop for Windows.

  • Open the Docker Desktop download page in your browser:
  • https://docs.docker.com/desktop/setup/install/windows-install/
  • Click the download button to get Docker Desktop Installer.exe.
  • Run Docker Desktop Installer.exe once the download completes.
  • Choose the per-user installation option (recommended, no admin privileges required).
  • On the Configuration page, ensure Use WSL 2 is selected.
  • Complete the wizard and restart your computer if prompted.

What is WSL 2?

WSL 2 (Windows Subsystem for Linux 2) is a lightweight Linux environment built into Windows. Docker Desktop uses it as its backend to run containers efficiently on Windows without a full virtual machine.

  • After the install finishes, press the Windows key to open your search bar.
  • Type Docker Desktop and press Enter to open it.
  • Wait for Docker Desktop to finish starting up. You'll see the Docker icon appear in your system tray when it's ready.

Docker Desktop won't start?

If Docker Desktop shows an error about WSL, your system may need a reboot after the WSL 2 components were installed. Restart your computer and try launching Docker Desktop again.

help me troubleshoot Docker Desktop not starting on Windows

Verify Docker is working

Now that Docker Desktop is running, let's confirm the Docker CLI works and run your first container.

  • Return to your VS Code terminal.
  • Confirm the Docker CLI is available by running this command:
docker --version

You should see a version string like Docker version 28.x.x printed in your terminal.

  • Run your first container by running this command:
docker run hello-world

What does this command do?

docker run hello-world tells Docker to create and run a container from the hello-world image. Since you don't have this image locally, Docker automatically downloads (pulls) it from Docker Hub first.

You should see a message that starts with Hello from Docker! in your terminal output. This confirms Docker can pull images, create containers, and run them successfully.

Don't see the success message?

Make sure Docker Desktop is fully running (check for the Docker icon in your system tray). If you see a connection error, Docker Desktop may still be starting up. Wait a minute and try the command again.

help me fix docker run hello-world failing on Windows

Understand what just happened

That single command demonstrated the entire container lifecycle. Every container you'll run in this project follows the same pattern.

  • Pull — Docker downloaded the hello-world image from Docker Hub.
  • Create — Docker made a new container from that image.
  • Run — The container executed its program and printed the success message.
  • Exit — The container finished its task and stopped.

Why does a container exit?

A container runs only as long as its main process is alive. The hello-world container's only job is to print a message, so it exits immediately after. In the next step, you'll run an nginx web server container that stays running because the server process keeps listening for requests.

Your Docker environment is ready. Next up, you'll create your website files and write a Dockerfile to package them into a custom image.

Create Your Website Files

Docker is installed and verified. You pulled an image from Docker Hub and ran your first container with a single command.

Now you'll create the files that will live inside your own custom container. Every Docker image starts with a recipe called a Dockerfile. Your Dockerfile will tell Docker to take the nginx web server and add your custom HTML page to it.

In this step, get ready to:
  • Create a project folder on your Desktop.
  • Write an HTML page that your container will serve.
  • Write a Dockerfile that packages your HTML with nginx.
Set up your project folder

You need a dedicated folder to hold your website files and Dockerfile. Docker looks for its instructions in whatever directory you run the build command from.

  • Switch back to your VS Code terminal and move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir my-platform-site
cd my-platform-site
  • Confirm the folder exists by running:
pwd

You should see a path ending in my-platform-site.

  • Now open this folder as your VS Code workspace. Click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop, select the my-platform-site folder, and click Select Folder.

VS Code reloads with my-platform-site shown in the Explorer sidebar on the left.

  • Open a new terminal by clicking Terminal in the top menu bar, then New Terminal.

Why open the folder as a workspace?

Opening the folder in VS Code sets it as the working directory for all new terminals. Any commands you run will automatically target the my-platform-site folder without needing to cd into it again.

🙋‍♀️ Don't see my-platform-site in the sidebar?

Make sure you selected the my-platform-site folder itself, not the Desktop folder containing it. The Explorer sidebar should show the folder name at the top.

help me open my project folder in VS Code

Write your HTML page

This HTML file is the website that nginx will serve from inside your container. The content is intentionally simple because the container itself is the focus, not the web design.

  • In the VS Code Explorer sidebar, hover over the MY-PLATFORM-SITE heading and click the New File icon (the page icon with a plus sign).
  • Type index.html as the file name and press Enter.
  • Copy the following HTML into index.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Platform Engineering Site</title>
    <style>
        body {
            font-family: sans-serif;
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        h1 { color: #2c3e50; }
        .container-info {
            background: white;
            padding: 20px;
            border-radius: 8px;
            border-left: 4px solid #3498db;
        }
    </style>
</head>
<body>
    <h1>Hello from Docker!</h1>
    <div class="container-info">
        <p>This website is running inside a Docker container.</p>
        <p>It uses the official nginx image as a base, with my custom HTML copied in during the image build.</p>
    </div>
</body>
</html>

What does this HTML do?

This is a basic webpage with a heading and two paragraphs inside a styled card. The .container-info class adds a white background and a blue left border for visual structure.

The content describes what's happening at runtime. Once this page is running inside a container, it serves as proof that your Docker workflow is working end to end.

  • Save index.html by pressing Ctrl+S.

You won't see this page in a browser yet. In the next step, you'll build it into a Docker image and run it as a container.

✔️ Awesome, I've got everything!

Great. Make sure you've saved index.html before moving on.

ⓧ I'd like to double check the full code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Platform Engineering Site</title>
    <style>
        body {
            font-family: sans-serif;
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        h1 { color: #2c3e50; }
        .container-info {
            background: white;
            padding: 20px;
            border-radius: 8px;
            border-left: 4px solid #3498db;
        }
    </style>
</head>
<body>
    <h1>Hello from Docker!</h1>
    <div class="container-info">
        <p>This website is running inside a Docker container.</p>
        <p>It uses the official nginx image as a base, with my custom HTML copied in during the image build.</p>
    </div>
</body>
</html>
Create your Dockerfile

A Dockerfile is a recipe that tells Docker exactly how to build an image. Each line is an instruction that adds a layer to the final image.

  • In the VS Code Explorer sidebar, click the New File icon again.
  • Type Dockerfile as the file name (no extension) and press Enter.
  • Copy the following two instructions into your Dockerfile:
FROM nginx:1.30-alpine
COPY index.html /usr/share/nginx/html/index.html

What do these instructions do?

  • FROM nginx:1.30-alpine tells Docker to start from the official nginx image. This gives you a fully working web server without any manual installation. The alpine variant is a lightweight version that keeps the image small.
  • COPY index.html /usr/share/nginx/html/index.html places your HTML file into the directory where nginx looks for content to serve. When the container starts, nginx automatically serves whatever is in that folder.
  • Save Dockerfile by pressing Ctrl+S.
  • Confirm both files exist by running this in your terminal:
ls

You should see Dockerfile and index.html listed in the output.

Don't see both files?

Make sure your terminal is in the my-platform-site folder. Run pwd to check your current directory. If it shows a different path, close the terminal and open a new one from Terminal > New Terminal. VS Code opens new terminals in the workspace root automatically.

Also check that Dockerfile has no file extension. It should not be Dockerfile.txt or Dockerfile.dockerfile.

help me find my project files

✔️ Awesome, I've got everything!

Great. Make sure both files are saved before moving on.

ⓧ I'd like to double check the full code

FROM nginx:1.30-alpine
COPY index.html /usr/share/nginx/html/index.html

Your project files are ready. Next up, you'll build these files into a Docker image and run your website inside a container.

Build and Run Your Container

You have a Dockerfile and an HTML file sitting in your project folder. Right now they are just files on your computer.

In this step, you will turn those files into a portable Docker image and launch it as a running container. By the end, your website will be live in your browser, served by nginx from inside a container.

In this step, get ready to:
  • Build a Docker image from your Dockerfile.
  • Run a container and view your website in the browser.
  • Manage your container with start, stop, and log commands.
Build your Docker image

A Docker image is like a snapshot that packages your application and everything it needs to run. Building an image reads your Dockerfile instructions and creates that snapshot.

  • Build your Docker image by running this command in the VS Code terminal:
docker build -t my-platform-site .

What does this command do?

  • -t my-platform-site gives your image a name (called a tag). Without this, Docker assigns a random ID that is hard to remember.
  • . tells Docker to look for the Dockerfile in the current directory (your my-platform-site folder).
  • Each instruction in your Dockerfile creates a layer. Docker caches layers so future builds are faster.
  • Watch the terminal output. You should see the word FINISHED in the first line of output once the build completes successfully.

Not seeing FINISHED?

  • Make sure you are running the command from inside the my-platform-site folder. Docker looks for a file literally named Dockerfile in the directory you specify.
  • Check that your Dockerfile has no file extension. On Windows, File Explorer sometimes hides extensions, so it could be Dockerfile.txt without you noticing.
  • Confirm Docker Desktop is running. Look for the Docker whale icon in your system tray.

Still stuck? Help me troubleshoot my Docker build failing.

Run your container

Your image is built. Now you will create a running container from it. A container is a live instance of your image, like pressing play on a video.

  • Start a container from your image by running this command:
docker run -d -p 8080:80 --name my-site my-platform-site

What does this command do?

  • -d runs the container in the background (detached mode). Your terminal stays free for more commands.
  • -p 8080:80 maps port 8080 on your machine to port 80 inside the container. Nginx listens on port 80 by default, so this lets you reach it at localhost:8080.
  • --name my-site gives your container a friendly name instead of a random one.
  • my-platform-site at the end is the image name you built in the previous command.
  • Open your web browser and go to http://localhost:8080.
  • You should see your HTML page with the heading "Hello from Docker!" served by nginx running inside a container.

Not seeing your website?

  • Make sure you typed http://localhost:8080 (not https). The container is not set up for HTTPS.
  • If you see "port already in use", another application is using port 8080. Stop it, or change the command to -p 9090:80 and visit http://localhost:9090 instead.
  • Run docker ps to check if the container is actually running. If it exited immediately, run docker logs my-site to see what went wrong.

Still stuck? Help me figure out why my container is not serving my website.

Explore container management commands

Your container is running in the background. Docker gives you commands to inspect, stop, and restart containers without losing any data.

  • See your running container by running:
docker ps
  • You should see a table with columns including CONTAINER ID, IMAGE, STATUS, PORTS, and NAMES. Your my-site container should appear with a status of "Up".
  • View the nginx access logs by running:
docker logs my-site
  • You should see log entries from nginx. If you refreshed your browser earlier, you will see the HTTP requests logged here.
  • Stop your container by running:
docker stop my-site
  • Refresh http://localhost:8080 in your browser. The page should no longer load because the container is stopped.
  • See all containers (including stopped ones) by running:
docker ps -a
  • Your my-site container should appear with a status of "Exited". The -a flag shows all containers, not just running ones.
  • Start the container again by running:
docker start my-site
  • Refresh http://localhost:8080 in your browser. Your website should be back.

Why does stop keep the container around?

Stopping a container is like pausing a program. The container still exists on disk with all its configuration intact. You can restart it anytime with docker start.

To fully remove a container, you would need to run docker rm after stopping it. You will do this in the next step.

🙋‍♀️ Container won't start again?

  • Run docker ps -a to confirm the container still exists. If you accidentally removed it, re-run the docker run command from earlier.
  • If another container is now using port 8080, stop it first or use a different port.

Still stuck? Help me troubleshoot my container management commands.

Your container is running, your website is live, and you know how to manage the container lifecycle. Next up, you will edit your website, rebuild the image, and see why containers are called immutable.

Modify, Rebuild, and Compare

Your container is running and serving your website. But what happens when you want to update the content?

In this step, you will edit your HTML, discover that containers are immutable (they do not pick up file changes automatically), and learn the platform engineering workflow of rebuild and redeploy.

In this step, get ready to:
  • Update your website with new content.
  • Rebuild your Docker image and redeploy the container.
  • Inspect your images to see what Docker stores.
Update your website

Your website currently shows a simple heading and two paragraphs. Let's add a "What I Learned" section so the page captures your progress.

  • In VS Code, open index.html.
  • Find the second <p> line that says It uses the official nginx image as a base... and its closing </p> tag.
  • Directly below that closing </p> tag (but still inside the <div class="container-info">), add the following code:
        <h2>What I Learned</h2>
        <ul>
            <li>A Docker image is a portable package containing everything an app needs to run</li>
            <li>A container is a running instance of an image</li>
            <li>A Dockerfile is a recipe that tells Docker how to build an image</li>
            <li>Port mapping connects the outside world to services inside the container</li>
        </ul>

What does this code do?

This adds a summary list to your page. Each list item captures a key Docker concept you have used in this project so far.

The content itself is not the focus. What matters is what happens next when you try to view it in your running container.

  • Save index.html.
  • Refresh your browser at http://localhost:8080.

The page still shows the old content. Your "What I Learned" list is nowhere to be seen.

Why don't my changes show up?

Containers are immutable snapshots. When you built the image, Docker copied your file at that moment in time. Editing the file on your machine does not change what is inside the running container.

To see your updated content, you need to rebuild the image and run a new container. This is the standard platform engineering workflow: change code, rebuild image, redeploy container.

✔️ Awesome, I've got everything!

Great. Double check you have saved your file before moving on.

ⓧ I'd like to double check the full code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Platform Engineering Site</title>
    <style>
        body {
            font-family: sans-serif;
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        h1 { color: #2c3e50; }
        .container-info {
            background: white;
            padding: 20px;
            border-radius: 8px;
            border-left: 4px solid #3498db;
        }
    </style>
</head>
<body>
    <h1>Hello from Docker!</h1>
    <div class="container-info">
        <p>This website is running inside a Docker container.</p>
        <p>It uses the official nginx image as a base, with my custom HTML copied in during the image build.</p>
        <h2>What I Learned</h2>
        <ul>
            <li>A Docker image is a portable package containing everything an app needs to run</li>
            <li>A container is a running instance of an image</li>
            <li>A Dockerfile is a recipe that tells Docker how to build an image</li>
            <li>Port mapping connects the outside world to services inside the container</li>
        </ul>
    </div>
</body>
</html>
Rebuild and redeploy

To get your changes into the container, you need to remove the old container, rebuild the image, and launch a fresh one.

  • Stop and remove the old container by running these commands:
docker stop my-site
docker rm my-site

Why stop and remove?

You cannot reuse a container name that already exists. docker stop gracefully shuts down the container. docker rm deletes the stopped container so you can create a new one with the same name.

  • Rebuild your image with the updated HTML by running:
docker build -t my-platform-site .

What happens during the rebuild?

Docker re-executes the instructions in your Dockerfile. The FROM layer is cached (the base image has not changed), so only the COPY layer rebuilds with your new file. This is Docker's layer caching at work.

  • Run a new container from the rebuilt image by running:
docker run -d -p 8080:80 --name my-site my-platform-site
  • Refresh your browser at http://localhost:8080.

You should see your updated page with the "What I Learned" list displayed below the original paragraphs.

Still seeing the old page?

  • Try a hard refresh with Ctrl+Shift+R to bypass browser cache.
  • Make sure you saved index.html before running the docker build command. The build copies the file as it was on disk at that moment.
  • Run docker ps to confirm a container named my-site is running. If it is not listed, re-run the docker run command above.

Help me troubleshoot why my rebuilt Docker container still shows old content.

Inspect your images

Now that you have rebuilt an image, your system holds multiple Docker images. Let's see what Docker stores locally.

  • List all images on your system by running:
docker images

You should see at least two entries: the nginx base image and your my-platform-site custom image.

What am I looking at?

Each row shows an image's repository name, tag, image ID, creation date, and size. Your custom image is built on top of the nginx:1.30-alpine base, so it shares most of its layers with that base image.

  • View detailed metadata about your custom image by running:
docker image inspect my-platform-site

You should see a large JSON output showing layers, size, creation date, and configuration details.

Why does this matter?

This shows why containers are reproducible. The image contains everything needed to run your app: the operating system base, the web server, and your HTML file. Anyone with this image gets the exact same result, every time.

🙋‍♀️ Seeing an error with docker image inspect?

  • Make sure you spell the image name exactly as my-platform-site. Run docker images to confirm the name.

Help me understand the output of docker image inspect.

Secret mission

Custom nginx Configuration and Multi-Page Site

Ready for a challenge? Extend your container to serve multiple pages with a custom nginx configuration, then peek under the hood at Docker image layers to understand how efficient builds work.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely on your local machine, so there are no ongoing cloud costs. Docker Desktop does use system memory while running, so you may want to pause or remove it if you need those resources back.

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Docker container my-site (running, serving your website at port 8080).
  • Docker images: my-platform-site, nginx:1.30-alpine, and hello-world.
  • Project folder my-platform-site on your Desktop (contains index.html, about.html, nginx.conf, and Dockerfile).
  • Docker Desktop application.

Keep everything running

No action needed. Choose this if you are still actively building or experimenting with Docker containers.

  • Your container my-site continues serving your website at http://localhost:8080.
  • Docker Desktop stays running in your system tray and uses some memory.
  • Your project files remain on your Desktop, ready for further edits.

Pause - I'll come back to this later

Stop your running container and quit Docker Desktop to free up system memory. Your files and images stay on disk for when you return.

  • Stop the running container by running this command in your VS Code terminal:
docker stop my-site
  • Quit Docker Desktop by right-clicking the Docker icon in your system tray and selecting Quit Docker Desktop.
  • When you come back, launch Docker Desktop again and run:
docker start my-site

Your website will be back at http://localhost:8080.

Delete - I don't want to use this again

Remove all project resources completely. This frees up disk space and system memory.

  • Stop and remove the container by running these commands:
docker stop my-site
docker rm my-site
  • Remove the Docker images by running:
docker rmi my-platform-site
docker rmi nginx:1.30-alpine
docker rmi hello-world
  • Delete your project folder. Open File Explorer, navigate to your Desktop, right-click the my-platform-site folder, and select Delete.
  • Uninstall Docker Desktop (optional). From the Windows Start menu, open Settings.
  • Select Apps, then Apps & features.
  • Find Docker Desktop in the list, select it, and click Uninstall.
  • Confirm by clicking Uninstall again when prompted.

Keep Docker Desktop if you plan to continue learning

Docker Desktop is used in almost every platform engineering project. If you plan to learn Kubernetes, CI/CD, or infrastructure as code next, keep it installed.

Nice Work!

Nice Work!

You just containerized a website from scratch using Docker. You now have the foundational skill that every platform engineering tool builds upon.

You learned how to:

  • Write a Dockerfile that packages a website with the nginx web server and build it into a portable Docker image.
  • Run a container with port mapping to serve your site locally at http://localhost:8080.
  • Manage the full container lifecycle (start, stop, remove, logs, inspect) and understand why changes require a rebuild and redeploy.
  • Secret Mission: Configured a custom nginx server block, served multiple pages from different URL paths, and explored image layers to understand how Docker caches builds.

Ready to quiz yourself?