Build a Local Ubuntu Home Lab

Host a website on Ubuntu and administer it remotely from Windows.

Introduction

30 Second Summary

A computer on your desk can help other devices in your home without sending anything to the cloud. The trick is giving it a useful job that you can reach from another room.

In this project, you will turn your Ubuntu PC into a local server that hosts a custom home lab webpage. You will also administer Ubuntu remotely from Windows with OpenSSH.

What You'll Build

Your custom home lab page loads in Microsoft Edge on your Windows PC when you enter the Ubuntu PC's LAN address.

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

  • A custom homepage built with HTML and CSS whose source you can inspect directly in the Ubuntu terminal.
  • A persistent LAN-hosted site that you can reload from Microsoft Edge while nginx runs in the background.
  • A secure remote terminal where Windows PowerShell controls Ubuntu through OpenSSH.
  • Secret Mission: Add a status page linked from the homepage. Its Home link takes you back.

Are there any prerequisites?

You need an Ubuntu PC plus a Windows PC on the same trusted home network. Administrator access is required on both.

Before We Start

Defining the finished home lab now gives every later command a clear purpose: Ubuntu will host a custom static website that Windows opens through its LAN address in Microsoft Edge. Windows PowerShell will administer Ubuntu remotely through SSH.

Prepare Your Two-Computer Workbench

Your Ubuntu PC will soon serve a webpage to your Windows PC. A missing local command would interrupt that first visible result.

This step verifies Python 3 on Ubuntu. It also confirms that GNU nano opens correctly.

You will finish by creating a dedicated workspace for the website files. Both computers stay connected to the same trusted home network.

In this step, get ready to:
  • Confirm that Python 3 is available on Ubuntu.
  • Confirm that GNU nano opens to its editor screen.
  • Create the ~/python/helloworld workspace.
Verify Python 3

Python 3 powers the temporary web server you will use for the first local test. Checking it now keeps that first result focused on your webpage.

  • Start a terminal on Ubuntu from your application launcher.
  • Ask Ubuntu to report its Python 3 version by running this command:
python3 --version

What does this command check?

The command asks Ubuntu which Python 3 version is available. Output beginning with Python 3 confirms that the interpreter is ready.

✔️ The output starts with Python 3

That check is complete. Python 3 is ready to serve your first local webpage.

ⓧ Command not found

Installing the missing package changes only your Ubuntu PC. Ubuntu may request administrator approval before it continues.

  • Prepare to enter your Ubuntu password if the terminal requests administrator access.
  • Install Ubuntu's Python 3 environment by running this command:
sudo apt install -y python3-full

What does this command do?

The command installs Ubuntu's Python 3 environment from the system repositories. The -y option approves the package manager's confirmation automatically.

  • Repeat the version check by running this command:
python3 --version

What should you see?

The output should now begin with Python 3. This confirms that Ubuntu can find the interpreter.

Still unable to run Python 3?

Look for a package or network problem in the installation output. Help me diagnose why Python 3 is unavailable on Ubuntu.

Practise using GNU nano

GNU nano is a text editor that runs inside the terminal. Its shortcut bar shows the key combinations used to save files or leave the editor.

  • Start GNU nano with an empty editing area by running this command:
nano

What should open?

GNU nano fills the terminal with an empty editing area. A shortcut bar appears along the bottom of the screen.

✔️ The GNU nano editor opens

  • Locate the shortcut bar along the bottom of the editor.
  • Press Ctrl+X to exit without saving.

You return to the Ubuntu terminal prompt. GNU nano is ready for the homepage file.

ⓧ Command not found

Installing GNU nano adds the editor to your Ubuntu PC. The package manager may ask for confirmation during the installation.

  • Prepare to enter your Ubuntu password if the terminal requests administrator access.
  • Prepare to approve the package installation if Ubuntu asks for confirmation.
  • Install GNU nano by running this command:
sudo apt install nano

What does this command do?

The command installs the GNU nano package from Ubuntu's system repositories. Administrator access allows the package manager to add the editor.

  • Start the installed editor by running this command:
nano

What should open now?

