Build and Secure a Linux Web Server

Configure nginx, diagnose networking, and secure a Linux server with iptables.

Introduction

30 Second Summary

Every website you visit runs on a server. Behind that server is a Linux machine configured with networking rules and security settings that keep it safe and accessible.

In this project, you will build and secure a Linux web server from scratch using Ubuntu, nginx, and iptables. You will create a custom website, diagnose its network path using real networking tools, and lock it down with firewall rules.

What You'll Build

A fully configured Linux web server protected by a firewall, running on Ubuntu inside WSL2 on your Windows machine.

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

  • A running nginx web server with a custom HTML page accessible from your Windows browser.
  • A network diagnostic report showing IP addresses, open ports, DNS resolution, and HTTP traffic flow.
  • A configured iptables firewall that allows HTTP traffic on port 80 and blocks everything else.
  • Secret Mission: Write a Bash script that automates the entire server setup from scratch, simulating an AWS EC2 User Data script.

Are there any prerequisites?

You need a Windows 10 machine with WSL already installed. If you don't have WSL set up yet, follow Microsoft's official installation guide before starting this project.

Verify Your Environment and Install Tools

You're about to build a Linux web server from scratch, configure its network, and protect it with a firewall. Before any of that, you need a working Linux environment with the right tools installed.

In this step, you'll confirm your WSL2 setup is ready, install a web server and networking utilities, and get familiar with how Linux organizes its filesystem.

In this step, get ready to:
  • Verify that WSL2 is running with Ubuntu.
  • Install nginx and networking tools.
  • Explore the Linux filesystem and user identity.
Verify WSL2 and Ubuntu are working

WSL2 gives you a full Linux kernel running inside Windows. You need to confirm your Ubuntu distro is running on version 2 (not version 1) before proceeding.

  • Press the Windows key to open your search bar.
  • Type PowerShell and press Enter to open it.
  • Check your WSL version by running this command:
wsl -l -v

You should see a table listing your Ubuntu distribution and its version number.

✔️ I see VERSION 2 next to Ubuntu

Your WSL2 environment is ready. Continue to the next instruction below.

ⓧ I see VERSION 1 next to Ubuntu

Your distro is running on the older WSL1. You need to upgrade it to version 2.

  • Upgrade your distro by running this command in PowerShell:
wsl --set-version Ubuntu 2

This may take a few minutes. Once it finishes, run wsl -l -v again to confirm VERSION 2 appears.

ⓧ No distro listed or command not found

WSL or Ubuntu may not be installed correctly.

  • Install WSL with the default Ubuntu distro by running this command in PowerShell:
wsl --install

Restart your computer when prompted. After restart, open PowerShell again and run wsl -l -v to confirm Ubuntu appears with VERSION 2.

  • Open your Ubuntu terminal by pressing the Windows key, typing Ubuntu, and pressing Enter.
  • Confirm your Ubuntu version by running this command:
lsb_release -a

You should see details about your Ubuntu release, including the version number and codename.

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

What does this command do?

This is actually two commands joined by &&.

  • sudo apt update refreshes the list of available packages from Ubuntu's servers.
  • sudo apt upgrade -y installs the latest versions of all your existing packages. The -y flag automatically confirms the upgrade.

This may take a minute or two depending on how many packages need updating.

Seeing permission denied or network errors?

  • Make sure you included sudo at the start. Without it, Linux blocks the operation because package management requires administrator privileges.
  • If you see a network error, check your Windows internet connection. WSL2 shares your Windows network.

Help me troubleshoot my package update error in WSL2.

Install the tools needed for this project

You need a web server, networking utilities, and a text editor. One command installs all of them.

  • Install all project tools by running this command:
sudo apt install nginx curl dnsutils iproute2 nano -y

What is each tool for?

  • nginx is the web server that will host your custom website.
  • curl makes HTTP requests from the command line so you can test your server.
  • dnsutils provides nslookup for testing DNS resolution.
  • iproute2 provides the ip command for viewing network configuration.
  • nano is a beginner-friendly text editor with keyboard shortcuts visible on screen.
  • Verify nginx installed correctly by running:
nginx -v
  • Verify curl installed correctly by running:
curl --version
  • Verify nslookup installed correctly by running:
nslookup -version
  • Verify ip installed correctly by running:
ip -V

Each command should print a version number. If all four succeed, your tools are ready.

Command not found after installing?

  • Scroll up in your terminal and check whether the install command showed any errors. Look for lines mentioning an inability to locate a package.
  • Run sudo apt update first, then re-run the install command. Your package list may have been stale.

