Deploy a Web Server on Linux

Run a Python web server on Ubuntu and diagnose network traffic.

Introduction

30 Second Summary

Every time you visit a website, your computer sends a message across the internet to another computer and waits for a reply. When that reply doesn't come back, someone needs to figure out where the message got stuck.

In this project, you will set up a Linux environment on your Windows machine, create a simple website, serve it with Python, and use networking commands to see exactly how your computer sends and receives traffic.

What You'll Build

A local Linux development environment where you create a website, serve it with Python's built-in HTTP server, and diagnose network connectivity using real industry-standard tools.

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

  • A configured Ubuntu environment running inside WSL on your Windows machine.
  • A running HTTP server serving a custom HTML page you created from scratch.
  • Hands-on experience diagnosing network connectivity using ip, ss, ping, and curl.
  • Secret Mission: Configure a firewall with UFW to block and allow traffic to your server.

Are there any prerequisites?

You need a Windows PC running Windows 10 version 2004 or higher, or Windows 11. No prior Linux experience is required.

Install WSL and Ubuntu

Linux is the operating system running on most of the world's servers. To work as a software engineer, you need to be comfortable navigating files, running services, and diagnosing issues in a Linux terminal.

Windows Subsystem for Linux (WSL) lets you run a full Linux environment directly on your Windows machine. In this step, you will install WSL with Ubuntu and prepare it for the rest of the project.

In this step, get ready to:
  • Install WSL and Ubuntu from PowerShell.
  • Set up your Ubuntu user account.
  • Update system packages and verify pre-installed tools.
Install WSL from PowerShell

WSL needs to be installed from an elevated PowerShell prompt. This single command handles everything: enabling the required Windows features, downloading the Linux kernel, and installing Ubuntu.

  • Press the Windows key to open your search bar.
  • Type PowerShell into the search field.
  • Right-click Windows PowerShell in the results and select Run as administrator.
  • Click Yes if Windows asks for permission to make changes.
  • Install WSL by running this command:
wsl --install

What does this command do?

This single command performs four actions behind the scenes:

  • Enables the WSL and Virtual Machine Platform components in Windows.
  • Downloads the latest Linux kernel.
  • Sets WSL 2 as the default version.
  • Installs the Ubuntu distribution.
  • Wait for the installation to finish. You will see progress messages in the terminal.
  • When prompted, restart your computer to complete the installation.

Seeing an error about virtualization?

If you see a message about virtualization not being enabled, you may need to turn it on in your BIOS settings. Restart your computer, enter BIOS (usually by pressing F2, F10, or Del during boot), and enable "Intel VT-x" or "AMD-V" under the CPU settings.

If you see "WslRegisterDistribution failed", try running wsl --update in PowerShell as Administrator and then restarting again.

Still stuck? Help me troubleshoot my WSL installation error.

Set up your Ubuntu user account

After your computer restarts, Ubuntu will launch automatically and finish its first-time setup. This takes a few minutes.

  • Press the Windows key to open your search bar.
  • Type Ubuntu and press Enter to open it.
  • Wait for the message "Installing, this may take a few minutes..." to finish.
  • When prompted, enter a username (must be lowercase, no spaces).
  • Enter a password when prompted. The characters will not appear as you type. This is normal Linux behaviour.
  • Re-enter the password to confirm it.

Why can't I see my password?

Linux hides password characters for security. Nothing appears on screen while you type, not even dots or asterisks. Just type your password and press Enter.

  • Verify you are in your home directory by running:
pwd

You should see /home/yourname where "yourname" is the username you just created.

Ubuntu didn't open after restart?

If Ubuntu did not launch automatically, open it manually from the Start menu by searching for Ubuntu. If you do not see it in the Start menu, open PowerShell as Administrator and run wsl --install -d Ubuntu to install the distribution again.

Need more help? Help me get Ubuntu running after installing WSL.

Update your system and verify tools

Before building anything, you need to make sure your Ubuntu packages are up to date. You will also confirm that Python and the networking tools this project uses are ready.

  • Update and upgrade all system packages by running this command:
sudo apt update && sudo apt upgrade -y

What does this command do?