You should see GNU nano's empty editing area. Its shortcut bar should be visible along the bottom.

  • Locate the shortcut bar along the bottom of the editor.
  • Press Ctrl+X to exit without saving.

Still unable to open GNU nano?

Look for a package or network problem in the installation output. Help me diagnose why GNU nano will not open on Ubuntu.

Create the project workspace

A dedicated directory keeps the website source in one predictable location. Your terminal needs to finish inside that workspace so the next step creates the homepage in the correct place.

Before you run this command, where do you expect the terminal to place you after creating the workspace?

  • Create the ~/python/helloworld workspace and enter it by running this command:
mkdir -p ~/python/helloworld && cd ~/python/helloworld

What does this command do?

  • The mkdir -p section creates the complete workspace path if it does not already exist.
  • The cd section makes ~/python/helloworld the active terminal directory.

The command returns to the terminal prompt without an error. Your terminal is now working inside ~/python/helloworld.

Workspace command failed?

Check that the command contains the complete ~/python/helloworld path. Help me fix the Ubuntu workspace command.

Before the final check, do you expect changing directories to affect Python 3 or GNU nano?

  • Confirm Python 3 from the active workspace by running this command:
python3 --version

What should you see?

You should see output beginning with Python 3. Python 3 remains available inside the new workspace.

  • Launch GNU nano from the active workspace by running this command:
nano

What should you see?

You should see the empty GNU nano editor. The shortcut bar confirms that the editor is ready for the homepage.

  • Confirm that the shortcut bar is visible.
  • Press Ctrl+X to return to the terminal.

Your Ubuntu workbench is ready. Python 3 works inside the website workspace while GNU nano opens successfully for editing.

Final check not matching?

Confirm that the workspace command completed before repeating both tool checks. Help me verify my Ubuntu workbench state.

Your tools are verified inside a dedicated workspace. Next, you will build the home lab page and serve it locally from Ubuntu.

Build and Serve Your First Page Locally

Your Ubuntu workbench is ready after the tool checks from the previous step. The next goal is a styled homepage that your PC can return on request.

A temporary Python HTTP server gives you an immediate local result. Binding it to localhost keeps this first test on Ubuntu. That limited reach creates the networking problem you will investigate next.

In this step, get ready to:
  • Create a styled home lab page in index.html.
  • Start a temporary Python server from ~/python/helloworld.
  • Request the page from a second Ubuntu terminal.
Create the home lab page

HTML defines the page content. CSS controls how that content looks. Your finished file combines both in one document.

  • Open index.html in GNU nano by running this command from the active Ubuntu terminal:
nano index.html

What does this command do?

The command opens or creates index.html in your active ~/python/helloworld directory. GNU nano keeps the editing controls visible in the shortcut bar.

  • Paste the document opening plus the page-level styles into GNU nano:
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>My Ubuntu Home Lab</title>
  <style>
    :root {
      color-scheme: dark;
      font-family: system-ui, sans-serif;
      background: #111827;
      color: #e5e7eb;
    }

    body {
      margin: 0;
      min-height: 100vh;
      display: grid;
      place-items: center;
      background: linear-gradient(135deg, #111827, #1e3a8a);
    }

What does this code do?

  • The document opening gives the browser the page language plus the information it needs to size the page correctly.
  • The :root selector defines the dark colour palette plus the system font.
  • The body selector fills the screen with a blue gradient. Its grid layout prepares the content for centring.
  • Continue directly below the existing code with the main panel plus the card layout:
    main {
      width: min(760px, 88vw);
      padding: 2.5rem;
      border: 1px solid #60a5fa;
      border-radius: 1rem;
      background: rgba(17, 24, 39, 0.9);
      box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, 0.35);
    }

    h1 {
      margin-top: 0;
      color: #93c5fd;
    }

    .grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      gap: 1rem;
      margin-top: 1.5rem;
    }

    .card {
      padding: 1rem;
      border-radius: 0.75rem;
      background: #1f2937;
    }

