Complete Guide: Secure Application Deployment with Pangolin(Cloud VPS) and NPM(Home Deployment)
This comprehensive guide will walk you through setting up a secure application infrastructure using Pangolin for tunneling and Nginx Proxy Manager (NPM) for reverse proxying.
We’ll break this into two main parts.
Part 1: Pangolin Configuration
Let’s start by setting up the Pangolin side of our infrastructure.
Initial Pangolin Setup
First, we need to create a site and set up the proper resources in Pangolin.
Creating a Site
- Log into your Pangolin dashboard
- Navigate to Sites and select “Add Site”
- Fill in the following details:
Name: Internal Applications Description: Hosts internal services behind NPM Connection Method: Newt (recommended for better stability) - After creation, you’ll receive Newt credentials. Save these securely:
PANGOLIN_ENDPOINT=your-pangolin-instance.com NEWT_ID=generated-id NEWT_SECRET=generated-secret
Setting Up Resources
For each application, we need to create a corresponding resource in Pangolin. Let’s set up resources for common applications:
Uptime Kuma
- Go to Resources → Add Resource
- Configure:
Name: Uptime Kuma Subdomain: uptime (will create uptime.yourdomain.com) Site: Internal Applications Under Connectivity: - Enable SSL: Yes - Method: HTTP - IP/Hostname: 10.24.7.96 - Port: 89
Read down for explanation.
Nextcloud Resource
- Go to Resources → Add Resource
- Configure:
Name: Nextcloud Subdomain: cloud (will create cloud.yourdomain.com) Site: Internal Applications Under Connectivity: - Enable SSL: Yes - Method: HTTP - IP/Hostname: nginx-proxy-manager - Port: 80
Gitea Resource
- Add another resource:
Name: Gitea Subdomain: git Site: Internal Applications Under Connectivity: - Enable SSL: Yes - Method: HTTP - IP/Hostname: nginx-proxy-manager - Port: 80
WordPress Resource
- Add resource:
Name: WordPress Subdomain: blog Site: Internal Applications Under Connectivity: - Enable SSL: Yes - Method: HTTP - IP/Hostname: nginx-proxy-manager - Port: 80
Authentication Configuration
For each resource, configure authentication under the Authentication tab:
-
Platform SSO:
Use Platform SSO: Enabled Allow Public Access: Disabled -
Additional Authentication (optional):
Email Whitelist: Add approved email domains Pin Code: Set up for quick access Custom Password: For shared access
SSL Configuration
In Pangolin’s Traefik configuration, ensure proper SSL handling:
- Edit
traefik_config.yml:certificatesResolvers: letsencrypt: acme: email: your-email@domain.com storage: /letsencrypt/acme.json httpChallenge: entryPoint: web entryPoints: web: address: ":80" websecure: address: ":443" http: tls: certResolver: letsencrypt
Part 2: NPM and Application Deployment
Now let’s set up the local infrastructure with NPM and our applications.
Network and Base Infrastructure
First, create the required Docker network:
docker network create npm_network --external
External Application Compose Configuration
Create a Uptime Kuma docker-compose.yml file:
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: always
ports:
- "3010:3001"
volumes:
- ./uptime-kuma:/app/data
- /var/run/docker.sock:/var/run/docker.sock
networks:
- npm_network
volumes:
uptime-kuma:
networks:
npm_network:
name: npm_network
Docker Compose Configuration
Create a docker-compose.yml file:
networks:
npm_network:
external: true
volumes:
npm_data:
npm_letsencrypt:
nextcloud_data:
gitea_data:
wordpress_data:
wordpress_db:
services:
nginx-proxy-manager:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
- '89:80'
- '449:443'
- '81:81'
environment:
DISABLE_DEFAULT_SERVER: "true"
LE_EMAIL: "your-email@domain.com"
volumes:
- npm_data:/data
- npm_letsencrypt:/etc/letsencrypt
networks:
- npm_network
newt:
image: fosrl/newt:latest
container_name: newt
restart: unless-stopped
environment:
- PANGOLIN_ENDPOINT=your-pangolin-instance.com
- NEWT_ID=your-newt-id
- NEWT_SECRET=your-newt-secret
networks:
- npm_network
# Application Services
nextcloud:
image: nextcloud:latest
container_name: nextcloud
restart: unless-stopped
volumes:
- nextcloud_data:/var/www/html
networks:
- npm_network
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: unless-stopped
volumes:
- gitea_data:/data
networks:
- npm_network
wordpress:
image: wordpress:latest
container_name: wordpress
restart: unless-stopped
environment:
WORDPRESS_DB_HOST: wordpress-db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: secure_password
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress_data:/var/www/html
networks:
- npm_network
wordpress-db:
image: mariadb:latest
container_name: wordpress-db
restart: unless-stopped
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: secure_password
MYSQL_ROOT_PASSWORD: very_secure_root_password
volumes:
- wordpress_db:/var/lib/mysql
networks:
- npm_network
NPM Proxy Host Configuration
Access NPM’s admin interface (port 81) and configure each application:
Uptime Kuma Proxy Host
Domain Names: uptime.yourdomain.com
Scheme: http
Forward Hostname: 10.24.7.96
Forward Port: 3010
Advanced: Enable WebSocket Support
Nextcloud Proxy Host
Domain Names: cloud.yourdomain.com
Scheme: http
Forward Hostname: nextcloud
Forward Port: 80
Advanced: Enable WebSocket Support
Nextcloud Configuration
location / {
proxy_pass http://nextcloud:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Nextcloud-specific headers
proxy_set_header X-Forwarded-Host $host;
client_max_body_size 512M;
# WebDAV support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Gitea Proxy Host
Domain Names: git.yourdomain.com
Scheme: http
Forward Hostname: gitea
Forward Port: 3000
Advanced: Enable WebSocket Support
Gitea Configuration
location / {
proxy_pass http://gitea:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Git-specific settings
proxy_set_header X-Forwarded-Host $host;
client_max_body_size 512M;
# WebSocket support for Gitea
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
WordPress Proxy Host
Domain Names: blog.yourdomain.com
Scheme: http
Forward Hostname: wordpress
Forward Port: 80
WordPress Configuration
location / {
proxy_pass http://wordpress:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WordPress-specific settings
client_max_body_size 128M;
proxy_read_timeout 90;
}
Starting the Infrastructure
-
Launch the stack:
docker-compose up -d -
Verify services:
docker-compose ps -
Check Newt connection:
docker logs newt
Imp-- Pangolin and NPM Integration - Understanding Network Architecture
When connecting services through Pangolin and NPM, we need to consider two distinct scenarios: services that run inside our Docker network stack and services that run outside of it. Let’s understand both approaches.
Internal Services (Within Docker Network)
When your applications run inside the same Docker network as NPM, they communicate using Docker’s internal networking. In this case:
networks:
npm_network:
external: true
services:
nginx-proxy-manager:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx-proxy-manager
ports:
- '89:80' # External mapping
- '449:443' # External mapping
- '81:81' # Admin interface
networks:
- npm_network
nextcloud:
image: nextcloud:latest
container_name: nextcloud
networks:
- npm_network # Same network as NPM
In Pangolin, configure your resource like this:
Name: Nextcloud
Target Configuration:
- Method: HTTP
- IP/Hostname: nginx-proxy-manager
- Port: 80 # Internal Docker port, not the mapped port
This works because both containers can communicate directly through Docker’s internal network, where NPM listens on its default port 80.
External Services (Outside Docker Network)
For services running outside the Docker network (like on your local network or another server), we need to use the external mapped ports:
services:
nginx-proxy-manager:
image: 'jc21/nginx-proxy-manager:latest'
ports:
- '89:80' # This mapping becomes important
In Pangolin, configure your resource like this:
Name: External Service
Target Configuration:
- Method: HTTP
- IP/Hostname: 10.24.7.96 # Actual IP of the server
- Port: 89 # External mapped port
Here, we use port 89 because the service must connect through NPM’s external interface, not through Docker’s internal networking.
Understanding the Difference
Let’s break down why this matters:
-
Internal Services:
- Use container names for hostnames
- Use default ports (80, 443)
- Benefit from Docker’s DNS resolution
- More secure as traffic stays within Docker network
-
External Services:
- Use actual IP addresses
- Use mapped ports (89, 449)
- Must consider network routing
- Traffic flows through external network interfaces
Here’s a practical example combining both approaches:
services:
nginx-proxy-manager:
image: 'jc21/nginx-proxy-manager:latest'
ports:
- '89:80'
- '449:443'
networks:
- npm_network
# Internal service example
wordpress:
image: wordpress:latest
networks:
- npm_network
# Pangolin configurations:
# For WordPress (internal)
Resource 1:
Name: WordPress
Target: nginx-proxy-manager:80
# For External App(Uptime Kuma)
Resource 2:
Name: UPTIME
Target: 10.24.7.96:89
This understanding helps you properly configure your resources in Pangolin based on where your services are located relative to your NPM installation. Remember: internal services use port 80, external services use port 89.
The key is remembering that Docker’s internal networking operates independently of the port mappings we create for external access. This is why internal services can use the default ports while external services must use the mapped ports.
Post-Deployment Verification
- Access each application through its domain
- Verify SSL certificates are working
- Test Pangolin authentication
- Confirm application functionality
Maintenance Procedures
Regular maintenance tasks:
-
Update containers:
docker-compose pull docker-compose up -d -
Backup volumes:
docker run --rm -v npm_data:/data -v /backup:/backup alpine tar czf /backup/npm-backup.tar.gz /data -
Monitor logs:
docker-compose logs -f
This setup provides a secure, maintainable infrastructure for hosting multiple applications behind Pangolin’s secure tunnel and NPM’s reverse proxy. The configuration can be extended to add more applications as needed, with each benefiting from the central authentication and SSL termination provided by Pangolin.












