Nginx Proxy Manager - Clean URLs & SSL Certificates [Part 5 of 10]
Setting up reverse proxy with automatic HTTPS for all your services
![]()
![]()
![]()
![]()
Youāve got Docker and Portainer running. Now you can deploy services. But thereās a problem:
Every service runs on a different port. Want to access your photo app? http://192.168.1.100:2283. Your forum? http://192.168.1.100:8080. Your wiki? http://192.168.1.100:3000.
Thatās ugly and hard to remember.
What if instead you could use:
-
https://photos.homelab.example.com -
https://forum.homelab.example.com -
https://wiki.homelab.example.com
Clean URLs. HTTPS encryption. One tool: Nginx Proxy Manager.
What is a Reverse Proxy?
A reverse proxy sits in front of your services and routes traffic to the right place.
Think of it like a receptionist:
-
You ask for āphotosā ā They direct you to port 2283
-
You ask for āforumā ā They direct you to port 8080
-
You ask for āwikiā ā They direct you to port 3000
Benefits:
-
Clean domain names instead of IP:port combinations
-
Automatic SSL certificates (HTTPS)
-
Single entry point for all services
-
Access control and authentication
-
Load balancing (for advanced setups)
Why Nginx Proxy Manager?
Nginx is the industry-standard reverse proxy. Itās powerful but complex to configure.
Nginx Proxy Manager (NPM) gives you a beautiful web UI to manage Nginx without editing config files.
Features
-
Web-based GUI - No command-line config editing
-
Free SSL certificates - Automatic Letās Encrypt integration
-
Auto-renewal - SSL certs renew automatically
-
Access lists - Control who can access what
-
Custom locations - Advanced routing rules
-
Stream forwarding - TCP/UDP proxying
-
Dead simple - Seriously, itās that easy
Perfect For
-
HomeLab services with clean URLs
-
Automatic HTTPS for everything
-
Centralizing access to multiple services
-
Learning reverse proxy concepts
What Youāll Need
Prerequisites
-
Docker installed (Part 4) -
Portainer running (Part 4) -
Domain name (optional but recommended) -
Paid: Namecheap, Cloudflare, etc. (~$15/year)
-
Free: DuckDNS
Ports Required
-
Port 80 - HTTP traffic
-
Port 443 - HTTPS traffic
-
Port 81 - NPM admin interface
Installing Nginx Proxy Manager
Weāll deploy NPM using Portainer Stacks.
Step 1: Create Data Directory
# Create directory for NPM data
sudo mkdir -p /mnt/storage/docker/nginx-proxy-manager/data
sudo mkdir -p /mnt/storage/docker/nginx-proxy-manager/letsencrypt
# Set ownership (replace 'admin' with your username)
sudo chown -R admin:admin /mnt/storage/docker/nginx-proxy-manager
Step 2: Deploy via Portainer
Login to Portainer:
https://192.168.1.100:9443
Create new stack:
-
Click Stacks in left sidebar
-
Click + Add stack
-
Name:
nginx-proxy-manager -
Build method: Web editor
Paste this compose configuration:
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
- '80:80' # Public HTTP Port
- '443:443' # Public HTTPS Port
- '81:81' # Admin Web Port
environment:
TZ: "America/New_York" # Change to your timezone (see note below)
DISABLE_IPV6: 'true'
volumes:
- /mnt/storage/docker/nginx-proxy-manager/data:/data
- /mnt/storage/docker/nginx-proxy-manager/letsencrypt:/etc/letsencrypt
Note about timezone (TZ):
-
Find your timezone code at: List of TZ Database Timezones
-
Examples:
America/New_York,America/Los_Angeles,Europe/London,Asia/Tokyo -
Use the value from the āTZ identifierā column
Deploy:
-
Scroll down
-
Click Deploy the stack
-
Wait 1-2 minutes for deployment
Step 3: Configure Firewall
On your Ubuntu server (via SSH), allow NPM ports from your local network:
# Allow HTTP from LAN
sudo ufw allow from 192.168.1.0/24 to any port 80 proto tcp comment 'HTTP from LAN'
# Allow HTTPS from LAN
sudo ufw allow from 192.168.1.0/24 to any port 443 proto tcp comment 'HTTPS from LAN'
# Allow NPM admin from LAN
sudo ufw allow from 192.168.1.0/24 to any port 81 proto tcp comment 'NPM Admin from LAN'
# Check firewall status
sudo ufw status numbered
Note: Replace 192.168.1.0/24 with your network range.
Step 4: Access NPM Admin Interface
Open your browser:
http://192.168.1.100:81
Default login credentials:
-
Email:
admin@example.com -
Password:
changeme
IMPORTANT: Youāll be forced to change these on first login. Choose a strong password!
Step 5: Initial Setup
On first login:
- Change email and password
-
Enter your email address
-
Choose a strong password (12+ characters)
-
Click Save
Need a strong password?
-
Recommended: Use a password manager (Bitwarden, 1Password, Proton Pass, KeePassXC)
-
Generate via command line:
# Generate a 20-character random password
openssl rand -base64 20
- Youāre in! You should see the NPM dashboard.
Setting Up Your Domain
You have two options for domain names:
Option 1: Paid Domain (Recommended)
Register a domain:
-
Namecheap (~$15/year)
-
Cloudflare (~$10/year)
-
Porkbun (~$10/year)
Example: homelab.example.com
For this guide, weāll use: homelab.example.com as a placeholder. Replace with your actual domain.
Option 2: Free DuckDNS
Sign up at DuckDNS
-
Login with Google/GitHub
-
Create a subdomain:
yourhomelab.duckdns.org -
Point it to your home IP address
-
Free forever!
DNS Configuration
You need to point your domain to your server. You have two approaches:
Approach 1: Local DNS Only (What I Use)
Best for: Services only accessed from home network
Setup:
-
Configure your router or DNS server (like Pi-hole, OPNsense) to resolve your domain locally
-
Create a wildcard DNS entry:
*.homelab.example.comā192.168.1.100 -
Domain doesnāt resolve publicly (more secure)
Pros:
-
More secure (not exposed to internet)
-
Works without port forwarding
-
No dynamic DNS needed
Cons:
-
Only works on your local network
-
Canāt access services remotely
Approach 2: Public DNS + Port Forwarding
Best for: Accessing services from anywhere
Setup:
-
Point domain to your public IP at your registrar
-
Configure port forwarding on router:
-
Forward port 80 ā 192.168.1.100:80
-
Forward port 443 ā 192.168.1.100:443
- Use dynamic DNS if you donāt have static IP
Pros:
-
Access services from anywhere
-
Works outside your network
Cons:
-
Exposes services to internet (security risk)
-
Requires port forwarding
-
Need dynamic DNS for changing IPs
Important Considerations:
-
ISP Terms of Service: Some ISPs prohibit running servers on residential connections. Check your ISPās acceptable use policy.
-
ISP Equipment: If youāre renting a modem/router from your ISP, it may have limited or disabled port forwarding capabilities. Consider using your own router.
-
Port Blocking: Some ISPs block common ports (80, 443, 25) on residential connections. You may need to use alternative ports.
-
Dynamic IP: Most residential connections have dynamic IPs that change periodically. Use a dynamic DNS service (DuckDNS, No-IP, etc.) to keep your domain updated.
Security Warning: Only expose services to the internet if you understand the security implications. Use strong passwords, keep services updated, and consider VPN access instead.
Getting SSL Certificates
NPM makes SSL certificates incredibly easy with Letās Encrypt.
Method 1: HTTP Challenge (Easiest)
Requirements:
-
Domain must be publicly accessible
-
Ports 80 and 443 forwarded to your server
Steps in NPM:
-
Go to SSL Certificates
-
Click Add SSL Certificate
-
Select Letās Encrypt
-
Domain Names: Enter your domain (e.g.,
photos.homelab.example.com) -
Email: Your email for renewal notifications
-
Use a DNS Challenge: Leave unchecked
-
Agree to Terms: Check the box
-
Click Save
NPM will:
-
Request certificate from Letās Encrypt
-
Verify domain ownership via HTTP
-
Install certificate automatically
-
Auto-renew before expiration
Method 2: DNS Challenge (Advanced)
Requirements:
-
API access to your DNS provider
-
Works even without public access
Supported providers:
-
Cloudflare
-
Namecheap
-
DuckDNS
-
Many others
Steps in NPM:
-
Go to SSL Certificates
-
Click Add SSL Certificate
-
Select Letās Encrypt
-
Domain Names: Enter your domain
-
Use a DNS Challenge: Check this box
-
DNS Provider: Select your provider
-
Credentials: Enter API credentials
-
Propagation Seconds: 120 (2 minutes)
-
Click Save
This is what I use - works with local-only DNS.
Creating Your First Proxy Host
Letās proxy Portainer so you can access it via a clean URL.
Step 1: Add Proxy Host
In NPM:
-
Go to Hosts ā Proxy Hosts
-
Click Add Proxy Host
Step 2: Configure Details Tab
Domain Names:
- Enter:
portainer.homelab.example.com
Scheme:
- Select:
https(Portainer uses HTTPS)
Forward Hostname / IP:
-
Enter:
192.168.1.100(your server IP) -
Or:
portainer(if using Docker network)
Forward Port:
- Enter:
9443(Portainerās port)
Options:
-
Cache Assets -
Block Common Exploits -
Websockets Support (important for Portainer)
Step 3: Configure SSL Tab
SSL Certificate:
-
Select your Letās Encrypt certificate
-
Or click Request a new SSL Certificate
Options:
-
Force SSL -
HTTP/2 Support -
HSTS Enabled (leave unchecked for now)
Step 4: Save and Test
Click: Save
Test it:
https://portainer.homelab.example.com
You should see Portainer! No more remembering ports.
Adding More Services
Repeat the process for each service:
Example: NPM itself
-
Domain:
npm.homelab.example.com -
Forward to:
192.168.1.100:81 -
SSL: Use your certificate
Example: Future services
-
Photos:
photos.homelab.example.comā port 2283 -
Forum:
forum.homelab.example.comā port 8080 -
Wiki:
wiki.homelab.example.comā port 3000
Security Best Practices
Docker Network Setup (Important!)
For NPM to proxy to other Docker containers using container names, they must be on the same network.
Connect NPM to Portainerās network:
On your Ubuntu server (via SSH):
# Connect NPM to portainer-network
docker network connect portainer-network nginx-proxy-manager
# Verify connection
docker inspect nginx-proxy-manager --format='{{range $net,$v := .NetworkSettings.Networks}}{{$net}} {{end}}'
# Should show: nginx-proxy-manager_default portainer-network
Now you can proxy to containers by name:
-
Use
portainerinstead of192.168.1.100:9443 -
Use
nginx-proxy-managerfor NPM itself
For future services:
-
Connect them to
portainer-networkin their docker-compose files -
Then use container names (e.g.,
immich_server,wikijs,discourse) -
This is cleaner than using IP addresses and works even if IPs change
Access Lists
Restrict access to sensitive services:
-
Go to Access Lists
-
Click Add Access List
-
Name:
LAN Only -
Authorization:
-
Satisfy Any: Off
-
Pass Auth: Off
- Access:
-
Allow:
192.168.1.0/24 -
Deny:
all
- Click Save
Apply to proxy host:
-
Edit proxy host
-
Access List: Select
LAN Only -
Save
Note: Access lists work best when using IP addresses in the āForward Hostname/IPā field. If using container names, the access list restricts who can access the domain, not the backend container.
Strong Passwords
-
Use unique, strong passwords for NPM admin
-
Enable 2FA if available (future feature)
-
Donāt use default credentials
Keep Updated
-
Regularly update NPM container
-
Check for security advisories
-
Monitor access logs
TL;DR
-
Nginx Proxy Manager provides reverse proxy with web UI
-
Clean URLs instead of IP:port combinations
-
Free SSL certificates via Letās Encrypt
-
Installation: Deploy via Portainer stack
-
Configuration: Add proxy hosts for each service
-
DNS: Local DNS or public with port forwarding
-
Next: Weāll install Immich for photo management in Part 6
Your Turn
Are you using local-only DNS or exposing services publicly?
What domain registrar do you prefer?
Any questions about reverse proxy or SSL certificates?
Drop a comment below!
Navigation: ā Part 4 | Part 6 ā