Production Hardening
SSO integration, CrowdSec threat blocking, automated backups, safe update procedures, and monitoring for a production-ready deployment.
Single Sign-On with External Identity Providers
Pangolin supports any OIDC-compatible identity provider. Navigate to Identity Providers → Add Identity Provider and fill in Client ID, Secret, Issuer URL, and scopes (openid email profile).
Google Workspace
- Google Cloud Console → APIs & Services → Credentials
- Create an OAuth 2.0 Client ID
- Redirect URI:
https://pangolin.yourdomain.com/api/v1/auth/oidc/callback - Set Issuer URL to
https://accounts.google.com
Microsoft Entra ID
- Azure portal → Register a new application
- Redirect URI: same callback URL as above
- Create a client secret under "Certificates & secrets"
- Issuer URL:
https://login.microsoftonline.com/<tenant-id>/v2.0
Authentik (Self-hosted)
Create an OAuth2/OIDC Provider in Authentik, set the redirect URI, copy credentials to Pangolin. Gives you centralized user management, MFA in one place, and group-based access mapping.
2FA for Local Accounts
If staying with internal accounts, enable TOTP 2FA for all users — especially the admin account. A compromised admin account means a compromised access control layer.
CrowdSec Integration
CrowdSec analyzes Traefik logs in real time, detects malicious patterns, blocks IPs, and shares threat intelligence with a global community network.
crowdsec:
image: crowdsecurity/crowdsec:latest
container_name: crowdsec
restart: unless-stopped
environment:
- COLLECTIONS=crowdsecurity/traefik
- GID=1000
volumes:
- ./config/crowdsec:/etc/crowdsec
- ./data/crowdsec:/var/lib/crowdsec/data
- ./logs/traefik:/var/log/traefik:ro
networks:
- pangolin_net
crowdsec-bouncer:
image: fbonalair/traefik-crowdsec-bouncer:latest
container_name: crowdsec-bouncer
restart: unless-stopped
environment:
- CROWDSEC_BOUNCER_API_KEY=${CROWDSEC_BOUNCER_API_KEY}
- CROWDSEC_AGENT_HOST=crowdsec:8080
networks:
- pangolin_net
depends_on:
- crowdsec# config/traefik/traefik.yml
accessLog:
filePath: /var/log/traefik/access.log
bufferingSize: 100mkdir -p ~/pangolin/logs/traefik
docker compose up -d crowdsec
docker compose exec crowdsec cscli bouncers add traefik-bouncer
# Copy the API key, then:
echo "CROWDSEC_BOUNCER_API_KEY=<your-api-key>" >> ~/pangolin/.env
docker compose up -d crowdsec-bouncerhttp:
middlewares:
crowdsec-bouncer:
forwardAuth:
address: http://crowdsec-bouncer:8080/api/v1/forwardAuth
trustForwardHeader: truedocker compose exec crowdsec cscli metrics
docker compose exec crowdsec cscli decisions listVPS-Level Hardening
Fail2ban for SSH
apt install fail2ban -y
systemctl enable fail2ban
systemctl start fail2ban
fail2ban-client status sshdAutomatic Security Updates
apt install unattended-upgrades -y
dpkg-reconfigure -plow unattended-upgradesRestrict SSH by IP (optional)
ufw delete allow 22/tcp
ufw allow from <your-ip> to any port 22 proto tcpBackup Strategy
What to Back Up
| Path | Contents |
|---|---|
| ~/pangolin/config/ | All configuration files |
| ~/pangolin/data/pangolin/db.sqlite3 | Pangolin database (users, resources, policies) |
| ~/pangolin/docker-compose.yml | Container definitions |
| ~/pangolin/.env | API keys and secrets |
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/var/backups/pangolin"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
PANGOLIN_DIR="/home/pangolin/pangolin"
mkdir -p "$BACKUP_DIR"
# Stop Pangolin briefly for a consistent database snapshot
docker compose -f "$PANGOLIN_DIR/docker-compose.yml" stop pangolin
# Archive config and data
tar -czf "$BACKUP_DIR/pangolin-$TIMESTAMP.tar.gz" \
-C "$PANGOLIN_DIR" \
config/ \
data/pangolin/db.sqlite3 \
docker-compose.yml \
.env 2>/dev/null || true
# Restart Pangolin
docker compose -f "$PANGOLIN_DIR/docker-compose.yml" start pangolin
# Remove backups older than 30 days
find "$BACKUP_DIR" -name "pangolin-*.tar.gz" -mtime +30 -delete
echo "Backup completed: $BACKUP_DIR/pangolin-$TIMESTAMP.tar.gz"chmod +x /usr/local/bin/pangolin-backup.sh
# Add to root crontab:
# 0 2 * * * /usr/local/bin/pangolin-backup.sh >> /var/log/pangolin-backup.log 2>&1
# Off-site sync with rclone:
rclone sync /var/backups/pangolin remote:your-bucket/pangolin-backupsRestore Procedure
cd ~/pangolin
docker compose down
tar -xzf /var/backups/pangolin/pangolin-<timestamp>.tar.gz -C ~/pangolin/
docker compose up -d
docker compose ps
curl -k https://pangolin.yourdomain.com/api/v1/healthUpdating Pangolin & Newt
Pangolin
cd ~/pangolin
# Run backup first!
sudo ./installer --updateAfter updating: verify containers are running, check logs, test dashboard login, test a sample resource.
Newt
curl -fsSL https://github.com/fosrl/newt/releases/latest/download/newt-linux-amd64 \
-o /usr/local/bin/newt
chmod +x /usr/local/bin/newt
systemctl restart newtdocker compose pull newt
docker compose up -d newtMonitoring
Lightweight approaches that work alongside Pangolin:
- Uptime Kuma: Self-hosted uptime monitor — can itself be exposed through Pangolin. Monitor dashboard and individual resource availability.
- Traefik health endpoint:
http://<vps-ip>:8080/ping - Log management: Configure
logrotatefor Docker logs to prevent disk bloat.
Series Recap
- Part 1: Architecture — Pangolin + Gerbil + Traefik + Newt and why a VPS is the right control plane location
- Part 2: Running Pangolin on RamNode with DNS, firewall, and auto-start
- Part 3: Private networks connected via Newt (home lab, Docker host, remote server)
- Part 4: Web applications at HTTPS subdomains with identity-based access control
- Part 5: Client-based private access to SSH, databases, RDP, and network ranges
- Part 6: SSO, CrowdSec, automated backups, and reliable update process
The result: a zero-trust access layer running on a single VPS — no subscription fees, no limits on services, no third-party in the path of your traffic.
