Windows 10 + WSL2 Ubuntu Server Guide

LAMP (Nginx + PHP + MySQL) + Node + WebRTC + Reverse Proxy + Auto Networking

You can save this page as a PDF in your browser (Print → Save as PDF) after you’ve opened it locally.

0. Overview

This guide will help you turn a Windows 10 PC into a small server using WSL2 and Ubuntu. You will end up with:

Important: Start by making everything work on your own PC, then on your local network (LAN), and only then think about exposing it to the internet.

1. Prepare Windows 10

1.1 Update Windows

  1. Click the Start button.
  2. Click Settings (gear icon).
  3. Go to Update & SecurityWindows Update.
  4. Click Check for updates and install everything important.
Windows Update screen example

1.2 Enable WSL and Virtualization

  1. Click Start, type PowerShell.
  2. Right-click Windows PowerShellRun as administrator.
  3. Paste and run the following commands one by one:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
  1. Restart your PC when prompted.

1.3 Install WSL2 Kernel and Set Default Version

  1. Open PowerShell as administrator again.
  2. Run:
wsl --update
wsl --set-default-version 2

2. Install Ubuntu in WSL2

2.1 Install Ubuntu from Microsoft Store

  1. Open the Microsoft Store.
  2. Search for Ubuntu 22.04 LTS.
  3. Click Get or Install.
  4. After installation, click Launch.
Microsoft Store Ubuntu page example

2.2 First Launch Setup

  1. A terminal window will open with Ubuntu starting.
  2. It will ask for a UNIX username (e.g. darryl).
  3. It will ask for a password (this is your Linux admin password).

2.3 Update Ubuntu

In the Ubuntu window, run:

sudo apt update
sudo apt upgrade -y

2.4 Create Project Folders

Still in Ubuntu, create folders for your server:

mkdir -p ~/server/{web,node,logs}

3. Install Nginx, PHP, MySQL, Node

3.1 Install Nginx, PHP, MySQL

sudo apt install -y nginx mysql-server php-fpm php-mysql

3.2 Secure MySQL

sudo mysql_secure_installation

Recommended answers:

3.3 Install Node.js via nvm

sudo apt install -y curl build-essential
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install --lts
node -v
npm -v

4. Configure Nginx + PHP + Reverse Proxy

4.1 Create Web Root and Test PHP File

mkdir -p ~/server/web/public
echo "<?php phpinfo(); ?>" > ~/server/web/public/index.php

4.2 Nginx Site Configuration

Replace YOURUSER with your Ubuntu username.

