An identity aware reverse proxy with a built in WireGuard tunnel.
A self hosted Go hub for TLS, per route authentication, and routing,
plus an optional node agent for machines behind NAT or CGNAT.
Overview · Features · Installation · Use cases · Documentation · Security
Portlyn is a self hosted reverse proxy that combines routing, TLS, identity, and a WireGuard tunnel in a single Go binary. It exposes private services on your own domain over HTTPS, gates them with the authentication method that fits each route, and can reach machines behind NAT or CGNAT through an embedded userspace tunnel.
It is built for homelabs and small teams that want one process instead of stitching together Traefik, Authelia or Authentik, Crowdsec, and WireGuard separately. Configuration lives in an admin UI; routes, identity providers, certificates, nodes, and audit are all first class resources.
The hub runs as a standalone binary on a Linux host or as a Docker Compose stack with PostgreSQL, Loki, Alloy, and Grafana. The node agent is a separate small binary that dials out to the hub over WireGuard, so the machines behind the firewall never listen for inbound traffic.
- Per service routes by domain and path
- Access modes:
public,authenticated,restricted - Access methods per route: session, OIDC SSO, PIN, email code, magic link
- User groups and service groups for reusable policy
- Access windows with timezone aware weekday and time ranges
- IP allow and block lists, GeoIP allow and block lists per service
- CrowdSec LAPI integration with periodic decisions pull
- Local password authentication with bcrypt
- OIDC SSO with role claim mapping and allowed email domains
- TOTP MFA with recovery codes
- WebAuthn passkeys parallel to TOTP, registration and login
- Magic link sharing per service for one off access without an account
- Email code based one time passwords for routes that do not require accounts
- Optional mandatory MFA for admins with a bootstrap wizard that cannot be skipped before enrollment
- ACME with HTTP-01 and DNS-01 challenges
- Wildcard certificates through DNS-01
- DNS providers: Cloudflare, Hetzner DNS, AWS Route 53, DigitalOcean DNS
- Multi SAN issuance, Let's Encrypt production and staging
- Automatic renewal, manual renew, retry, sync status, PEM import
- DNS provider credentials encrypted at rest with AES-256-GCM and Argon2id derived keys
- Userspace WireGuard server in the same process, backed by
wireguard-goand gVisor netstack - No kernel module and no
wg-quickglue. The tunnel itself runs in userspace; binding privileged ports may still need root, Linux capabilities, or a fronting proxy depending on the deployment - Per node WireGuard keypairs that never leave the node, enrollment via single use tokens
- Per service routing through a tunnel node by setting
node_idon the service - Subnet routing so a node can advertise LAN subnets that the mesh can reach
- Roaming clients with a WireGuard config and QR code for the official WireGuard app
- Hash chained audit log with previous hash verification
- Webhook fan out to Slack, Discord, ntfy, and generic JSON, signed with HMAC-SHA256
- Per service exposure scanner with DNS, TLS, header, redirect, and auth posture checks
- Access tester and per request decision trace for troubleshooting policy denials
- Releases are built from GitHub Actions and signed with Cosign keyless via Sigstore
- Self update verifies the SHA-256 checksum and the full Sigstore certificate chain through sigstore-go against an embedded TUF trust root before atomic swap
- No telemetry, no analytics SDK, no automatic update checks
Single binary on Linux:
curl -L https://github.com/invaliduser231/Portlyn/releases/latest/download/portlyn-linux-amd64 -o portlyn
chmod +x portlyn
sudo mv portlyn /usr/local/bin/portlyn
sudo portlyn init
sudo portlynportlyn init is an interactive wizard. It generates secrets, writes a .env file, prepares the data directory, and creates the admin account.
For production installs, verify the SHA-256 checksum and the Sigstore signature of the binary before running it. The full verification procedure is in docs/INSTALL.md.
Docker Compose with the published image:
git clone https://github.com/invaliduser231/Portlyn.git
cd Portlyn
cp .env.docker.example .env.docker
# edit secrets and admin credentials in .env.docker
docker compose --env-file .env.docker up -dThe Compose stack pulls ghcr.io/invaliduser231/portlyn:latest by default. Pin a specific tag with PORTLYN_IMAGE_TAG=v1.2.3.
Detailed steps for release verification with Cosign, node agent enrollment, configuration, and the production checklist are in docs/INSTALL.md.
- Expose a homelab service on your own domain without opening inbound ports. A node agent on the home machine dials the hub on a VPS; the hub terminates TLS and forwards traffic through the tunnel.
- Put authentication in front of any internal tool, whether or not it has its own login. A PIN or one time email code is enough for low friction sharing; OIDC SSO and passkey based MFA cover the admin and team cases.
- Simplify smaller self hosted setups that would otherwise need Traefik, Authelia or Authentik, and WireGuard as separate components plus their glue.
- Not a CDN and not protection against volumetric L3/L4 DDoS
- Not a Web Application Firewall; it does not inspect request bodies
- Not multi tenant; all admins see all services
- Not a mature HA edge cluster yet; a single hub today
- Not a replacement for fixing vulnerable upstream applications
- Installation and configuration
- Production hardening
- Security policy and threat model
- Release process
- Backup and restore
- HA deployment notes
- Secret rotation
- Break glass recovery
- OpenAPI specification
- Changelog
Issues, PRs, and discussions are welcome. See CONTRIBUTING.md and the Code of Conduct. To report a vulnerability privately, see SECURITY.md.
Portlyn is released under the MIT License. See LICENSING.md for details.