This is actually two commands chained together with &&.

  • sudo apt update refreshes the list of available packages from Ubuntu's servers.
  • sudo apt upgrade -y installs newer versions of all packages you already have. The -y flag automatically answers "yes" to confirmation prompts.

The sudo prefix runs the command with administrator privileges. It will ask for the password you created during setup.

Wait for the update to finish. This may take a minute or two depending on your internet speed.

  • Check your Python version by running:
python3 --version

✔️ I see Python 3.12

You should see output like Python 3.12.x (the exact patch number may vary). Python is ready for the rest of this project.

ⓧ I see a version lower than 3.12

If you see a version number lower than 3.12, you may have an older Ubuntu release installed.

  • Check your Ubuntu version by running:
lsb_release -a

This project requires Ubuntu 24.04 LTS. If you have an older version, you can reset your WSL installation.

  • Open PowerShell as Administrator on your Windows side.
  • Unregister the old distribution by running:
wsl --unregister Ubuntu
  • Reinstall Ubuntu by running:
wsl --install -d Ubuntu
  • Restart your computer and set up your user account again.

ⓧ Command not found

If you see "command not found", your Ubuntu installation may be incomplete.

  • Install Python by running:
sudo apt install python3 -y
  • Run python3 --version again to confirm it installed successfully.
  • Verify the networking tools are available by running:
ip --version

✔️ I see iproute2 version info

You should see output displaying the iproute2 version. This confirms the ip command is ready for the networking steps later in this project.

ⓧ Command not found

The ip command comes from the iproute2 package, which should be pre-installed on Ubuntu 24.04.

  • Install it by running:
sudo apt install iproute2 -y
  • Run ip --version again to confirm it installed successfully.

Your Linux environment is ready to go. Next up, you will learn to navigate the Linux file system and create the project files for your web server.

Navigate Linux and Create Your Project

Your Ubuntu environment is installed and up to date. Now it's time to learn how to move around inside it and set up your project.

Linux organizes everything as files and directories in a tree structure starting from the root (/). In this step, you'll navigate that tree, create a project folder, and build the HTML page your server will host.

In this step, get ready to:
  • Navigate the Linux file system using core commands.
  • Create a project directory and an HTML file.
  • Read Linux file permissions.
Learn essential navigation commands

Before creating anything, you need to know where you are and what's around you. These commands are the ones you'll use every single day in Linux.

  • Check your current location by running this command:
pwd

You should see /home/yourname (with your actual username). This is your home directory.

  • See what files and folders exist here by running:
ls
  • Now run the detailed version to see hidden files, permissions, and sizes:
ls -la

What is the difference between ls and ls -la?

Plain ls shows only visible file and folder names.

Adding -la reveals hidden files (those starting with .) and shows details like permissions, owner, file size, and last-modified date.

  • Navigate to the root of the entire file system by running:
cd /
  • List what's here by running:
ls

You should see top-level directories like bin, etc, home, var, and tmp. These are the building blocks of a Linux system.

  • Return to your home directory by running:
cd ~
  • Create your project folder by running:
mkdir my-server
  • Enter the new folder by running:
cd my-server
  • Confirm you're in the right place by running:
pwd

You should see /home/yourname/my-server.

Don't see my-server in the path?

Make sure you ran cd ~ before mkdir my-server. If you were still in / when you ran mkdir, you would get a permission denied error because regular users cannot create folders at the system root.

Run cd ~/my-server to navigate directly to the correct location. Help me figure out where my folder was created

Create your website files

Your project folder is ready. Now you'll create the HTML page that your web server will eventually serve to a browser.

  • Create an index.html file by running this command:
echo '<!DOCTYPE html><html><head><title>My Linux Server</title></head><body><h1>Hello from Linux!</h1><p>This page is served from Ubuntu running inside WSL.</p></body></html>' > index.html

What does this command do?

The echo command prints text. The > symbol redirects that text into a file instead of printing it on screen. So this creates index.html and writes your HTML content directly into it.

  • View the file contents to confirm everything looks right by running:
cat index.html

You should see your full HTML content printed in the terminal.

  • Check the file details by running:
ls -la

You should see index.html listed with its file size, permissions, and timestamp.

See a blank file or wrong content?

