Remote Access#

What Is It?#

Remote access lets you reach your apps from anywhere on the internet — not just from your local network. When you expose an app, it becomes accessible at its public subdomain (e.g., https://jellyfin.yourdomain.ca) from your phone, a coffee shop, or anywhere else.

Remote access sits on top of your normal networking — it doesn’t replace the router or DNS, it just adds an internet-side entry point for apps you explicitly expose.

You interact with the feature through the psw remote command group. You never deal with the underlying pieces directly.

Why Not Just Open Ports?#

Opening ports on your home router exposes your entire network to the internet. A tunnel is safer:

  • Your home IP stays hidden — traffic goes through a rented server (the VPS — Virtual Private Server, a small server you rent in the cloud with a public IP address).
  • Only the apps you explicitly expose are reachable.
  • All traffic is encrypted via WireGuard (a fast, modern VPN — Virtual Private Network, i.e. an encrypted tunnel between two machines across the public internet).
  • No port forwarding needed on your router.

How It Works#

Under the hood the feature is assembled from two pieces of plumbing. You do not install or configure these yourself — they are owned by the remote-access feature and deployed automatically:

  • Pangolin (tunnel server) runs on a bare target — typically a cheap VPS with a public IP address (e.g., from Hostinger, Hetzner, or DigitalOcean). It is the public-facing entry point that receives internet traffic and forwards it through WireGuard tunnels to your self-hosted solution.
  • Newt (tunnel client) runs on each managed target that has exposed apps. It establishes a WireGuard tunnel back to Pangolin and forwards traffic to the local apps.

Both are marked managed_by: remote-access in their app metadata , which is why they do not appear in psw app list and cannot be added with psw app add. The feature deploys them for you.

The Flow#

Internet user visits jellyfin.yourdomain.ca
    │
    ▼
VPS (bare target) — Pangolin receives the request
    │
    ▼ WireGuard tunnel (encrypted)
    │
LXC target (media) — Newt receives and forwards to Jellyfin on port 8920
    │
    ▼
Jellyfin responds, traffic flows back through the tunnel

If you have exposed apps on multiple targets , each target runs its own Newt instance with its own tunnel:

VPS (Pangolin)
    ├── WireGuard tunnel → Core target (Newt) → Forgejo, Dashboard
    └── WireGuard tunnel → Media target (Newt) → Jellyfin, Sonarr

Getting a VPS Ready#

Before you can expose anything you need a bare target to run Pangolin on. The psw vps command group handles the provider-level lifecycle of that server — OS reinstall, reset, and SSH (Secure Shell — the protocol PSW uses to connect to remote machines) verification. Today it supports Hostinger ; other providers plug in as additional subcommands.

# Wipe and reinstall the OS on the VPS registered in network.yml
psw -C ~/my-project vps reinstall --confirm

# Start over from a clean slate
psw vps reset 167.88.46.88 --confirm

The VPS itself is declared as a bare target in network.yml:

targets:
  tunnel-us:
    type: bare
    ip: 203.0.113.45
    ssh_user: root

You register it once with psw target add --type bare tunnel-us --ip 203.0.113.45. Pangolin is then deployed to that target by the feature — no psw app add pangolin needed (and it would be refused anyway).

Setting Up the Feature#

Once the VPS exists and is SSH-reachable, tell the feature which target runs Pangolin:

psw -C ~/my-project remote setup              # single bare target, auto-picked
psw -C ~/my-project remote setup --vps tunnel-us   # explicit target

This is declarative — it writes services/pangolin/service.yml on your behalf (the feature is allowed to install its own plumbing; psw app add pangolin is not). The next convergence actually deploys Pangolin.

If you prefer the dashboard: navigate to /remote, fill in the VPS name, click Set up Pangolin. Same flow, same outcome.

Exposing an App#

To make an app accessible from the internet:

psw -C ~/my-project remote expose jellyfin

This is declarative — it only writes configuration. The actual tunnel deployment happens during convergence .

What Happens#

  1. PSW reads the app’s metadata to find its port and subdomain.
  2. Writes a remote_access block to the service manifest:
# services/jellyfin/service.yml
name: jellyfin
target: media
state: present
remote_access:
  enabled: true
  pangolin_target: tunnel-us
  subdomain: jellyfin
  port: 8920
  1. Syncs the Pangolin and Newt deployment recipes so convergence can deploy them.

What Convergence Does#

On the next convergence run:

  1. Deploys Pangolin on the VPS (if not already running).
  2. For each target with exposed apps:
    • Registers a site with Pangolin’s API (Pangolin’s name for “one target’s tunnel endpoint”).
    • Deploys Newt on the target.
    • Creates a Pangolin resource for each exposed app (subdomain → port mapping).
    • Establishes the WireGuard tunnel.

After convergence, jellyfin.yourdomain.ca is live from the internet.

TLS Certificates Survive VPS Reinstalls#

Every convergence reads the latest Let’s Encrypt acme.json from the VPS and snapshots it — encrypted with SOPS (a tool that encrypts secrets so they can live safely in a git repo) — to secrets/acme.json. If you later wipe the VPS with psw vps reinstall, the next convergence restores those certs before Traefik requests fresh ones — avoiding rate-limit pain after a rebuild. There is no manual snapshot step.

Unexposing an App#

psw -C ~/my-project remote unexpose jellyfin

This removes the remote_access block from the service manifest. On next convergence , the Pangolin resource is cleaned up. If no more apps on that target are exposed, Newt is removed too.

Checking Status#

At any time, see what the feature is doing:

psw remote status            # fast — reads project files, returns in milliseconds
psw remote status --deep     # slow — also probes Pangolin HTTP, Newt SSH, and the VPS provider API

You get back the target running Pangolin, the list of exposed apps, the state of each Newt tunnel, and the TLS cert expiry. The dashboard /remote page shows the same data with live refresh buttons.

When Things Go Wrong#

Two recovery commands handle the usual failure modes:

  • psw remote rotate-certs — clears the cached acme.json on both the project and the VPS, then restarts Traefik so it re-registers with Let’s Encrypt on the next HTTPS request. Use it when Traefik ended up with a staging cert or a stale account. Heads-up: Let’s Encrypt rate-limits duplicate certs (5 per domain per week on production), so use sparingly.
  • psw remote rotate-vps — the one-shot “rebuild the VPS” flow. Snapshots live certs → psw vps reinstall (OS wipe) → redeploys Pangolin. Certs survive end-to-end because the pangolin role restores them from secrets/acme.json before Traefik starts. Takes ~6–10 minutes.

Both are also on the dashboard /remote page as buttons, behind a retype-IP confirm guard.

Prerequisites#

  1. A bare target with a public IP — a VPS from any cloud provider, registered with psw target add --type bare <name> --ip <ip>.
  2. OS prepared on the VPS — run psw vps reinstall --confirm once.
  3. Feature set up — run psw remote setup (see above) to register the Pangolin target.
  4. WAN DNS provider configured (recommended) — so public DNS records and SSL certificates work automatically. Without WAN DNS, the tunnel still works but you would have to manage DNS records and certificates manually.

Targeting a Specific Pangolin VPS#

If you have multiple bare targets with Pangolin (e.g., US and EU), psw remote expose auto-picks when there is only one. With multiple, specify explicitly:

psw remote expose jellyfin --pangolin-target tunnel-us

DNS and Certificates#

Remote access works together with providers for a complete setup:

  • Local DNS resolves jellyfin.yourdomain.ca → internal Traefik IP (when you’re at home).
  • WAN DNS resolves jellyfin.yourdomain.ca → Pangolin’s public IP (when you’re away).
  • ACME provides valid TLS certificates so HTTPS works on both paths.

The same URL works from anywhere — at home it goes directly through internal Traefik, from the internet it goes through the Pangolin tunnel.

Quick Reference#

TermWhat It Is
PangolinTunnel server on the VPS. Managed by the remote-access feature.
NewtTunnel client on each target with exposed apps. Managed by the remote-access feature.
psw remote setup [--vps <name>]Install the feature on a bare target — writes services/pangolin/service.yml on your behalf.
psw remote status [--deep]Show what’s running, what’s exposed, cert state, tunnel health. --deep probes Pangolin HTTP, Newt SSH, and the provider API.
psw remote expose <app>Mark an app for internet access (declarative, file-only).
psw remote unexpose <app>Remove an app from internet access.
psw remote rotate-certsForce Traefik to re-request certs from Let’s Encrypt (use sparingly: rate-limited).
psw remote rotate-vpsSafe VPS rebuild: snapshot → reinstall → redeploy. TLS certs preserved end-to-end.
psw vps reinstallWipe and reinstall the OS on the bare target (provider-level operation).
psw vps reset <host>Wipe all containers/data on a VPS (destructive).
Dashboard /remoteSame feature surface with live status, expose toggles, and destructive-action modals.
remote_accessBlock in service.yml that configures the tunnel for an app.
managed_by: remote-accessMeta.yml field that marks Pangolin and Newt as feature-owned plumbing.
WireGuardFast, modern VPN protocol used for the encrypted tunnel between Pangolin and Newt.