How does the layout work?

  • The main selector creates the bordered panel that holds the homepage.
  • The h1 selector gives the main heading a light blue colour.
  • The .grid selector arranges the skill cards into columns that adapt to the available width.
  • The .card selector gives each skill its own dark panel.
  • Complete the remaining card styles plus the footer styles directly below:
    .card h2 {
      margin-top: 0;
      font-size: 1rem;
      color: #bfdbfe;
    }

    footer {
      margin-top: 1.5rem;
      color: #9ca3af;
    }
  </style>
</head>

What do these styles control?

  • The .card h2 selector keeps each card heading compact plus easy to distinguish.
  • The footer selector separates the closing message from the main content.
  • The closing tags finish the style section plus the document head.
  • Finish the document by pasting the visible homepage content directly below:
<body>
  <main>
    <p>Local server online</p>
    <h1>My Ubuntu Home Lab</h1>
    <p>This page is hosted on my Ubuntu PC and opened from my Windows PC.</p>

    <section class="grid" aria-label="Home lab skills">
      <article class="card">
        <h2>Linux</h2>
        <p>I can create files and manage services from the terminal.</p>
      </article>
      <article class="card">
        <h2>Networking</h2>
        <p>I can distinguish localhost from a LAN address.</p>
      </article>
      <article class="card">
        <h2>Web serving</h2>
        <p>I can publish a page with nginx.</p>
      </article>
    </section>

    <footer>Built on Ubuntu and tested from Windows.</footer>
  </main>
</body>
</html>

What appears on the page?

  • The main element contains the page heading plus the description of your two-computer lab.
  • The .grid section contains cards for Linux. It also contains cards for networking plus web serving.
  • The footer records that Ubuntu hosts the page while Windows tests it.
  • Save index.html with Ctrl+O.
  • Exit GNU nano with Ctrl+X.

✔️ Awesome, I've got everything!

Your complete homepage is saved in ~/python/helloworld/index.html.

ⓧ I'd like to double check the full code

  • Compare your saved index.html with this complete reference:
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>My Ubuntu Home Lab</title>
  <style>
    :root {
      color-scheme: dark;
      font-family: system-ui, sans-serif;
      background: #111827;
      color: #e5e7eb;
    }

    body {
      margin: 0;
      min-height: 100vh;
      display: grid;
      place-items: center;
      background: linear-gradient(135deg, #111827, #1e3a8a);
    }

    main {
      width: min(760px, 88vw);
      padding: 2.5rem;
      border: 1px solid #60a5fa;
      border-radius: 1rem;
      background: rgba(17, 24, 39, 0.9);
      box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, 0.35);
    }

    h1 {
      margin-top: 0;
      color: #93c5fd;
    }

    .grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      gap: 1rem;
      margin-top: 1.5rem;
    }

    .card {
      padding: 1rem;
      border-radius: 0.75rem;
      background: #1f2937;
    }

    .card h2 {
      margin-top: 0;
      font-size: 1rem;
      color: #bfdbfe;
    }

    footer {
      margin-top: 1.5rem;
      color: #9ca3af;
    }
  </style>
</head>
<body>
  <main>
    <p>Local server online</p>
    <h1>My Ubuntu Home Lab</h1>
    <p>This page is hosted on my Ubuntu PC and opened from my Windows PC.</p>

    <section class="grid" aria-label="Home lab skills">
      <article class="card">
        <h2>Linux</h2>
        <p>I can create files and manage services from the terminal.</p>
      </article>
      <article class="card">
        <h2>Networking</h2>
        <p>I can distinguish localhost from a LAN address.</p>
      </article>
      <article class="card">
        <h2>Web serving</h2>
        <p>I can publish a page with nginx.</p>
      </article>
    </section>

    <footer>Built on Ubuntu and tested from Windows.</footer>
  </main>
</body>
</html>

File not saved correctly?

Confirm that GNU nano saved the file inside ~/python/helloworld. If the file looks shorter than the reference, paste the missing section before saving again.

If the document still differs, help me compare my HTML with the complete file.

Start the localhost server

An HTTP server waits for requests before returning files. This temporary server serves the contents of ~/python/helloworld on port 8000.

  • Start the localhost-only server from the active workspace by running this command:
python3 -m http.server -b localhost

What does this command do?

  • The python3 command starts Python 3.
  • The -m http.server portion runs Python's basic web server module.
  • The -b localhost portion limits connections to the Ubuntu PC itself.
  • The server uses port 8000 for incoming requests.
  • Leave this terminal open so the server keeps running.

You'll see a startup message in the terminal. The command keeps control of that terminal while the server waits for a request.

Server did not start?

Check that the terminal is still in ~/python/helloworld. A failure mentioning the port can mean another local program is already using port 8000.

If you need help identifying the cause, help me debug my local Python HTTP server.

Request the page from a second terminal

The running server handles one side of the exchange. The second terminal acts as the HTTP client that asks for index.html.

  • Use Ubuntu's app launcher to open a second terminal window.

Before you make the request, what evidence do you expect to see in each terminal?

  • Request the local homepage from the second terminal by running this command:
curl localhost:8000

What just happened?

  • The curl command acts as the HTTP client.
  • The address localhost:8000 points the client to the temporary server on the same Ubuntu PC.
  • The server reads index.html from the workspace before returning its contents.

You'll see the page's HTML in the second terminal. The returned content includes My Ubuntu Home Lab.

  • Switch back to the first Ubuntu terminal to inspect the new request log entry.

You'll see that the Python server recorded the request. Together, the returned HTML plus the log entry prove that the local client reached the server.

Page not returned?

Check that the first terminal is still running the Python server. Confirm that the second terminal requests localhost:8000.

If the returned HTML is missing your heading, compare index.html with the full-code reference. You can also ask for help tracing the local request.

That is your first live home lab result. Ubuntu served your custom page in response to a local request.

Next, you will test the boundary around localhost from the Windows PC. That test reveals why a different server setup is needed for your home network.

Discover the Localhost Boundary

Your custom page already responds to an HTTP request made by the Ubuntu PC. That success proves the server works through localhost on port 8000.

Now you need to test how the same server behaves across your home network. You will identify Ubuntu's LAN address before requesting the page from the Windows PC.

In this step, get ready to:
  • Identify the Ubuntu PC's LAN IPv4 address.
  • Test the page from Windows through the LAN address.
  • Compare the local result with the Windows result.
Find the Ubuntu LAN address

A computer can have several network addresses. The active interface's non-loopback IPv4 address identifies your Ubuntu PC on the home network.

  • List the Ubuntu PC's network interfaces by running this command in the second Ubuntu terminal:
ip addr

What does this command show?

  • The ip addr command lists the network interfaces on your Ubuntu PC.
  • The lo interface uses 127.0.0.1 for the local loopback address.
  • The active interface's inet entry contains the LAN IPv4 address you need.
  • Find the active interface's inet address that is different from 127.0.0.1.
  • Record that address here: <Ubuntu-LAN-IP>.

Can't identify the LAN address?

Ignore the lo interface. Look for the non-loopback inet address under the interface carrying your home network connection.

If several addresses still look possible, help me identify my Ubuntu LAN address.

Test the page from Windows

The local request proved that Ubuntu can reach the server. A request from Microsoft Edge tests whether another computer can reach the same process through Ubuntu's LAN address.

Before the test, consider whether the page will load from Windows.

  • Press the Windows key on the Windows PC to open search.
  • Type Microsoft Edge into the search field.
  • Press Enter to open Microsoft Edge.
  • Click the address bar at the top of Microsoft Edge.
  • Enter http:// followed by <Ubuntu-LAN-IP> followed by :8000.
  • Press Enter to request the page.

Microsoft Edge cannot load the page. This failure is the intended result of the test.

This Failure Reveals the Boundary

The -b localhost option makes the Python server listen only for requests originating through Ubuntu's loopback address. Each computer uses localhost to refer to itself.

The Windows request targets Ubuntu's LAN address. The server is not listening on that address.

Confirm the contrast and stop the server

The Windows result becomes meaningful when the local request still succeeds. One more Ubuntu request confirms that the server stayed healthy throughout the test.

