How to Host Multiple Public Websites on Your Windows PC
Note: Written with the help of my research and editorial team 🙂 including: (Google Gemini, Google Notebook LM, Microsoft Copilot, Perplexity.ai, Claude.ai and others as needed)
From Zero to Hero: Using Docker, Nginx Proxy Manager & HTTPS
Like many developers, I grew tired of the limitations and recurring costs of commercial web hosting for development projects. Have you ever wanted to host your own powerful websites or web applications from home, but felt constrained by the costs and limitations of commercial hosting? Stop paying for underpowered / underutilized Virtual Private Servers (VPS)! The truth is, the old hardware gathering dust in your closet often boasts superior CPU and memory resources. You can leverage that equipment: using a Windows PC, Docker, and Nginx Proxy Manager, and can easily overcome the challenges of networking and SSL to create a cost-effective, multi-application web server secured with free HTTPS.
This guide will walk you through everything, from getting your domain name to deploying your first secure application. Let’s dive in!
Part 1: The Foundations – What You Need & Why
Before we start tinkering, let’s understand the core components and why they’re essential:
- A Windows PC (Your Host): This is the physical machine running Docker.
- Docker Desktop: This magical tool allows you to run applications in isolated “containers,” making deployment consistent and secure.
- A Domain Name (e.g.,
yourdomain.com): Absolutely crucial for public access and HTTPS. You’ll need to purchase this from a registrar (Namecheap, Google Domains, GoDaddy, etc.). - Nginx Proxy Manager (NPM): This Docker container acts as your “traffic cop.” It handles incoming web requests, manages free SSL certificates (via Let’s Encrypt) for your subdomains, and intelligently forwards traffic to the correct application container.
- Your Web Applications: These are your actual sites or APIs, each running in its own Docker container.
- Home Router & Internet Service Provider (ISP): Your router will need to be configured for “Port Forwarding” to direct internet traffic to your Windows PC. Your ISP provides your internet connection and a public IP address.
Part 2: Setting Up Your Environment
Step 1: Install Docker Desktop on Windows
If you don’t have it already, download and install Docker Desktop for Windows.
Download Docker Desktop
Follow the installation instructions. You’ll likely need to enable WSL 2 (Windows Subsystem for Linux 2) for it to work. After installation, ensure Docker Desktop is running.
Step 2: Get Your Domain Name
Purchase a domain name (e.g., myawesomeserver.com). Choose a registrar you like. This is non-negotiable for public access and HTTPS.
Step 3: Understand Your Public IP and Dynamic DNS (DDNS)
Most home internet connections have a dynamic public IP address, meaning it changes periodically. For your domain to consistently point to your home, you’ll need:
- DDNS Service: Sign up for a free DDNS service (e.g., from No-IP, Dynu, or some domain registrars offer it). This service automatically updates your domain’s A-records whenever your public IP changes.
- Configure Router/Client: Many modern routers have built-in DDNS client support. If not, you might install a small client application on your Windows PC that runs in the background and updates your DDNS provider.
Your domain’s A-records will point to your DDNS hostname, which then points to your current public IP.
Step 4: Assign a Static Local IP to Your Windows PC
Inside your home network, your Windows PC should have a static local IP address. This prevents its IP from changing within your home network, which is crucial for reliable port forwarding.
- Go to
Settings > Network & Internet > Ethernet(or Wi-Fi, if applicable). - Click on your network adapter.
- Under “IP assignment,” click “Edit.”
- Change from “Automatic (DHCP)” to “Manual.”
- Toggle IPv4 on.
- Enter a static IP (e.g.,
192.168.1.100), your router’s gateway (e.g.,192.168.1.1), and your preferred DNS servers (e.g.,8.8.8.8,8.8.4.4for Google DNS). - Save the settings.
Part 3: Setting Up Nginx Proxy Manager with Docker Compose
We’ll use docker-compose.yml to define and manage our NPM and application containers.
Step 5: Create Your Project Directory and docker-compose.yml
- Create a new folder on your Windows PC, e.g.,
C:\webserver. - Inside this folder, create a file named
docker-compose.yml.
Now, paste the following content into your docker-compose.yml file. This sets up Nginx Proxy Manager (NPM) and its required MariaDB database. We’ve also included placeholders for two sample applications.
YAML
version: '3.8'
services:
# Nginx Proxy Manager (Our Traffic Cop & SSL Handler)
nginx-proxy-manager:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
# These are the ONLY ports you open in your HOME ROUTER/FIREWALL:
- '80:80' # Public HTTP access for Let's Encrypt validation & redirects
- '443:443' # Public HTTPS access for your websites
# IMPORTANT: Port 81 for NPM's web interface.
# Only access this from your local network (e.g., http://192.168.1.100:81).
# DO NOT port forward 81 on your public router unless you know the risks!
- '81:81'
environment:
DB_MYSQL_HOST: npm_database # Connects to the database service defined below
DB_MYSQL_DATABASE: 'npm'
DB_MYSQL_USER: 'npmuser'
DB_MYSQL_PASSWORD: 'secure_password_npm' # <--- !!! CHANGE THIS !!!
volumes:
- ./data:/data # Stores NPM config, proxy hosts, etc.
- ./letsencrypt:/etc/letsencrypt # Stores SSL certificates
networks:
- web_network # All web services will join this network
# Database for Nginx Proxy Manager (NPM needs this for its config)
npm_database:
image: mariadb:latest
container_name: npm_database
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: 'secure_root_password_db' # <--- !!! CHANGE THIS !!!
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npmuser'
MYSQL_PASSWORD: 'secure_password_npm' # <--- !!! MATCH NPM's PASSWORD !!!
volumes:
- ./mysql:/var/lib/mysql # Stores the database files
# This service does NOT need ports exposed. It's only for NPM.
networks:
- web_network
# Your First Application (e.g., a simple Nginx web server)
my-app-1:
image: 'nginx:latest' # Replace with your actual application image
container_name: my_first_app
restart: unless-stopped
# This application listens internally on port 80 (Nginx default)
# No public ports needed, NPM routes traffic to it.
networks:
- web_network
# Your Second Application (e.g., a "hello-world" web app)
my-app-2:
image: 'hello-world' # Replace with your actual application image
container_name: my_second_app
restart: unless-stopped
# This app typically serves on port 80 (if it's a web server)
networks:
- web_network
networks:
# Define a custom network for all our web-related services
web_network:
driver: bridge
IMPORTANT SECURITY NOTES:
- CHANGE ALL PASSWORDS! Immediately replace
secure_password_npmandsecure_root_password_dbwith strong, unique passwords. - The
hello-worldimage is just a placeholder; replace it with your actual application images. - DO NOT port forward port 81 publicly on your router. This is for NPM’s administration panel, which should only be accessible from your local network.
Step 6: Start Your Docker Services
Open PowerShell or Command Prompt, navigate to your C:\webserver directory, and run:
Bash
docker compose up -d
This command will download the necessary images and start all your containers in the background.
Part 4: Router Configuration & Public Access
Step 7: Configure Port Forwarding on Your Home Router
This is a critical step to allow internet traffic to reach your Docker setup.
- Log in to your Router: Open a web browser and go to your router’s IP address (e.g.,
192.168.1.1). - Find Port Forwarding Settings: Look for sections like “Port Forwarding,” “NAT,” “Virtual Servers,” or “Firewall.”
- Create New Rules: You need to forward external ports 80 and 443 to your Windows PC’s static local IP address (e.g.,
192.168.1.100) and the corresponding internal ports 80 and 433, respectively.ServiceExternal PortInternal PortProtocolInternal IP (Your PC)HTTP8080TCP192.168.1.100HTTPS443443TCP192.168.1.100 - Save/Apply Settings: Your router should now direct web traffic to your Docker host.
Step 8: Configure DNS Records for Your Domain
This tells the internet where your domain and subdomains live.
- Log in to Your Domain Registrar/DDNS Provider: Access the DNS management section.
- Add A Records: For each subdomain you want to use, you’ll create an
Arecord pointing to your public IP address (or your DDNS hostname if using a DDNS service).TypeHost/NameValueA@oryourdomain.comYour Public IP (or DDNS Hostname)AwwwYour Public IP (or DDNS Hostname)Aapp1Your Public IP (or DDNS Hostname)Aapp2Your Public IP (or DDNS Hostname)Note: DNS changes can take a few minutes to a few hours (propagation time) to update across the internet.
Part 5: Configuring Nginx Proxy Manager & Deploying Applications
Step 9: Access Nginx Proxy Manager Admin Panel
Open your web browser and go to http://YOUR_WINDOWS_PC_LOCAL_IP:81 (e.g., http://192.168.1.100:81).
- Default Login:
- Email:
admin@example.com - Password:
changeme
- Email:
- IMMEDIATELY CHANGE THESE CREDENTIALS! Go to “Users” in the sidebar to update your admin account.
Step 10: Create Your First Proxy Host (e.g., for my-app-1)
Now, let’s configure NPM to route app1.yourdomain.com to your my-app-1 Docker container and add HTTPS.
- In NPM, go to Hosts > Proxy Hosts.
- Click Add Proxy Host.
- Details Tab:
- Domain Names: Enter
app1.yourdomain.com. - Scheme:
http(your application container typically listens on HTTP internally). - Forward Hostname / IP: Enter
my_first_app(this is thecontainer_namefrom yourdocker-compose.yml). - Forward Port: Enter
80(this is the port yournginx:latestcontainer is listening on internally). - Check
Block Common Exploits(recommended).
- Domain Names: Enter
- SSL Tab:
- SSL Certificate: Select
Request a New SSL Certificate. - Check
Force SSL. - Check
I Agree to the Let's Encrypt Terms of Service. - Enter a valid email address for certificate expiry notifications.
- Click Save.
- SSL Certificate: Select
NPM will now attempt to obtain an SSL certificate for app1.yourdomain.com and, once successful, your application will be securely available!
Step 11: Repeat for Additional Applications
For each additional application (e.g., my-app-2 at app2.yourdomain.com), repeat Step 10:
- Add a new
proxy hostin NPM. - Specify
app2.yourdomain.comas the domain. - Forward to the container name
my_second_appand its internal port (e.g., 80 forhello-world). - Request a new SSL certificate.
Step 12: Deploy a New Application
To add an entirely new application (let’s say my-app-3):
- Create a Docker Image: If your application isn’t on Docker Hub, create a
Dockerfilefor it and build your custom image (e.g.,docker build -t my-custom-app:latest .). - Update
docker-compose.yml: Add a new service block. - Restart Docker Compose:
docker compose up -d(this will createmy_third_appwithout affecting others). - Configure in NPM: Go to NPM, add a new Proxy Host for
app3.yourdomain.com, forward to the correct container name and internal port, and request SSL. - Update DNS: Add an A record for
app3.yourdomain.compointing to your public IP.
Part 6: Internal Network Access & Troubleshooting
You will absolutely be able to access your entire platform from within your home network. However, there are a few important points regarding how traffic flows internally.
Accessing the NPM Admin Panel
Always access the NPM interface directly via your host’s local IP:
- URL:
http://[Your_Static_Local_IP]:81(e.g.,http://192.168.1.100:81)
Accessing Applications via Domain Name
When you use the public URL (https://app1.yourdomain.com) from a device inside your home, the traffic needs to take a special path:
- If your router supports NAT Loopback (or Hairpin NAT): This is the easiest scenario. Your router recognizes that you are trying to reach your own public IP address and cleverly routes the traffic back internally to your Windows Host. You can use the public URL, and it works perfectly.
- If your router DOES NOT support NAT Loopback:
- Trying to use the public URL will likely result in a timeout or failure because the router doesn’t know how to send the traffic back “in.”
- Solution: Use Your Local Hosts File: For your home computer, you can edit the hosts file to map your subdomains directly to your Windows host’s static local IP. This forces your local machine to look at the correct server internally, bypassing the router’s public route issue.
| Domain/Subdomain | Internal IP |
app1.yourdomain.com | 192.168.1.100 |
app2.yourdomain.com | 192.168.1.100 |
Part 7: Security and Maintenance Tips
- Keep Everything Updated: Regularly update Docker Desktop, Nginx Proxy Manager, your application images, and your Windows OS for the latest security patches.
- Strong Passwords: Use complex, unique passwords for NPM, database, and any application admin panels.
- Firewall on Host: Ensure your Windows Firewall is active and only allows necessary ports (80, 443, and 81 internally). Docker Desktop usually handles rules for published ports.
- Backups: Regularly back up the
dataandletsencryptfolders for NPM, and any persistent data for your applications. - Monitor: Keep an eye on your server’s logs and resource usage.
- Consider VPS for Production: While this setup is great for learning and personal projects, for critical, high-traffic production websites, a dedicated Virtual Private Server (VPS) often offers better reliability, security, and performance than a home internet connection.
You now have a powerful, flexible, and secure way to host multiple web applications from your Windows PC using Docker and Nginx Proxy Manager. This setup opens up a world of possibilities for your projects. Happy hosting!