Help me fix a command not found error after installing packages in Ubuntu.

Understand the Linux filesystem

Before you start building, it helps to know where you are in the filesystem and who you are as a user. These concepts map directly to IAM in AWS, where every action is tied to an identity with specific permissions.

  • See your current location in the filesystem by running:
pwd

This prints your home directory path, something like /home/yourname. This is where your personal files live.

  • View the top-level Linux filesystem by running:
ls /

What are these folders?

Linux organizes everything into a tree starting at / (the root). Key directories include:

  • /home contains user home directories.
  • /etc stores system configuration files (you'll edit nginx config here later).
  • /var holds variable data like web server files and logs.
  • Check your username and user identity by running these commands:
whoami
id

Why does identity matter?

whoami shows your username. id shows your numeric user ID (uid), group ID (gid), and group memberships.

Linux uses these identities to control what files you can read, write, or execute. When you ran sudo earlier, you temporarily elevated to the root user (uid=0) to perform admin tasks. This is the same principle behind AWS IAM roles and permissions.

Your environment is verified and all tools are installed. Next up, you'll launch your web server and create a custom website.

Launch and Configure Your Web Server

You have all the tools installed. Now it's time to put them to work by launching a web server and serving your own custom page.

In this step, you will start nginx, verify it responds to requests, then swap the default page for one you create yourself. This is the equivalent of launching an EC2 instance with a web server in AWS.

In this step, get ready to:
  • Start nginx and confirm it serves a page in your Windows browser.
  • Create a custom HTML page for your site.
  • Configure nginx to serve your custom page instead of the default.
Start nginx and verify it works

Before you can see anything in a browser, the nginx service needs to be running. Starting it tells Linux to begin listening for web requests on port 80.

  • Start the nginx service by running this command:
sudo service nginx start
  • Confirm nginx is running by checking its status:
sudo service nginx status

You should see a message confirming that nginx is running in your terminal output.

Seeing a different message?

If the status says nginx is not running, try running sudo service nginx start again. Make sure you typed sudo before the command. nginx requires root permissions to start.

Help me troubleshoot nginx not starting

  • Switch to your Windows browser.
  • Type http://localhost in the address bar and press Enter.

You should see the default nginx welcome page. This confirms your Linux web server is responding to requests from Windows.

How does localhost reach WSL2?

WSL2 uses NAT networking with localhost forwarding. Services running inside your Ubuntu environment are automatically accessible from Windows via http://localhost. You do not need to configure any port forwarding.

Create a custom HTML page

The default welcome page proves nginx works. Now you will replace it with your own page to simulate deploying a real website.

  • Create a new directory for your site by running this command:
sudo mkdir -p /var/www/mycloud
  • Confirm the directory was created by running:
ls /var/www/

You should see mycloud listed alongside the existing html directory.

  • Open a new file in nano by running:
sudo nano /var/www/mycloud/index.html

What is nano?

Nano is a terminal-based text editor. It shows keyboard shortcuts at the bottom of the screen, making it beginner-friendly. The ^ symbol means the Ctrl key.

  • Type or paste the following HTML into the nano editor:
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Cloud Engineering Lab</title>
</head>
<body>
    <h1>My Cloud Engineering Lab</h1>
    <p>Este servidor fue configurado manualmente en Linux.</p>
    <p>Habilidades practicadas: Linux CLI, nginx, networking, iptables.</p>
    <p>Proximo paso: desplegar esto en AWS EC2.</p>
</body>
</html>

What does this page contain?

This is a simple HTML page that identifies your server and lists the skills you are practising. It serves as proof that you configured the server yourself.

  • Save the file by pressing Ctrl+O, then press Enter to confirm the filename.
  • Exit nano by pressing Ctrl+X.
  • Verify the file was saved by running:
ls /var/www/mycloud/

You should see index.html listed in the output.

Don't see index.html?

Make sure you pressed Ctrl+O (the letter O, not zero) to save before pressing Ctrl+X to exit. If the file is missing, run the sudo nano command again and repeat the save steps.

Help me figure out why my file wasn't saved

✔️ Awesome, I've got everything!

Great. Make sure the file is saved before moving on.

ⓧ I'd like to double check the full code

Your /var/www/mycloud/index.html file should look exactly like this:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Cloud Engineering Lab</title>
</head>
<body>
    <h1>My Cloud Engineering Lab</h1>
    <p>Este servidor fue configurado manualmente en Linux.</p>
    <p>Habilidades practicadas: Linux CLI, nginx, networking, iptables.</p>
    <p>Proximo paso: desplegar esto en AWS EC2.</p>
</body>
</html>
Configure nginx to serve your custom page

Your custom HTML file exists, but nginx is still pointing at the default directory. You need to update the nginx configuration so it knows where to find your new page.

  • Open the default nginx site configuration file by running:
sudo nano /etc/nginx/sites-available/default
  • Scroll down until you find this line:
root /var/www/html;
  • Change /var/www/html to /var/www/mycloud, so the line now looks like this:
root /var/www/mycloud;

What does the root directive do?

The root directive tells nginx which directory to look in when someone requests a page. By changing it from /var/www/html to /var/www/mycloud, nginx will now serve your custom index.html instead of the default welcome page.

  • Save the file by pressing Ctrl+O, then Enter.
  • Exit nano by pressing Ctrl+X.
  • Restart nginx so it picks up the new configuration:
sudo service nginx restart
  • Switch to your Windows browser.
  • Visit http://localhost again (refresh the page if it is already open).

You should now see your custom page with the heading "My Cloud Engineering Lab" instead of the default nginx welcome page.

Still seeing the default welcome page?

Make sure you saved the config file in nano before exiting. Confirm you changed the correct line. The root directive should now point to /var/www/mycloud with a semicolon at the end.

If you edited the wrong line, reopen the file and look for the root line inside the server block. After correcting it, run sudo service nginx restart again.

Help me fix my nginx configuration

Your web server is live and serving your own content. Next up, you will use networking tools to trace exactly how traffic travels from your browser to this server.

Diagnose Your Network

Your web server is live and serving your custom page. But how does traffic actually get from your Windows browser to nginx inside WSL2?

In this step, you will use Linux networking tools to trace the path that HTTP traffic takes. You will inspect network interfaces, open ports, and DNS resolution. These are the same diagnostic skills cloud engineers use to troubleshoot VPC connectivity and security group rules in AWS.

In this step, get ready to:
  • Explore your network interfaces and routing table.
  • Inspect open ports and verify nginx is listening on port 80.
  • Test DNS resolution to understand how domain names become IP addresses.
Explore your network configuration

Every machine on a network has at least one network interface with an IP address. In AWS, this maps to the private IP address that an EC2 instance receives from its VPC subnet.

  • View your network interfaces and IP addresses by running this command:
ip addr show

You should see an interface called eth0 with an IP address in the 172.x.x.x range. This is your WSL2 instance's private IP.

What does this output mean?

The eth0 interface is your primary network adapter. The inet line shows your IPv4 address and subnet mask (like 172.29.43.111/20).

In AWS, every EC2 instance gets a private IP from its VPC subnet in exactly the same way. Understanding this output helps you debug connectivity issues in the cloud.

  • View your routing table by running this command:
ip route show

You should see a default via line showing your gateway IP. This is the path traffic takes to leave your network.

How does this relate to AWS?

A routing table tells the operating system where to send packets. In AWS, VPC route tables serve the same purpose. They direct traffic to internet gateways, NAT gateways, or other subnets.

  • Confirm your user identity by running these commands:
whoami
id

You should see your Linux username and group memberships. In AWS, this maps to IAM identities and permissions.

Inspect open ports and connections

A listening port means a service is ready to accept connections. Knowing which ports are open is essential for securing any server.

  • List all TCP ports that are currently listening by running this command:
ss -tlnp

You should see a line showing 0.0.0.0:80 with nginx in the Process column. This confirms nginx is listening on port 80.

What do the flags mean?

  • -t shows only TCP sockets.
  • -l shows only listening sockets (services waiting for connections).
  • -n shows port numbers instead of service names.
  • -p shows the process using each port.
  • Make an HTTP request to your server and view the response headers by running this command:
curl -I http://localhost

You should see HTTP/1.1 200 OK as the first line. The -I flag tells curl to show only the response headers, not the page content.

What is a 200 status code?

HTTP status code 200 means the server received your request and responded successfully. Other common codes include 404 (not found) and 500 (server error). When you troubleshoot a web app in production, headers are the first thing you check.

Not seeing nginx on port 80?

If ss -tlnp does not show nginx, your server may have stopped. Restart it with sudo service nginx start and run ss -tlnp again.

If you see "permission denied" when running ss -tlnp, try sudo ss -tlnp to see process details.

help me troubleshoot why nginx is not showing on port 80 in my ss output

Test DNS resolution

DNS translates human-readable domain names (like google.com) into IP addresses that computers use to route traffic. This is the same mechanism behind AWS Route 53 and VPC DNS settings.

  • Resolve a public domain name to an IP address by running this command:
nslookup google.com

You should see one or more IP addresses listed under Address. These are the servers that handle requests for google.com.

  • Now resolve localhost to see local DNS resolution by running this command:
nslookup localhost

You should see 127.0.0.1 in the output. This is the loopback address that always points back to your own machine.

Why does DNS matter for cloud engineering?

Every time you visit a website or call an API, DNS resolves the domain name to an IP address first. AWS Route 53 is Amazon's DNS service. It routes users to the correct server based on domain name lookups.

Understanding DNS resolution helps you debug "connection refused" and "host not found" errors in production. These are among the most common issues cloud engineers troubleshoot.

nslookup timing out?

If nslookup google.com hangs or times out, your WSL2 instance may not have internet access. Try ping 8.8.8.8 to test raw connectivity. If that fails, restart WSL2 from PowerShell with wsl --shutdown and reopen Ubuntu.

help me fix DNS resolution issues in WSL2

You now have a clear picture of how traffic flows from your browser to nginx. Next up, you will lock down your server with firewall rules that control exactly which traffic is allowed in.

Secure Your Server with iptables

In the previous step, you used networking tools to see exactly how traffic flows to your server. You confirmed nginx is listening on port 80 and that DNS resolution works.

There is a problem though. Right now your server accepts connections on every single port. Anyone could try to reach any service running on your machine. In this step, you will lock it down with firewall rules using iptables, allowing only the traffic your web server needs.

In this step, get ready to:
  • View your server's current firewall rules and confirm everything is wide open.
  • Create iptables rules that allow only HTTP traffic and block everything else.
  • Test that your firewall works by confirming allowed traffic passes and blocked traffic fails.
View current firewall rules and understand the default policy

Before adding any restrictions, you need to see what rules your server currently has. This gives you a baseline to compare against after you lock things down.

  • List all current firewall rules by running this command:
sudo iptables -L -v

What does this output mean?

You should see three chains: INPUT, FORWARD, and OUTPUT. Each chain has a policy of ACCEPT, meaning all traffic is allowed through by default.

This is equivalent to an AWS Security Group that allows all inbound and outbound traffic. Not secure at all.

  • Now view the same rules with line numbers by running:
sudo iptables -L -v --line-numbers

Why line numbers?

Line numbers help you identify the position of each rule. iptables processes rules from top to bottom and stops at the first match. Order matters.

Create firewall rules that allow only HTTP and block everything else

Now you will build a ruleset that works like a whitelist. Only the traffic you explicitly allow will get through. Everything else gets silently dropped.

Why does rule order matter?

iptables evaluates rules from top to bottom. The first matching rule wins. If you put the DROP rule first, it would block everything before reaching any ACCEPT rules.

This is the same concept behind AWS NACLs, which also process rules by number from lowest to highest.

  • Add the four firewall rules in this exact order by running these commands:
# Rule 1: Allow all traffic on the loopback interface (localhost)
sudo iptables -A INPUT -i lo -j ACCEPT

# Rule 2: Allow responses to connections YOU initiated
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Rule 3: Allow new HTTP connections on port 80
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT

# Rule 4: Drop everything else silently
sudo iptables -A INPUT -j DROP

What does each rule do?

  • -i lo -j ACCEPT allows traffic on the loopback interface. This keeps local tools like curl http://localhost working from inside WSL.
  • --state ESTABLISHED,RELATED -j ACCEPT allows return traffic for connections your server already started. Without this, things like apt update would break because responses would be dropped.
  • --dport 80 -j ACCEPT allows new incoming TCP connections on port 80. This is your web server port.
  • -j DROP silently discards any packet that did not match the rules above. The sender gets no response and eventually times out.
  • Verify your rules are in place by running:
sudo iptables -L -v --line-numbers

You should see 4 rules listed in the INPUT chain, in the exact order you entered them.

Rules not showing up?

  • Make sure you typed each command with sudo at the beginning. Without sudo, the commands fail silently.
  • Check you typed -A INPUT (capital A, capital INPUT). Lowercase will cause errors.
  • If you accidentally added rules in the wrong order, flush everything with sudo iptables -F and start again from Rule 1.

help me troubleshoot my iptables rules

Test your firewall

Rules mean nothing until you prove they work. You will now test that allowed traffic passes and blocked traffic fails.

  • Switch to your Windows browser and visit http://localhost.
  • Confirm your custom "My Cloud Engineering Lab" page still loads.

Port 80 traffic from Windows reaches your server over the network interface and matches Rule 3.

  • Return to your Ubuntu terminal and test loopback access by running:
curl -I http://localhost

You should see HTTP/1.1 200 OK in the response headers. This works because Rule 1 allows all loopback traffic.

  • Now test a port that should be blocked by running:
curl http://localhost:8080

You should see a connection refused message, confirming that no service is accessible on that port. From the Windows side, a connection attempt to a non-allowed port over the network interface would be silently dropped by Rule 4, causing a timeout instead.

How does this map to AWS?

The ruleset you just created is a simplified version of an AWS Security Group. In AWS, you create inbound rules that allow specific ports (like 80 for HTTP or 443 for HTTPS) and deny everything else by default.

The key difference is that AWS Security Groups are stateful (they automatically allow return traffic), while iptables requires you to explicitly allow ESTABLISHED/RELATED connections with Rule 2.

Reset if something breaks

If your firewall rules accidentally lock you out or cause problems, flush all rules by running:

sudo iptables -F

This removes every rule and returns all chains to their default ACCEPT policy, giving you a clean slate to start over.

Page not loading in your Windows browser?

  • Make sure nginx is still running with sudo service nginx status. If it stopped, restart it with sudo service nginx restart.
  • Verify Rule 3 appears in your chain by running sudo iptables -L --line-numbers. You should see ACCEPT for tcp dpt:http on line 3.
  • If your rules are in the wrong order, run sudo iptables -F to flush them all and re-enter the four commands from the beginning of this substep.

help me fix my iptables firewall so localhost loads in my browser

Your server is now locked down. Only HTTP traffic on port 80 can reach your web server, and everything else is silently dropped. Next up, you will build a Bash script that reports your entire server status in one command.

Build a Network Status Script

Your server is now locked down with firewall rules that only allow HTTP traffic on port 80. You ran a series of commands in Steps 3 and 4 to check your server's health. Imagine running those same commands every time you need a status update.

Cloud engineers solve this problem with Bash scripts that bundle diagnostic commands into one reusable tool. In this step, you will write a script that displays your server's network status in a single command.

In this step, get ready to:
  • Create a Bash script file with the correct shebang line.
  • Write diagnostic commands that report IP, ports, nginx, firewall, and HTTP status.
  • Make the script executable and run it to see a full server status report.
Create a Bash script file

A Bash script is a text file containing a sequence of commands that Linux executes in order. Instead of typing five separate commands every time you check your server, you run one script.

  • Open a new file in nano by running this command:
nano ~/server-status.sh

This opens the nano text editor with a blank file called server-status.sh in your home directory.

What is a shebang?

The first line of a Bash script must be #!/bin/bash. This is called a shebang. It tells Linux which program to use when running the file.

Without it, Linux does not know the file contains Bash commands and may fail to execute it.

Write the script content
  • Type the following script into nano:
#!/bin/bash

echo "=================================="
echo "   SERVER STATUS REPORT"
echo "=================================="

echo ""
echo "--- IP Address ---"
ip -4 addr show eth0 | grep inet

echo ""
echo "--- Listening Ports ---"
ss -tlnp

echo ""
echo "--- Nginx Status ---"
service nginx status

echo ""
echo "--- Firewall Rules ---"
sudo iptables -L --line-numbers

echo ""
echo "--- HTTP Test ---"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost)
echo "HTTP Response Code: $HTTP_CODE"