Before the final request, predict whether the homepage HTML will still return on Ubuntu.

  • Request the page again from the second Ubuntu terminal by running this command:
curl localhost:8000

What does this command prove?

The curl localhost:8000 request stays entirely on the Ubuntu PC. A successful response proves that the server is still running through its localhost binding.

You'll see the homepage HTML containing My Ubuntu Home Lab. The server terminal records the successful request.

That's the diagnosis locked in: Ubuntu can reach the page through localhost. The Windows PC cannot reach the listener through the LAN address.

Local request no longer works?

Return to the server terminal from earlier. Confirm that the Python process is still running before repeating the local request.

If the local request still fails, help me debug my localhost server.

  • Switch back to the Ubuntu terminal running the temporary Python server.
  • Press Ctrl+C to interrupt the server.

You'll see the terminal return to its shell prompt. The temporary Python server is now stopped.

You have exposed the localhost boundary with a real two-computer test. Next, you'll give the website a persistent service that can answer requests across your home network.

Publish the Site with nginx

Your custom page already works on Ubuntu through a localhost-only Python server. The Windows test exposed the boundary between localhost traffic and your LAN.

This step replaces the temporary server with nginx. nginx runs as a system service that can serve your homepage across the home network.

In this step, get ready to:
  • Install nginx as an Ubuntu system service.
  • Copy the custom homepage into the nginx web root.
  • Load the persistent site from Windows over the home network.
Install nginx as a system service

A system service runs in the background without depending on an open development-server terminal. That gives your home lab a persistent web server.

Why nginx for this site?

The Python server gave you a fast local preview. nginx fits the finished static site because Ubuntu installs it as a dedicated web service.

Its default configuration serves files with little setup. Your page stays available after the temporary Python process has stopped.

  • Move into your existing ~/python/helloworld workspace by running this command:
cd ~/python/helloworld

What does this command do?

This command returns your terminal to the folder containing index.html. The publishing command later uses that file as its source.

The installer may ask for your Ubuntu administrator password. That password authorizes the package installation on your own PC.

  • Refresh Ubuntu's package list and install nginx by running:
  • Enter your Ubuntu password if prompted.
  • Approve the package installation if Ubuntu asks for confirmation.
sudo apt update && sudo apt install nginx

What does this command do?

  • The first command refreshes Ubuntu's available package information.
  • The second command installs the Ubuntu-managed nginx package.
  • Ubuntu starts the nginx web server after installation.

When the command finishes, you'll return to the shell prompt with nginx installed. The web server is now ready for your custom page.

Could not install nginx?

  • Confirm that the Ubuntu PC can access the internet before retrying the installation.
  • Wait for any other Ubuntu package operation to finish before running the command again.
  • Use help me diagnose my nginx installation with the terminal output.
  • Share the terminal output in the NextWork community if the installation still fails.
Publish the homepage to nginx

Ubuntu's default nginx site serves files from /var/www/html. Copying your homepage there publishes it while preserving the editable source in ~/python/helloworld.

  • Publish index.html to the nginx web root by running:
sudo cp index.html /var/www/html/

What does this command do?

The command copies your custom homepage to /var/www/html/index.html. nginx checks for index.html when someone requests the site's main address.

Your original file remains in ~/python/helloworld. That workspace stays available for future edits.

Before you check, do you expect nginx to remain active without the Python server terminal?

  • Inspect the nginx system service by running:
sudo systemctl status nginx

What does this command show?

The status view reports whether nginx is running as an Ubuntu system service. It also confirms that the service uses the installed nginx configuration.

You should see active (running) in the status output. nginx is now installed as an enabled service that serves files from its web root.

  • Use the exit hint at the bottom of the status view to return to the shell prompt.

Is nginx not active?

Another web server may already be using the HTTP port. Close that server before checking nginx again.

Use help me diagnose the nginx service with the status output if nginx still does not run.

✔️ Awesome, I've got everything!

Your published homepage matches the source file in ~/python/helloworld. Keep that source file for future changes.