If cat index.html shows nothing or unexpected content, you may have a typo in the echo command. Pay close attention to the single quotes wrapping the HTML. If you accidentally used double quotes, special characters may have been interpreted by the shell.

Delete the file with rm index.html and run the echo command again. Help me fix my index.html file

✔️ Awesome, I've got everything!

Great. Double check you've saved your file by running cat index.html one more time.

ⓧ I'd like to double check the full code

Your index.html should contain exactly this:

<!DOCTYPE html><html><head><title>My Linux Server</title></head><body><h1>Hello from Linux!</h1><p>This page is served from Ubuntu running inside WSL.</p></body></html>
Explore file permissions and ownership

Every file in Linux has a permissions string that controls who can read, write, or execute it. Understanding this format is essential for troubleshooting access issues.

  • Inspect the permissions on your HTML file by running:
ls -la index.html

You should see output like -rw-r--r-- 1 yourname yourname 163 ...date... index.html.

How do I read file permissions?

The permissions string has 10 characters. The first character is the file type. A - means a regular file. A d means a directory.

The remaining 9 characters are three groups of rwx (read, write, execute):

  • Characters 2-4: owner permissions (what you can do).
  • Characters 5-7: group permissions (what your group can do).
  • Characters 8-10: others permissions (what everyone else can do).

So -rw-r--r-- means: regular file, owner can read and write, group can read, others can read. No one can execute it (no x appears).

Your project files are in place and you understand how Linux tracks who can access them. Next up, you'll start a web server and see your HTML page live in a browser.

Run a Web Server and Inspect It

You have a website file sitting on disk, but nobody can see it yet. For a browser to display your page, a program needs to listen for incoming requests and send back the HTML.

In this step, you will start Python's built-in HTTP server to serve your page, then use networking tools to prove it is running and responding.

In this step, get ready to:
  • Start a web server that serves your HTML file on port 8080.
  • Inspect the running server process using socket statistics.
  • Test connectivity to your server with curl.
Start Python's built-in HTTP server

Python ships with a simple HTTP server module that can serve files from any directory. You do not need to install anything extra.

  • Make sure you are in your project directory by running this command:
cd ~/my-server
  • Start the HTTP server on port 8080 by running:
python3 -m http.server 8080

You should see a message that says Serving HTTP on 0.0.0.0 port 8080 in your terminal.

What does this command do?

python3 -m http.server 8080 tells Python to run its built-in HTTP server module. It serves every file in the current directory to anyone who connects on port 8080.

The 0.0.0.0 address means the server accepts connections from all network interfaces, not just localhost.

Your terminal is now occupied by the running server. You cannot type further commands here until you stop the server.

  • Open a browser on Windows and visit http://localhost:8080 in the address bar.

You should see your "Hello from Linux!" page displayed in the browser.

Why does localhost work from Windows?

WSL automatically forwards ports from the Linux environment to your Windows host. When your server listens on port 8080 inside Ubuntu, Windows can reach it at localhost:8080 without any extra configuration.

Don't see your page in the browser?

Make sure the server is still running in your Ubuntu terminal. If you accidentally pressed Ctrl+C, the server stopped. Run python3 -m http.server 8080 again from inside ~/my-server.

Check that the URL is exactly http://localhost:8080 with no trailing slash missing or extra characters.

Help me troubleshoot

Inspect the running server from a second terminal

Your server is running, but how do you prove it from the command line? The ss command shows which processes are listening on which ports.

  • Open a new Ubuntu terminal window. Press the Windows key, type Ubuntu, and press Enter.
  • Keep the first terminal open so your server keeps running.
  • List all TCP listening ports and their owning processes by running:
ss -tlnp

You should see a table of listening sockets. Look for a line showing port 8080 with python3 as the process.

What do the ss flags mean?

  • -t shows only TCP sockets.
  • -l shows only listening sockets (servers waiting for connections).
  • -n displays port numbers instead of service names.
  • -p shows which process owns each socket.
  • Filter the output to show only your server by running:
ss -tlnp | grep 8080

You should see a single line confirming that python3 is listening on port 8080.

What does grep do here?

The pipe (|) sends the full output of ss into grep, which filters it down to only lines containing 8080. This is a common pattern for finding exactly what you need in long command output.