if [ "$HTTP_CODE" = "200" ]; then
    echo "Server is working correctly!"
else
    echo "WARNING: Server returned unexpected code."
fi

echo ""
echo "=================================="
echo "   END OF REPORT"
echo "=================================="

What does this script do?

This script runs five diagnostic checks and displays the results in a single report:

  • ip -4 addr show eth0 | grep inet shows your server's IPv4 address on the eth0 interface.
  • ss -tlnp lists all TCP ports that are listening, including nginx on port 80.
  • service nginx status checks whether nginx is currently running.
  • sudo iptables -L --line-numbers displays your firewall rules with their order numbers.
  • The curl line makes an HTTP request to localhost and stores only the status code in the variable HTTP_CODE. The if block then checks whether the code equals 200 (success) or something else.

Why use a variable and conditional?

Storing the HTTP code in HTTP_CODE lets you test the value with an if statement. This is a pattern cloud engineers use in health-check scripts to take automated action when a service fails.

  • Save the file by pressing Ctrl+O, then press Enter to confirm.
  • Exit nano by pressing Ctrl+X.

✔️ Awesome, I've got everything!

Great. Double check you saved your file before continuing.

ⓧ I'd like to double check the full code

Here is the complete server-status.sh file for comparison:

#!/bin/bash

echo "=================================="
echo "   SERVER STATUS REPORT"
echo "=================================="