ⓧ 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">
  <title>My Ubuntu Home Lab</title>
  <style>
    :root {
      color-scheme: dark;
      font-family: system-ui, sans-serif;
      background: #111827;
      color: #e5e7eb;
    }

    body {
      margin: 0;
      min-height: 100vh;
      display: grid;
      place-items: center;
      background: linear-gradient(135deg, #111827, #1e3a8a);
    }

    main {
      width: min(760px, 88vw);
      padding: 2.5rem;
      border: 1px solid #60a5fa;
      border-radius: 1rem;
      background: rgba(17, 24, 39, 0.9);
      box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, 0.35);
    }

    h1 {
      margin-top: 0;
      color: #93c5fd;
    }

    .grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      gap: 1rem;
      margin-top: 1.5rem;
    }

    .card {
      padding: 1rem;
      border-radius: 0.75rem;
      background: #1f2937;
    }

    .card h2 {
      margin-top: 0;
      font-size: 1rem;
      color: #bfdbfe;
    }

    footer {
      margin-top: 1.5rem;
      color: #9ca3af;
    }
  </style>
</head>
<body>
  <main>
    <p>Local server online</p>
    <h1>My Ubuntu Home Lab</h1>
    <p>This page is hosted on my Ubuntu PC and opened from my Windows PC.</p>

    <section class="grid" aria-label="Home lab skills">
      <article class="card">
        <h2>Linux</h2>
        <p>I can create files and manage services from the terminal.</p>
      </article>
      <article class="card">
        <h2>Networking</h2>
        <p>I can distinguish localhost from a LAN address.</p>
      </article>
      <article class="card">
        <h2>Web serving</h2>
        <p>I can publish a page with nginx.</p>
      </article>
    </section>

    <footer>Built on Ubuntu and tested from Windows.</footer>
  </main>
</body>
</html>

What should this file contain?

This is the complete homepage copied into the nginx web root. Its heading identifies the server while its cards describe the home lab skills you are demonstrating.

Open nginx to the home network

The Ubuntu firewall, UFW, may filter incoming traffic. If it is active, your nginx site needs an allow rule for HTTP traffic on port 80.

Keep this site on your trusted home network. Router port forwarding is outside this local lab.

  • Check the Ubuntu firewall state by running:
sudo ufw status

What does this command show?

The command reports whether UFW is active. Your next action depends on the state shown in the terminal.

Could not check UFW?

Use help me diagnose the UFW status check with the terminal output. Avoid changing firewall settings until you know its current state.

✔️ UFW is inactive

UFW is not filtering the incoming request. Continue to the Windows browser check.

ⓧ UFW is active

An active firewall needs a rule that permits the nginx request on port 80.

  • Allow incoming HTTP traffic by running:
sudo ufw insert 1 allow 80

What does this command do?

This command adds an allow rule for port 80. That rule lets computers on your network request the nginx site through HTTP.

  • Confirm that the new web rule is present by running:
sudo ufw status

What should the firewall show?

The updated status should include an allow entry for HTTP port 80. That entry confirms UFW permits web traffic to nginx.

Is the web rule missing?

Check the earlier command for a typing mistake before applying another rule. Use help me inspect my UFW web rule if the rule still does not appear.

Before you load the site, do you think the homepage remains available after the temporary Python server has stopped?

  • Switch back to Microsoft Edge on Windows.
  • Select the browser's address bar.
  • Enter http://<Ubuntu-LAN-IP>.
  • Press Enter.

You'll see the styled homepage with the heading My Ubuntu Home Lab. The address uses nginx on port 80.

You've crossed the network boundary. Your Windows PC can now reach the persistent website served by Ubuntu.

Your home lab website now survives beyond a temporary terminal process. Next, you'll administer the Ubuntu PC remotely from Windows.

Administer Ubuntu with OpenSSH

Your nginx site now stays online on Ubuntu. Managing it still means walking back to the Ubuntu keyboard.

OpenSSH Server gives your Windows PC an encrypted remote terminal. You will use that connection to confirm nginx is running without touching the Ubuntu keyboard.

In this step, get ready to:
  • Enable remote access on the Ubuntu PC.
  • Verify or install OpenSSH Client through an elevated Windows PowerShell session.
  • Connect from Windows to inspect nginx remotely.
