Pangolin on Your VPS Series
    Part 4 of 6

    Exposing Web Applications

    Create HTTPS resources with automatic SSL, identity-based access control, and app-specific proxy configurations.

    30 minutes
    Connected Newt site required

    Understanding Resources

    A resource is a specific service exposed through a site's tunnel. Every resource has:

    • A domain (e.g., nextcloud.yourdomain.com)
    • A target (internal IP/hostname and port)
    • Authentication settings (who can access it)
    • A site association (which Newt tunnel routes to it)

    Creating Your First HTTPS Resource

    1. Navigate to ResourcesCreate Resource
    2. Select HTTP/S Resource
    3. Choose the connected site
    4. Enter the subdomain and target upstream address

    Target examples:

    • Same host as Newt: http://localhost:8080
    • Another LAN machine: http://192.168.1.50:8096
    • Docker container: http://jellyfin:8096

    Common Application Configurations

    Nextcloud

    Subdomain: nextcloud · Target: http://localhost:80

    Required Nextcloud config (config/config.php)
    'trusted_proxies' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'],
    'trusted_domains' => ['nextcloud.yourdomain.com'],
    'overwrite.cli.url' => 'https://nextcloud.yourdomain.com',
    'overwritehost' => 'nextcloud.yourdomain.com',
    'overwriteprotocol' => 'https',
    'forwarded_for_headers' => ['HTTP_X_FORWARDED_FOR'],

    Jellyfin

    Subdomain: jellyfin · Target: http://localhost:8096

    In admin → Networking: leave Base URL blank, add VPS IP to Known Proxies. Streaming works through the tunnel without special configuration.

    Gitea

    Subdomain: git · Target: http://localhost:3000

    app.ini
    [server]
    ROOT_URL = https://git.yourdomain.com/
    DOMAIN = git.yourdomain.com
    PROTOCOL = http  ; Gitea is HTTP; Pangolin/Traefik handles TLS

    SSH Git access requires TCP pass-through — see Part 5.

    Portainer

    Subdomain: portainer · Target: https://localhost:9443

    Enable "Skip TLS Verification" — Portainer serves HTTPS with a self-signed cert by default.

    Grafana

    Subdomain: grafana · Target: http://localhost:3000

    grafana.ini
    [server]
    root_url = https://grafana.yourdomain.com
    serve_from_sub_path = false

    Home Assistant

    Subdomain: ha · Target: http://localhost:8123

    configuration.yaml
    http:
      use_x_forwarded_for: true
      trusted_proxies:
        - 172.16.0.0/12
        - 10.0.0.0/8

    WebSocket connections for real-time updates work automatically.

    Access Control

    Protected (default)

    Users must authenticate with a Pangolin account. Unauthenticated requests redirect to login.

    Public

    No authentication. Anyone with the URL can access the resource.

    Passcode

    Simpler alternative to full auth. Good for guest access without Pangolin accounts.

    IP Whitelist

    Restrict to specific IP ranges regardless of authentication. Combine with auth for defense in depth.

    Share Links

    For temporary access without an account, create a Share Link from the resource page. Set expiry time, max use count, and optional passcode.

    Custom Domains

    1. Create an A record for the custom domain pointing to your VPS IP
    2. In Pangolin → Domains → add the domain
    3. When creating/editing a resource, select the custom domain

    Pangolin requests a separate Let's Encrypt certificate automatically.

    WebSocket & Long-Lived Connections

    Traefik handles WebSocket upgrades natively. Apps known to work well: Home Assistant, Uptime Kuma, code-server, Netdata, Cockpit. If you see WebSocket failures, ensure the upstream uses http:// not https://.

    Health Checks

    Advanced resource settings (YAML view)
    healthCheck:
      path: /health
      interval: 30s
      timeout: 5s

    Debugging Resource Routing

    1. Can you reach the service locally? curl -v http://localhost:8096
    2. Check Traefik logs: docker compose logs traefik 2>&1 | grep -i "error\|warning"
    3. Check Pangolin logs: docker compose logs pangolin 2>&1 | tail -50
    4. Check Newt logs: journalctl -u newt -n 30
    5. Check Traefik route table: curl http://localhost:8080/api/http/routers | python3 -m json.tool