echo ""
echo "--- IP Address ---"
ip -4 addr show eth0 | grep inet

echo ""
echo "--- Listening Ports ---"
ss -tlnp

echo ""
echo "--- Nginx Status ---"
service nginx status

echo ""
echo "--- Firewall Rules ---"
sudo iptables -L --line-numbers

echo ""
echo "--- HTTP Test ---"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost)
echo "HTTP Response Code: $HTTP_CODE"

if [ "$HTTP_CODE" = "200" ]; then
    echo "Server is working correctly!"
else
    echo "WARNING: Server returned unexpected code."
fi

echo ""
echo "=================================="
echo "   END OF REPORT"
echo "=================================="
Make the script executable and run it

Linux does not let you run a text file as a program by default. You need to add execute permission first.

  • Make your script executable by running this command:
chmod +x ~/server-status.sh

What does chmod +x do?

chmod changes file permissions. The +x flag adds execute permission, which tells Linux this file can be run as a program. This is the same permission model that controls access to files on EC2 instances.

  • Run your script by executing this command:
~/server-status.sh
  • You should see a formatted report with five sections: your IP address, listening ports (including :80 for nginx), nginx running status, your four iptables rules, and HTTP Response Code: 200 followed by Server is working correctly!.

Script not running?

  • If you see Permission denied, you may have skipped the chmod +x step. Run it again and retry.
  • If you see No such file or directory, check that the file is saved in your home directory. Run ls ~/server-status.sh to verify it exists.
  • If the HTTP test shows a code other than 200, confirm nginx is still running with sudo service nginx status. Restart it with sudo service nginx restart if needed.

