Pangolin on Your VPS Series
    Part 6 of 6

    Production Hardening

    SSO integration, CrowdSec threat blocking, automated backups, safe update procedures, and monitoring for a production-ready deployment.

    40 minutes
    Running Pangolin instance

    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

    1. Google Cloud Console → APIs & Services → Credentials
    2. Create an OAuth 2.0 Client ID
    3. Redirect URI: https://pangolin.yourdomain.com/api/v1/auth/oidc/callback
    4. Set Issuer URL to https://accounts.google.com

    Microsoft Entra ID

    1. Azure portal → Register a new application
    2. Redirect URI: same callback URL as above
    3. Create a client secret under "Certificates & secrets"
    4. 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.

    Add CrowdSec to docker-compose.yml
      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
    Enable Traefik access logging
    # config/traefik/traefik.yml
    accessLog:
      filePath: /var/log/traefik/access.log
      bufferingSize: 100
    Generate bouncer API key
    mkdir -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-bouncer
    Traefik bouncer middleware (config/traefik/dynamic/crowdsec.yml)
    http:
      middlewares:
        crowdsec-bouncer:
          forwardAuth:
            address: http://crowdsec-bouncer:8080/api/v1/forwardAuth
            trustForwardHeader: true
    Verify CrowdSec
    docker compose exec crowdsec cscli metrics
    docker compose exec crowdsec cscli decisions list

    VPS-Level Hardening

    Fail2ban for SSH

    apt install fail2ban -y
    systemctl enable fail2ban
    systemctl start fail2ban
    fail2ban-client status sshd

    Automatic Security Updates

    apt install unattended-upgrades -y
    dpkg-reconfigure -plow unattended-upgrades

    Restrict SSH by IP (optional)

    ufw delete allow 22/tcp
    ufw allow from <your-ip> to any port 22 proto tcp

    Backup Strategy

    What to Back Up

    PathContents
    ~/pangolin/config/All configuration files
    ~/pangolin/data/pangolin/db.sqlite3Pangolin database (users, resources, policies)
    ~/pangolin/docker-compose.ymlContainer definitions
    ~/pangolin/.envAPI keys and secrets
    Automated backup script (/usr/local/bin/pangolin-backup.sh)
    #!/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"
    Schedule and sync off-site
    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-backups

    Restore 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/health

    Updating Pangolin & Newt

    Pangolin

    cd ~/pangolin
    # Run backup first!
    sudo ./installer --update

    After updating: verify containers are running, check logs, test dashboard login, test a sample resource.

    Newt

    Systemd-based 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 newt
    Docker-based Newt
    docker compose pull newt
    docker compose up -d newt

    Monitoring

    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 logrotate for 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.