Enable remote access on Ubuntu

OpenSSH Server runs on the Ubuntu PC. It accepts authenticated remote connections on port 22.

  • Enter your Ubuntu password if sudo requests it.
  • Approve the package installation if APT requests confirmation.
  • Install OpenSSH Server by running this command:
sudo apt install openssh-server

What does this command do?

APT installs the server application with its support files. Ubuntu then runs the service so it can accept SSH connections.

OpenSSH Server did not install?

Confirm that the Ubuntu PC still has internet access. Check that your Ubuntu account has administrator permission.

If the installation still fails, help me diagnose the OpenSSH Server installation.

Ubuntu may use UFW to filter incoming network traffic. The SSH rule is only needed when that firewall is active.

  • Check the Ubuntu firewall state by running this command:
sudo ufw status

What does this command do?

This command reports whether UFW currently filters network traffic. Its result determines whether you need to add the port 22 rule.

✔️ The firewall is off

No firewall rule is required. OpenSSH Server can use the trusted home network without a UFW change.

ⓧ The firewall is active

  • Permit SSH traffic through port 22 by running this command:
sudo ufw allow 22

What does this command do?

This rule permits incoming connections to the default SSH port. The existing HTTP rule continues to permit your nginx site on port 80.

Firewall rule was rejected?

Confirm that you included sudo. Check that you entered port 22.

If UFW still rejects the rule, help me diagnose the UFW SSH rule.

Your Ubuntu PC is now ready to receive encrypted remote connections from the trusted home network.

Prepare the Windows SSH client

The Windows PC needs OpenSSH Client to start the remote session. An elevated Windows PowerShell window can inspect this optional Windows capability.

  • Press the Windows key to open the search bar.
  • Type Windows PowerShell into the search bar.
  • Right-click the Windows PowerShell search result.
  • Choose the option that opens Windows PowerShell with administrator privileges.
  • Approve the Windows permission request.
  • Check the OpenSSH capability states by running this command:
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'

What does this command do?

The command reads Windows optional capability states. Its filter keeps the OpenSSH entries in the output.

The capability output now tells you which path applies.

✔️ OpenSSH Client is ready

The client capability is available. Keep the elevated Windows PowerShell window open for the connection.

ⓧ The client state is NotPresent

Windows has the capability available for installation. Add it before starting the remote session.

  • Install OpenSSH Client by running this command:
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0

What does this command do?

Windows adds its in-box OpenSSH Client capability. This provides the ssh command used for the remote connection.

  • Confirm the new client state by running the capability check again:
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'

What does this command confirm?

The refreshed output confirms whether Windows added the client capability. The OpenSSH Client entry should no longer report NotPresent.

OpenSSH Client did not install?

Confirm that Windows PowerShell has administrator privileges. Check that the Windows PC still has internet access.

If the state still reports NotPresent, help me diagnose the Windows capability installation.

That clears the Windows side of the connection. Your PC now has the client needed to reach Ubuntu.

Connect and inspect nginx remotely

An SSH session gives Windows an authenticated shell on Ubuntu. The first connection asks you to confirm the host before requesting your Ubuntu password.

Using your Ubuntu password here is expected because you are signing in to your own machine on the trusted home network.

  • Enter your Ubuntu sign-in name here: <Ubuntu-username>.
  • Reuse your Ubuntu LAN address from earlier: <Ubuntu-LAN-IP>.
  • Replace user with the Ubuntu username above.
  • Replace machine with the Ubuntu LAN address above.
  • Start the session by substituting your values into this command pattern:
ssh user@machine

What does this command do?

The ssh command starts an encrypted terminal session. The user@machine pattern identifies the Ubuntu account and the computer it belongs to.

  • Accept the first-connection authenticity prompt.
  • Enter your Ubuntu password when requested.

You will see an Ubuntu shell prompt inside Windows PowerShell. That prompt proves the remote session is active.

Cannot connect to Ubuntu?

  • Confirm that both computers remain connected to the same trusted home network.
  • Confirm that user was replaced with your Ubuntu sign-in name.
  • Confirm that machine was replaced with the Ubuntu LAN address from earlier.