Don't see python3 on port 8080?

Switch back to your first terminal and check that the server is still running. If it crashed or you closed that terminal, restart it with cd ~/my-server && python3 -m http.server 8080.

Help me debug this

Test connectivity with curl

Browsers are one way to test a server. The curl command lets you make HTTP requests directly from the terminal, which is essential for debugging on remote servers that have no browser.

  • In your second terminal, fetch your web page by running:
curl http://localhost:8080

You should see your raw HTML content printed directly in the terminal.

  • View only the HTTP response headers by running:
curl -I http://localhost:8080

You should see header lines including the status code, content type, and server information.

What is the difference between these two curl commands?

curl http://localhost:8080 downloads the full response body (your HTML). This is what a browser would display.

curl -I http://localhost:8080 sends a HEAD request and shows only the response headers. Headers tell you the status code, content length, and server type without downloading the page content.

  • Switch back to your first terminal (where the server is running).
  • Notice the access log lines that appeared. Each request from your browser and from curl is recorded with a timestamp and status code.

Why do access logs matter?

Access logs are your first line of evidence when debugging a web server. They show you exactly who connected, when, and what response code they received.

If a request never appears in the logs, the problem is in the network layer, not the application.

Curl showing connection refused?

This means nothing is listening on port 8080. Your server likely stopped. Restart it in the first terminal with cd ~/my-server && python3 -m http.server 8080.

Help me debug this

Your server is running, verified, and responding to requests. Next up, you will dig deeper into your network configuration to understand how your machine communicates with the outside world.

Diagnose Your Network

In the last step, you started your Python server and used ss and curl to confirm it was running and responding to requests.

Now you'll go deeper into your machine's network stack. You'll inspect your IP address, test whether your machine can reach the outside world, and build a mental model for diagnosing connectivity issues.

In this step, get ready to:
  • Inspect your network interfaces and IP addresses.
  • Test connectivity using ping.
  • Apply a four-step troubleshooting mental model.
Inspect your network interfaces and IP address

Every machine on a network has at least one network interface. Each interface gets an IP address that identifies it on the network.

  • Switch to your second Ubuntu terminal and display all your network interfaces by running this command:
ip addr show

What should I see?

You'll see a list of network interfaces. Look for two key ones:

  • lo (loopback) with IP 127.0.0.1. This is the interface your machine uses to talk to itself.
  • eth0 with an IP in the 172.x.x.x range. This is the virtual network interface WSL uses to connect to your Windows host.
  • View your routing table by running:
ip route show
  • Filter for just the default route by running:
ip route show default

What is a routing table?

A routing table tells your machine where to send network traffic. The "default" route is the gateway your machine uses when it doesn't have a more specific path to a destination.

Think of it like a postal system. The default gateway is the main post office that forwards your mail to anywhere you don't have a direct connection to.

You should see a line starting with default via followed by an IP address. That IP is your gateway.

Not seeing eth0?

Your interface might be named differently (like eth1 or enp0s1). Look for any interface that is NOT lo and has an inet address assigned.

Help me find my network interface

Test connectivity with ping

The ping command sends small packets to a target and measures whether they come back. It tells you if your machine can reach another machine on the network.

  • Ping yourself (the loopback interface) to confirm basic networking works by running:
ping -c 4 127.0.0.1

You should see 4 packets transmitted and 4 received, with 0% packet loss.

  • Test internet connectivity by pinging Google's DNS server:
ping -c 4 8.8.8.8

You should again see 0% packet loss. This confirms your machine can reach the internet.

  • Test DNS resolution AND connectivity together by running:
ping -c 4 google.com

Why three different pings?

Each ping tests a different layer of your network:

  • 127.0.0.1 tests your local network stack (is networking running at all?).
  • 8.8.8.8 tests internet connectivity (can I reach an external IP?).
  • google.com tests DNS resolution (can my machine translate domain names to IPs?).

If 8.8.8.8 works but google.com fails, the problem is DNS, not your network connection.

Seeing packet loss on 8.8.8.8?

If you see 100% packet loss, your WSL instance may not have internet access. Try restarting WSL from PowerShell with wsl --shutdown then reopening Ubuntu.

