Nginx Proxy Manager: Reverse Proxy Setup
Nginx Proxy Manager (NPM) provides a clean web interface for managing reverse proxies and SSL certificates. This article covers deployment, configuration, and integration with Kubernetes services.
Why NPM?
Requirements:
- Manage multiple domains/subdomains
- Automatic SSL certificates (Let’s Encrypt)
- Easy configuration via web UI
- Integration with Kubernetes services
Alternatives Considered:
- Traefik Ingress - More complex, less user-friendly
- Nginx Ingress - Requires YAML editing
- Caddy - Good but less features
NPM Advantages:
- Beautiful web interface
- One-click SSL certificate generation
- Easy proxy host management
- Statistics and logs built-in
Architecture
Internet
│
▼
Home Router (Port Forwarding)
│
▼
NPM LoadBalancer (192.168.1.202)
│
├─► blog.sortium.fr → homelab-blog.blog.svc.cluster.local:80
├─► npm.sortium.fr → localhost:81
└─► Other services...
Deployment
Kubernetes Manifest
apiVersion: v1
kind: Namespace
metadata:
name: npm
labels:
pod-security.kubernetes.io/enforce: privileged
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: npm-data
namespace: npm
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-proxy-manager
namespace: npm
spec:
replicas: 1
template:
spec:
containers:
- name: npm
image: jc21/nginx-proxy-manager:latest
ports:
- containerPort: 80
- containerPort: 81
- containerPort: 443
volumeMounts:
- name: data
mountPath: /data
- name: letsencrypt
mountPath: /etc/letsencrypt
env:
- name: DISABLE_IPV6
value: "true"
volumes:
- name: data
persistentVolumeClaim:
claimName: npm-data
- name: letsencrypt
persistentVolumeClaim:
claimName: npm-letsencrypt
---
apiVersion: v1
kind: Service
metadata:
name: nginx-proxy-manager
namespace: npm
spec:
type: LoadBalancer
ports:
- name: http
port: 80
targetPort: 80
- name: admin
port: 81
targetPort: 81
- name: https
port: 443
targetPort: 443
selector:
app: nginx-proxy-manager
Installation Script
#!/bin/bash
# scripts/08-install-npm.sh
echo "========================================"
echo "08 - Installation Nginx Proxy Manager"
echo "========================================"
# Apply manifest
kubectl apply -f kubernetes/networking/npm/npm-deploy.yaml
# Wait for pod
kubectl wait --for=condition=ready pod -l app=nginx-proxy-manager -n npm --timeout=120s
# Get LoadBalancer IP
NPM_IP=$(kubectl get svc -n npm nginx-proxy-manager -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "✅ NPM installé"
echo "🌐 Interface admin: http://${NPM_IP}:81"
echo " Email: admin@example.com"
echo " Password: changeme"
Configuration
Initial Setup
-
Access Admin UI
- URL:
http://192.168.1.202:81 - Default credentials:
admin@example.com/changeme - Important: Change password immediately!
- URL:
-
Router Configuration
- Configure port forwarding on home router:
- Port 80 →
192.168.1.202:80 - Port 443 →
192.168.1.202:443
- Port 80 →
- Configure port forwarding on home router:
Creating Proxy Hosts
Example: Blog Service
-
Go to Proxy Hosts → Add Proxy Host
-
Details Tab:
- Domain Names:
blog.sortium.fr - Scheme:
http(important: Kubernetes services use HTTP) - Forward Hostname/IP:
homelab-blog.blog.svc.cluster.local - Forward Port:
80 - Cache Assets: ✅
- Block Common Exploits: ✅
- Websockets Support: ❌ (not needed for static blog)
- Domain Names:
-
SSL Tab:
- SSL Certificate: Request a new SSL Certificate with Let’s Encrypt
- Force SSL: ✅
- HTTP/2 Support: ✅
- HSTS Enabled: ✅
-
Advanced Tab:
- Usually empty for simple services
-
Click Save
Result:
- NPM generates Let’s Encrypt certificate automatically
- Blog accessible at
https://blog.sortium.fr - SSL auto-renewal configured
Kubernetes Service Discovery
NPM can access Kubernetes services using DNS:
Format: <service-name>.<namespace>.svc.cluster.local:<port>
Examples:
- Blog:
homelab-blog.blog.svc.cluster.local:80 - Console API:
console-api.console.svc.cluster.local:8080 - NPM itself:
localhost:81or127.0.0.1:81
Common Configurations
Static Site (Blog)
Domain: blog.sortium.fr
Forward to: homelab-blog.blog.svc.cluster.local:80
Scheme: http
SSL: Let's Encrypt
API Service
Domain: api.sortium.fr
Forward to: my-api.default.svc.cluster.local:3000
Scheme: http
SSL: Let's Encrypt
Websockets: ✅ (if needed)
Admin Interface
Domain: npm.sortium.fr
Forward to: localhost:81
Scheme: http
SSL: Let's Encrypt
Multiple Domains
Domain Names:
- app1.sortium.fr
- app1-alias.sortium.fr
Forward to: app1.default.svc.cluster.local:80
SSL Certificate Management
Automatic Let’s Encrypt
NPM handles everything automatically:
- Detects new proxy host
- Requests certificate from Let’s Encrypt
- Validates domain via HTTP-01 challenge
- Installs certificate
- Auto-renews before expiration
Manual Certificate Upload
For custom certificates:
- SSL Certificates → Add SSL Certificate
- Select Custom
- Upload certificate files
- Assign to proxy hosts
Troubleshooting
Certificate Generation Fails
Problem: Let’s Encrypt validation fails
Causes:
- Domain doesn’t point to NPM IP
- Port 80 not accessible from internet
- Firewall blocking Let’s Encrypt
Solution:
# Verify DNS
nslookup blog.sortium.fr
# Should return your public IP
# Verify port forwarding
curl -I http://blog.sortium.fr
# Should return NPM response
# Check NPM logs
kubectl logs -n npm -l app=nginx-proxy-manager | grep -i ssl
404 Errors
Problem: Proxy host returns 404
Causes:
- Wrong forward hostname/IP
- Service not accessible from NPM pod
- Wrong port
Solution:
# Test service from NPM pod
kubectl exec -n npm $(kubectl get pod -n npm -l app=nginx-proxy-manager -o jsonpath='{.items[0].metadata.name}') -- \
curl -s http://homelab-blog.blog.svc.cluster.local:80
# Verify service exists
kubectl get svc -n blog homelab-blog
# Check NPM config
kubectl exec -n npm $(kubectl get pod -n npm -l app=nginx-proxy-manager -o jsonpath='{.items[0].metadata.name}') -- \
cat /data/nginx/proxy_host/1.conf
Scheme Mismatch
Problem: Service returns errors when using HTTPS forward
Cause: Kubernetes services typically use HTTP internally
Solution: Always use http scheme for Kubernetes services:
Forward Scheme: http (not https)
Forward to: service.namespace.svc.cluster.local:80
Backup & Restore
Backup Configuration
# Backup NPM data
kubectl exec -n npm $(kubectl get pod -n npm -l app=nginx-proxy-manager -o jsonpath='{.items[0].metadata.name}') -- \
tar -czf /tmp/npm-backup.tar.gz /data
# Copy to local
kubectl cp npm/$(kubectl get pod -n npm -l app=nginx-proxy-manager -o jsonpath='{.items[0].metadata.name}'):/tmp/npm-backup.tar.gz \
./npm-backup.tar.gz
Restore from Backup
# Copy backup to pod
kubectl cp ./npm-backup.tar.gz \
npm/$(kubectl get pod -n npm -l app=nginx-proxy-manager -o jsonpath='{.items[0].metadata.name}'):/tmp/npm-backup.tar.gz
# Extract
kubectl exec -n npm $(kubectl get pod -n npm -l app=nginx-proxy-manager -o jsonpath='{.items[0].metadata.name}') -- \
tar -xzf /tmp/npm-backup.tar.gz -C /
# Restart pod
kubectl rollout restart deployment nginx-proxy-manager -n npm
Performance & Scaling
Resource Limits
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
Caching
Enable caching for static assets:
- Cache Assets: ✅ in proxy host settings
- Reduces load on backend services
- Faster response times
Multiple Instances
For high availability:
replicas: 2
Requires shared storage (NFS or similar) for /data directory.
Security Considerations
- Change Default Password - First thing after installation
- Use Strong Passwords - For admin account
- Enable 2FA - If available in future versions
- Regular Updates - Keep NPM image updated
- Firewall Rules - Only expose necessary ports
- SSL Everywhere - Force HTTPS for all public services
Integration with Kubernetes
Service Discovery
NPM automatically discovers Kubernetes services via DNS:
- No need to expose services as LoadBalancer
- Use ClusterIP services
- NPM routes based on domain names
Ingress Alternative
Instead of Kubernetes Ingress:
- Use NPM for user-facing services
- Simpler SSL management
- Better UI for non-technical users
- Keep Ingress for internal services
Monitoring
Access Logs
kubectl exec -n npm $(kubectl get pod -n npm -l app=nginx-proxy-manager -o jsonpath='{.items[0].metadata.name}') -- \
tail -f /data/logs/proxy-host-*_access.log
Error Logs
kubectl exec -n npm $(kubectl get pod -n npm -l app=nginx-proxy-manager -o jsonpath='{.items[0].metadata.name}') -- \
tail -f /data/logs/proxy-host-*_error.log
Statistics
NPM provides built-in statistics:
- Requests per domain
- Bandwidth usage
- SSL certificate status
- Access via admin UI
Best Practices
- Use Descriptive Domain Names -
blog.sortium.frnotapp1.sortium.fr - Enable SSL Everywhere - Force HTTPS for all public services
- Monitor Certificate Expiry - Check renewal status regularly
- Backup Regularly - Export configuration periodically
- Use Namespaces - Organize proxy hosts logically
- Test Before Production - Verify proxy hosts work correctly
Conclusion
NPM provides an excellent solution for managing reverse proxies in a Kubernetes environment. It’s user-friendly, feature-rich, and integrates well with Kubernetes service discovery.
Key Takeaways:
- Simple web UI beats YAML editing
- Automatic SSL is a game-changer
- Kubernetes DNS makes service discovery easy
- Proper backup strategy is essential