If the connection still fails, help me diagnose my SSH connection.

Before you check, predict whether nginx kept running while you connected remotely:

  • Inspect nginx through the remote Ubuntu session by running this command:
sudo systemctl status nginx

What does this command confirm?

The command asks Ubuntu for the current nginx service state. Running it through SSH proves that Windows can administer the web server remotely.

You should see nginx report active (running). The result confirms that the persistent web service stayed online during the remote login.

  • Switch back to Microsoft Edge from earlier.
  • Refresh http://<Ubuntu-LAN-IP>.

You should still see the custom My Ubuntu Home Lab homepage. Windows can now reach both the website and the Ubuntu terminal.

You have completed the two-computer home lab. Your Ubuntu PC serves the site while your Windows PC administers the server remotely.

Secret mission

Add a Home Lab Status Page

Extend your home lab with a second page that reports its services. Link both pages so you can move between the homepage and status page from your Windows PC.

Clean Up Your Resources

Clean Up Your Resources

Everything in this project runs on your two computers. There are no cloud resources or ongoing service costs, so decide whether to keep the lab running, pause it, or delete it.

Resources you used:

  • Ubuntu project workspace at ~/python/helloworld with index.html and status.html.
  • Published website files at /var/www/html/index.html and /var/www/html/status.html.
  • nginx service serving the website on port 80.
  • OpenSSH Server service accepting authenticated connections on port 22.
  • Optional UFW allow rules for HTTP and SSH traffic.

Keep everything running

No action is needed. Choose this option if you want the home lab ready for future local networking or website projects.

  • Keep the nginx service active so both pages remain available on your trusted home network.
  • Keep the OpenSSH Server service active so Windows PowerShell can continue administering Ubuntu remotely.
  • Keep ~/python/helloworld as the editable source for your website.
  • Keep the published copies in /var/www/html so nginx can continue serving them.

Your website and remote terminal remain available whenever both computers are connected to the same trusted home network.

Pause - I'll come back to this later

Stop the two services to pause the lab while preserving every website file. Stopping OpenSSH ends your remote access, so complete this option directly on the Ubuntu PC.

  • Stop the nginx service with Ubuntu's system service controls.
  • Stop the OpenSSH Server service with Ubuntu's system service controls.
  • Keep the source files in ~/python/helloworld.
  • Keep the published files in /var/www/html.
  • Start the nginx service with the same controls when you return.
  • Start the OpenSSH Server service with the same controls when you need remote access again.

The website and remote terminal stay unavailable while their services are stopped. Your files remain ready for the next session.

Delete - I don't want to use this again

Deletion removes the home lab services and both copies of the website. This is permanent, so keep a separate copy of any HTML you want to reuse.

  • Return to the Ubuntu PC before removing OpenSSH Server.
  • Remove the nginx package through APT on Ubuntu.
  • Remove the openssh-server package through APT on Ubuntu.
  • Delete index.html and status.html from /var/www/html with administrator permission.
  • Delete the ~/python/helloworld folder from your Ubuntu home directory.
  • Remove only the optional UFW allow rules for ports 80 and 22 if this project added them.
  • Refresh the home lab tab in Microsoft Edge on Windows.
  • Try a new SSH connection from Windows PowerShell.

The website should no longer load from Windows. A new remote terminal session should no longer connect to the removed OpenSSH Server.

Nice Work!

Nice Work!

You did it: your Ubuntu PC now serves a linked home lab website through nginx. Your Windows PC can open it across your trusted home network.

You've learned how to:

  • Build a custom static homepage with HTML and CSS. Serve it locally with Python's temporary HTTP server.
  • Diagnose the network boundary by comparing localhost access with a request to the Ubuntu PC's LAN address.
  • Publish the site as a persistent nginx service. Open it from Microsoft Edge on Windows. Administer Ubuntu through a remote terminal from Windows PowerShell using OpenSSH.
  • Complete the optional Secret Mission by adding a linked Home Lab Status page with return navigation to the homepage.

Ready to quiz yourself?