sudo nano /etc/nginx/sites-available/app.conf
server {
    listen 80;
    server_name _;

    root /home/YOURUSER/server/web/public;
    index index.php index.html;

    access_log /home/YOURUSER/server/logs/nginx_access.log;
    error_log  /home/YOURUSER/server/logs/nginx_error.log;

    # Admin panel (PHP)
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # PHP handling
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php-fpm.sock;
    }

    # Node.js API
    location /api/ {
        proxy_pass http://localhost:3000/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # WebRTC signaling (WebSocket)
    location /signal/ {
        proxy_pass http://localhost:3001/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

Save and exit (Ctrl+O, Enter, Ctrl+X).

sudo ln -s /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl restart nginx

5. Node + WebRTC Signaling Server

5.1 Create Node Project

cd ~/server/node
npm init -y
npm install express ws

5.2 Create server.js

const express = require('express');
const WebSocket = require('ws');

const app = express();
const apiPort = 3000;
const signalPort = 3001;

// Basic API
app.get('/api/test', (req, res) => {
  res.json({ status: "API OK" });
});

app.listen(apiPort, () => {
  console.log(`API running on ${apiPort}`);
});

// WebRTC signaling
const wss = new WebSocket.Server({ port: signalPort });

wss.on('connection', ws => {
  ws.on('message', msg => {
    // Broadcast signaling messages
    wss.clients.forEach(client => {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(msg);
      }
    });
  });
});

console.log(`Signaling server running on ${signalPort}`);

Run it:

node server.js

6. Simple PHP Admin Panel

6.1 Replace index.php with Admin Panel

nano ~/server/web/public/index.php
<?php
$info = [
    "server_time" => date("Y-m-d H:i:s"),
    "php_version" => phpversion(),
    "mysql_status" => trim(shell_exec("systemctl is-active mysql 2>/dev/null")),
    "node_api_test" => @file_get_contents("http://localhost/api/test")
];
?>
<!DOCTYPE html>
<html>
<head>
    <title>Admin Panel</title>
    <style>
        body { font-family: Arial, sans-serif; padding: 20px; }
        pre { background: #eee; padding: 10px; }
    </style>
</head>
<body>
<h1>Admin Panel</h1>
<pre><?php print_r($info); ?></pre>
</body>
</html>

Test from inside Ubuntu:

curl http://localhost

Then from Windows, open a browser and go to:

http://localhost

7. Windows Firewall and Port Forwarding (LAN Access)

7.1 Allow Ports in Windows Firewall

  1. Open Start → type Windows Defender Firewall with Advanced Security → open it.
  2. Click Inbound RulesNew Rule....
  3. Select Port → Next.
  4. TCP, Specific local ports: 80 → Next.
  5. Allow the connection → Next.
  6. Check only Private for now → Next.
  7. Name it WSL_HTTP_80 → Finish.
Windows Firewall new inbound rule example

Repeat the same steps for ports 3000 and 3001 (Node API and signaling).

7.2 Find WSL IP

ip addr show eth0

Look for a line like inet 172.24.x.x/20. That IP is your WSL IP.

7.3 Manual Port Forwarding (First Time)

In PowerShell as administrator:

$wslIP = "YOUR_WSL_IP_HERE"

netsh interface portproxy add v4tov4 listenport=80 listenaddress=0.0.0.0 connectport=80 connectaddress=$wslIP
netsh interface portproxy add v4tov4 listenport=3000 listenaddress=0.0.0.0 connectport=3000 connectaddress=$wslIP
netsh interface portproxy add v4tov4 listenport=3001 listenaddress=0.0.0.0 connectport=3001 connectaddress=$wslIP

From another device on your LAN, open a browser and go to:

8. Auto‑Detect WSL IP and Re‑Apply Portproxy Rules

WSL2 changes its internal IP address every time Windows reboots. This means your port forwarding breaks unless you update it manually.

This step creates a PowerShell script that:

8.1 Create the PowerShell Script

1. Press Windows Key + E to open File Explorer.

2. Open your C:\ drive.

3. Right‑click → New → Text Document.

4. Name it: wsl-portproxy.ps1

(Make sure it ends in .ps1, not .txt.)

5. Right‑click the file → Edit (or open in Notepad).

6. Paste this inside:

$wslIP = wsl.exe hostname -I | ForEach-Object { $_.Split(" ")[0] }

# Clear old rules
netsh interface portproxy reset

# HTTP
netsh interface portproxy add v4tov4 listenport=80 listenaddress=0.0.0.0 connectport=80 connectaddress=$wslIP

# Node API
netsh interface portproxy add v4tov4 listenport=3000 listenaddress=0.0.0.0 connectport=3000 connectaddress=$wslIP

# WebRTC signaling
netsh interface portproxy add v4tov4 listenport=3001 listenaddress=0.0.0.0 connectport=3001 connectaddress=$wslIP

7. Save and close Notepad.

8.2 Test the Script

1. Click Start, type PowerShell.

2. Right‑click → Run as administrator.

3. Run:

powershell -ExecutionPolicy Bypass -File "C:\wsl-portproxy.ps1"

4. If no errors appear, the script works.

8.3 Make It Run Automatically at Boot

1. Press Start, type Task Scheduler, open it.

2. Click Create Task (right side).

3. Name it: WSL Portproxy Auto.

4. Check: Run with highest privileges.

5. Go to the Triggers tab → New → choose At startup.

6. Go to the Actions tab → New.

7. Program/script:

powershell.exe

8. Add arguments:

-ExecutionPolicy Bypass -File "C:\wsl-portproxy.ps1"

9. Save the task.

Now your WSL ports will always work after reboot.

9. Securing Your Stack Before Internet Exposure

Before opening anything to the outside world, lock down the basics.

9.1 Secure MySQL

Edit MySQL config:

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Find the line:

bind-address = 127.0.0.1

If it’s commented out, uncomment it.

Save → restart MySQL:

sudo systemctl restart mysql

9.2 Secure Nginx

9.3 Secure Node/WebRTC

10. Adding HTTPS (Let’s Encrypt)

This step is optional until you expose your server publicly.

10.1 Install Certbot

sudo apt install certbot python3-certbot-nginx -y

10.2 Run Certbot

sudo certbot --nginx

Follow the prompts:

Your site is now HTTPS‑secured.

11. Exposing Your Server to the Internet

Once everything works on LAN and is secured, you can expose it publicly.

11.1 Router Port Forwarding

Forward these ports from your router to your Windows PC:

Do not forward 3000 or 3001.

11.2 Test from Mobile Data

Turn off Wi‑Fi on your phone and visit:

https://your-domain.com

If it loads, your server is live.

12. Daily Workflow and Useful Commands

12.1 Start Node Server

cd ~/server/node
node server.js

12.2 Restart Nginx

sudo systemctl restart nginx

12.3 Check WSL IP

hostname -I

12.4 View Logs

tail -f ~/server/logs/nginx_error.log

Your server stack is now complete and production‑ready.