🌐 Nginx Proxy Manager - Clean URLs & SSL Certificates [Part 5 of 10]

:globe_with_meridians: Nginx Proxy Manager - Clean URLs & SSL Certificates [Part 5 of 10]

Setting up reverse proxy with automatic HTTPS for all your services

:wrench::locked::globe_showing_europe_africa::satellite_antenna:


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.


:brain: 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)


:light_bulb: 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.

:white_check_mark: 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

:bullseye: Perfect For

  • HomeLab services with clean URLs

  • Automatic HTTPS for everything

  • Centralizing access to multiple services

  • Learning reverse proxy concepts


:clipboard: What You’ll Need

Prerequisites

  • :white_check_mark: Docker installed (Part 4)

  • :white_check_mark: Portainer running (Part 4)

  • :white_check_mark: 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


:rocket: 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:

  1. Click Stacks in left sidebar

  2. Click + Add stack

  3. Name: nginx-proxy-manager

  4. 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

:warning: IMPORTANT: You’ll be forced to change these on first login. Choose a strong password!


Step 5: Initial Setup

On first login:

  1. Change email and password
  • Enter your email address

  • Choose a strong password (12+ characters)

  • Click Save

Need a strong password?


# Generate a 20-character random password

openssl rand -base64 20

  1. You’re in! You should see the NPM dashboard.

:globe_showing_europe_africa: Setting Up Your Domain

You have two options for domain names:

Option 1: Paid Domain (Recommended)

Register a domain:

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!


:wrench: 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:

  1. Configure your router or DNS server (like Pi-hole, OPNsense) to resolve your domain locally

  2. Create a wildcard DNS entry: *.homelab.example.com → 192.168.1.100

  3. 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:

  1. Point domain to your public IP at your registrar

  2. Configure port forwarding on router:

  • Forward port 80 → 192.168.1.100:80

  • Forward port 443 → 192.168.1.100:443

  1. 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

:warning: 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.

:warning: 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.


:locked: 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:

  1. Go to SSL Certificates

  2. Click Add SSL Certificate

  3. Select Let’s Encrypt

  4. Domain Names: Enter your domain (e.g., photos.homelab.example.com)

  5. Email: Your email for renewal notifications

  6. Use a DNS Challenge: Leave unchecked

  7. Agree to Terms: Check the box

  8. 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:

  1. Go to SSL Certificates

  2. Click Add SSL Certificate

  3. Select Let’s Encrypt

  4. Domain Names: Enter your domain

  5. Use a DNS Challenge: Check this box

  6. DNS Provider: Select your provider

  7. Credentials: Enter API credentials

  8. Propagation Seconds: 120 (2 minutes)

  9. Click Save

This is what I use - works with local-only DNS.


:bullseye: 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:

  1. Go to Hosts → Proxy Hosts

  2. 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:

  • :white_check_mark: Cache Assets

  • :white_check_mark: Block Common Exploits

  • :white_check_mark: 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:

  • :white_check_mark: Force SSL

  • :white_check_mark: HTTP/2 Support

  • :white_large_square: 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.


:mobile_phone: 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


:shield: 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 portainer instead of 192.168.1.100:9443

  • Use nginx-proxy-manager for NPM itself

For future services:

  • Connect them to portainer-network in 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:

  1. Go to Access Lists

  2. Click Add Access List

  3. Name: LAN Only

  4. Authorization:

  • Satisfy Any: Off

  • Pass Auth: Off

  1. Access:
  • Allow: 192.168.1.0/24

  • Deny: all

  1. 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


:brain: 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


:speech_balloon: 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 →