Help me fix internet connectivity in WSL

Apply the troubleshooting mental model

Now you'll put everything together into a structured debugging flow. This is the same approach engineers use when a service stops responding in production.

The four-step network debugging flow

  • Step 1: ip addr show (Do I have an IP address?)
  • Step 2: ping (Can I reach the target?)
  • Step 3: ss -tlnp (Is the service listening?)
  • Step 4: curl (Is the application responding?)

If any step fails, you've narrowed the problem to that layer. You never need to guess.

Let's simulate a real-world "service is down" scenario and walk through this model.

  • Switch to your first terminal (where the server is running).
  • Stop the server by pressing Ctrl+C.

The server is now down. Imagine you're a colleague who just noticed the website isn't loading. Let's debug it.

  • Switch back to your second terminal and attempt to reach the server by running:
curl http://localhost:8080

You should see an error containing "Connection refused". This tells you the host is reachable, but nothing is listening on that port.

  • Confirm nothing is listening on port 8080 by running:
ss -tlnp | grep 8080

This time, the command returns nothing. No output means no process is listening on port 8080.

What did the mental model tell us?

You just walked through the debugging flow backwards:

  • curl told you the application is NOT responding (Connection refused).
  • ss -tlnp told you the service is NOT listening.

The fix is clear: restart the service. This is exactly how you debug "service is down" issues in the real world.

Curl still returns HTML instead of an error?

Make sure you pressed Ctrl+C in the correct terminal (the one showing "Serving HTTP on 0.0.0.0 port 8080"). If the server is still running, curl will still get a response.

Help me stop the server correctly

You now have a repeatable framework for debugging any connectivity issue. Next up, you'll put these skills to use in a Secret Mission that locks down your server with a firewall.

Secret mission

Block and Allow Traffic with UFW

You have a running web server and know how to inspect it. But how do production servers decide which traffic gets in and which gets rejected? In this secret mission, you will configure a real Linux firewall to control access to your server. This is exactly how engineers secure services in the real world.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally via WSL, so there are no ongoing cloud costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • The Python HTTP server process (may still be running in your first terminal).
  • The ~/my-server project directory with index.html.
  • The UFW package (installed during the Secret Mission).
  • WSL with Ubuntu 24.04 (installed in Step 1).

Keep everything running

No action needed. Choose this if you plan to keep exploring Linux commands or building on this project.

  • Your WSL environment persists between reboots. You can reopen Ubuntu from the Start menu anytime.
  • The HTTP server stops automatically when you close the terminal window.
  • UFW remains installed but disabled (you ran sudo ufw disable at the end of the Secret Mission).

Pause - I'll come back to this later

Shut down running processes to free up memory. Your files and WSL environment stay intact for next time.

  • If your HTTP server is still running, switch to that terminal and press Ctrl+C to stop it.
  • Close both Ubuntu terminal windows.
  • WSL shuts down automatically after all terminal sessions close. Your files in ~/my-server will still be there when you reopen Ubuntu.

Delete - I don't want to use this again

Remove all project resources completely. Follow these steps in order.

  • If your HTTP server is still running, press Ctrl+C in that terminal to stop it.
  • Remove the project directory by running this in your Ubuntu terminal:
rm -rf ~/my-server
  • Remove the UFW package by running:
sudo apt remove ufw -y
  • If you want to remove WSL and Ubuntu entirely, open Settings on Windows.
  • Select Apps, then Installed apps.
  • Search for Ubuntu, click the three-dot menu, and select Uninstall.
  • This removes the Ubuntu distribution and all files inside it (including anything in your home directory).

Nice Work!

Nice Work!

You just set up a real Linux environment, deployed a web server, and diagnosed network traffic using the same tools professionals rely on every day.

You've learned how to:

  • Install and configure a full Linux environment (Ubuntu via WSL) on your Windows machine.
  • Create a website and serve it using Python's built-in HTTP server, then inspect listening ports and running processes with ss.
  • Apply a structured network troubleshooting mental model using ip, ping, ss, and curl to diagnose connectivity issues.
  • Secret Mission: Configured UFW firewall rules to block and allow traffic to your server.

Ready to quiz yourself?