Why is my server-status.sh script failing?

Secret mission

Automate Everything with a Provisioning Script

You configured your server manually, one command at a time. But what if you had to set up 50 servers? In the cloud, engineers use provisioning scripts to automate everything from a single file. Your mission: write a Bash script that rebuilds your entire server from scratch, just like an AWS EC2 User Data script.

Clean Up Your Resources

Clean Up Your Resources

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

Resources you used:

  • nginx web server (running as a service on port 80).
  • Custom website files at /var/www/mycloud/.
  • iptables firewall rules (stored in memory).
  • Bash scripts: ~/server-status.sh and ~/provision-server.sh.
  • nginx package (installed via apt).

Keep everything running

No action needed. Choose this if you are still actively building or want to keep practicing Linux commands.

  • Your nginx server continues running on port 80.
  • Your iptables rules remain active until WSL2 shuts down or restarts.
  • Your scripts remain in your home directory ready to run.

Pause - I'll come back to this later

Stop running processes to free up memory, but keep your files and configuration in place.

  • Stop nginx by running:
sudo service nginx stop
  • Flush your iptables rules by running:
sudo iptables -F

Your files and scripts stay on disk. When you come back, run ~/provision-server.sh to rebuild everything in one command.

Delete - I don't want to use this again

Remove all project resources and start fresh.

  • Stop nginx and flush firewall rules by running:
sudo service nginx stop
sudo iptables -F
  • Remove your custom website files by running:
sudo rm -rf /var/www/mycloud
  • Remove your Bash scripts by running:
rm ~/server-status.sh ~/provision-server.sh
  • Uninstall nginx and clean up unused dependencies by running:
sudo apt purge nginx -y
sudo apt autoremove -y
  • Verify nginx is removed by running:
nginx -v

You should see a "command not found" message confirming nginx has been removed.

Nice Work!

Nice Work!

You just built and secured a Linux web server from scratch. These are the same foundational skills that cloud engineers use every day to manage EC2 instances, configure VPCs, and protect infrastructure with Security Groups.

You've learned how to:

  • Configure nginx on Linux to serve a custom website accessible from your Windows browser.
  • Diagnose network traffic using Linux networking tools like ip, ss, curl, and nslookup to understand how traffic flows between your browser and your server.
  • Secure a server with iptables firewall rules that mirror how AWS Security Groups control inbound and outbound traffic.
  • Secret Mission: Automated the entire server provisioning with a Bash script, just like AWS EC2 User Data bootstraps instances from scratch.

Ready to quiz yourself?