Skip to content

Repository files navigation

Hostyt Proxy Gateway

A Caddy proxy manager - the self-hosted, multi-tenant alternative to Nginx Proxy Manager, built on Caddy.

Hostyt Proxy Gateway is a self-hosted control panel - a Caddy web UI - for a fleet of Caddy reverse-proxy nodes. Point your domains at backends through the web UI with automatic HTTPS, or run it as a hosting platform: customers get a VPS with a fixed backend IP and a fixed range of public ports and map their own domains to those ports. Scale it out and it becomes a multi-node Caddy fleet - a self-hosted CDN you operate yourself, with WireGuard tunnels to origin and per-node failover. The control plane configures every node over WireGuard, drives Let's Encrypt issuance, runs a WAF + GeoIP, and surfaces traffic stats.

Status: v1.4.8. Stack: Go 1.26.3, chi, MariaDB/MySQL or SQLite, Redis, Caddy 2.11. Single binary ~21 MB image, ~28 MB idle RAM.

Use cases

  • NPM-style single-box reverse proxy manager. Run one manager + one Caddy node and use /admin/hosts as a flat list of every domain: add a host (domain + backend IP:port), get automatic HTTPS via Caddy's on-demand TLS, and manage it from a dark/light web UI - a drop-in for anyone hand-editing an nginx or Caddy config, or currently on Nginx Proxy Manager.
  • Multi-tenant hosting-provider edge (reseller / multi-user). Give each customer a client account scoped to a plan with domain/port/RPM quotas, or use npm-kind plans where clients self-manage their own backend IP + port range from /app/services. Resellers own their own scoped clients, plans, and branding, with suspension fail-closed. Every admin action - including impersonation - is audit-logged with actor and IP, and the REST API v1 plus Terraform provider cover FOSSBilling/Hostyt-style automated provisioning.
  • Self-built multi-node CDN. Group Caddy nodes into node_groups with active_active (deploy to every node with capacity) or failover (primary + warm secondary) placement modes; the scheduler places new hosts across the group automatically. Customer/origin backends reach the fleet over a dedicated WireGuard tunnel that falls back to WSS (WebSocket-over-TLS) when UDP is blocked, so backends behind NAT or a restrictive firewall stay reachable. Certificate storage can be shared across a group (Redis-backed) so a failover or active_active peer already holds the cert before it needs to serve traffic. Node health is scraped from Caddy's Prometheus endpoint, and automatic failover moves routes off a dead node onto a healthy sibling in the same group.

Why Caddy Proxy Manager over Nginx Proxy Manager?

  • Caddy, not nginx - automatic HTTPS, HTTP/3, and a live JSON config API; no reload dance.
  • Multi-node + multi-tenant - manage a fleet of proxy nodes over a WireGuard mesh, with clients, resellers, plans, and quotas. NPM is single-node, single-tenant.
  • Built-in WAF (Coraza/OWASP CRS), GeoIP blocking, L4 stream proxying, access analytics, and an API + Terraform provider - not just a reverse-proxy form.
  • One ~21 MB Go binary, ~28 MB idle RAM.

Searching for a caddy proxy manager or an nginx proxy manager alternative? That's this.

How it compares

Fair comparison against the two closest open-source projects and the no-panel baseline. ? marks a cell we're not confident enough about to claim either way - check the project's own docs before relying on it.

Hostyt Proxy Gateway Nginx Proxy Manager GoEdge Plain Caddyfile
Multi-node fleet Yes - node groups, active_active/failover placement No (single instance) Yes - core use case (edge node cluster) No (one instance = one config)
Multi-tenant / reseller Yes - clients, plans, resellers, quotas No¹ Yes - user system for reselling bandwidth No
WAF Yes - Coraza/OWASP CRS, per-route toggle No Yes - built-in HTTP firewall No²
Geo blocking Yes - country/continent allow-deny, world map No Yes - region-based access rules No²
L4 (TCP/UDP) proxying Yes - SNI routing (caddy-l4) Partial³ ? Manual²
Tunnels to origin Yes - WireGuard, WSS fallback No ? No
API Yes - REST API v1 + Terraform provider Partial⁴ ? Yes - native Caddy Admin API
Audit trail Yes - every write logged with actor + IP No ? No
License MIT MIT Apache-2.0 Apache-2.0
  1. Per-proxy-host user permissions exist; no client-facing portal or billing/reseller model.
  2. Achievable by hand-wiring community Caddy modules and JSON/Caddyfile config; nothing built-in.
  3. Raw TCP/UDP "Streams" passthrough; no SNI-based routing.
  4. Internal REST API backs its own UI; not designed or documented for third-party provisioning.

Highlights

  • NPM-style operator surface: /admin/hosts is a flat list of every domain across every client. Add host in one form (domain + backend IP:port + node) - the implied client/plan/service rows are provisioned automatically under your admin account.
  • Self-bootstrap panel route: the install wizard pushes a virtual Caddy route for the public APP URL → app container on the first node, so you can sign in via https://proxy.example.com without adding a host first.
  • Two plan kinds:
    • restricted (default) - admin pins backend IP + port range, client picks domain + port from the range. The hosting-provider model.
    • npm - full self-service: the client may edit backend IP + port range from /app/services. Use for resellers / your own accounts.
  • Auditable client impersonation: super-admin opens /admin/users, clicks Impersonate, sees the client portal as them. Every action is audit-attributed to the admin with impersonated_user_id in meta; the impersonation banner is visible on every page. Back to admin via /auth/end-impersonation.
  • Bulk actions on hosts: enable / disable / delete many rows from the flat list, with a single Caddy resync per affected node.
  • Inline DNS pre-check on the Add-host form before submit.
  • Per-host retry triggers a DNS re-check + Caddy re-push, which is the clean unblock when Let's Encrypt has been failing for a host whose DNS is already correct.
  • One-command node join (Docker-Swarm style): operator generates a token in the panel; new VPS runs one curl | sudo bash line and is fully provisioned, including WireGuard mesh and Caddy.
  • WireGuard sidecar on the manager auto-applies peer add/remove via wg syncconf - no manual interface restart on each join.
  • Caddy Admin API driven in JSON mode, source of truth is the DB.
  • On-Demand TLS with an /internal/ask allowlist gate; ACME certificates issued automatically when a domain points at the right node.
  • 2FA: TOTP (QR enrollment + recovery codes), Email OTP, SMS OTP, WebAuthn/passkeys (discoverable login). OIDC (Authentik / Microsoft / generic), OAuth2 social login (GitHub, Google). Argon2id passwords, Redis-backed brute-force lockout (10 / 15 min), multi-provider CAPTCHA (Turnstile / hCaptcha / reCAPTCHA v3), AES-256-GCM encryption for all secrets at rest.
  • REST API v1 with bearer-token auth for FOSSBilling / Hostyt-style provisioning integrations.
  • Live admin stats: KPI cards, doughnut/line/bar charts, per-node traffic (Prometheus-scraped from Caddy).
  • Dark / light mode UI with Inter font and a consistent design system.
  • Dark-ops console UI: command palette (Cmd+K), collapsible nav groups, right-sheet drawer, teal design system.
  • AI assistant: floating bubble, multi-provider (Anthropic / OpenAI / Gemini / OpenRouter), streaming SSE, scoped read-only tool-calling, per-user rate limit.
  • WAF (Coraza): per-route toggle, rule suppression, event acknowledgement, real-time events from node-agent.
  • GeoIP blocking: country allow/deny, continent blocking, CIDR always-block/allow, configurable response code, fail-closed mode, world traffic map.
  • mTLS per route: per-tenant CA, client cert issue/revoke, path-based RBAC, enforcement audit log.
  • L4 TCP/UDP streams: SNI routing, configurable log retention.
  • Installation profiles: homelab / smallteam / advanced / provider modes with guided wizard.
  • Instance sync: master/slave HPG config replication for HA setups.
  • Multi-provider CAPTCHA: Turnstile, hCaptcha, reCAPTCHA v3 per-settings toggle.
  • Custom fields: operator-defined fields for clients and hosts (JSON-backed).
  • Advanced load balancing: uri_hash, header, cookie (with HMAC secret), query, client_ip_hash, random, random_choose, first (failover) in addition to round-robin/least-conn.
  • Server-side search/pagination/sort with saved filter presets across all list pages.
  • Host groups with filter and badge support.
  • Hourly log rollups (14-day retention) + analytics charts on host logs page.
  • Alert rules: high-error-rate detection and custom threshold alerts.
  • Client self-registration behind settings toggle.
  • Backup/restore: S3/SFTP/FTP targets, restore drill CLI.
  • Reseller multi-tenancy: resellers own scoped clients/plans/branding, fail-closed on suspension.
  • Terraform provider (terraform-provider-hpg): manage nodes, pools, plans, clients, services, routes as code.

Maturity

The feature list is wide; the exercise these features have had is not uniform. This table says what to trust with customer traffic today.

Area Status What that means
HTTP/HTTPS reverse proxy, ACME certs Stable The core path. Covered by tests and by every install.
Multi-tenant clients, plans, quotas Stable Ownership and limits are enforced at a single choke point.
Admin panel, audit log, REST API Stable
WireGuard node mesh + customer tunnels Beta Works, but node join/rotation has had less field exposure than the proxy path.
Multi-node placement, node groups Beta Capacity claims are atomic; cross-replica coordination is still single-writer per process.
Automatic failover, DNS steering Beta Exercised in tests, not yet across a long-running fleet.
WAF, L4 streams, GeoIP, HTTP cache Beta Each needs a custom Caddy module on every node; flipping the gate before the fleet is upgraded takes nodes offline.
Manual TLS certs, mTLS + path RBAC Beta
NPM import, instance sync Experimental Import has a dry run that reports exactly what it would create and what needs manual work. Review it before committing.
AI assistant Experimental Optional; off by default. Not on any path that serves traffic.
Backup/restore Beta Restore is drilled by CLI; practice it on your own data before relying on it.

Running several app replicas is supported for availability, but the config push serializes per process, so treat the control plane as single-writer.


Quick start (single host)

git clone https://github.com/host-yt/caddy-proxy-manager.git hostyt-proxy-gateway && cd hostyt-proxy-gateway
cp .env.example .env
$EDITOR .env       # at minimum: APP_URL, APP_SECRET (openssl rand -hex 32), DB_PASSWORD

docker compose -f deploy/docker-compose.yml --env-file .env up -d
open http://localhost:8080            # walks through the install wizard

After the wizard completes, sign in with the admin user you just created.

For multi-node deployments enable the WireGuard sidecar profile:

docker compose -f deploy/docker-compose.yml --env-file .env --profile mesh up -d

Then in Settings → WireGuard, fill Public endpoint (manager.example.com:51820), Save → keypair generated.

To add a remote Caddy node:

  1. Admin → Caddy nodes → Auto-join → Generate join command - shows a one-time curl … | sudo bash … line (TTL 30 min).
  2. Paste it on the new VPS as root. It installs WireGuard, Docker, Caddy, joins the mesh, and registers itself.

Full guide: docs/MULTI_NODE.md.


See it work: one customer, two nodes, a failover

Roughly 15 minutes on two cheap VPSes, and it exercises the parts that are specific to this project rather than to any proxy panel.

1. Add a customer. Admin → Clients → New client. Give it a plan (Admin → Plans) with a domain limit and a port range; that plan is what the customer is held to later, in the panel and over the API alike.

2. Join a second edge node. Admin → Caddy nodes → Auto-joinGenerate join command, run the one-liner on the VPS as root. It joins the WireGuard mesh and registers itself - and then waits: a node is not eligible for placement until you approve it (Admin → Caddy nodes → Approve, after matching the fingerprint the installer printed). A stolen join token alone never starts carrying customer traffic.

3. Reach a private backend. The customer's app does not need a public IP. Admin → the node's Tunnel modal → Enable tunnel, then Clients → the customer → WireGuard peers → add a peer and run the printed installer on the backend host. The backend now has a tunnel address on that node and nothing exposed to the internet.

4. Publish a domain. Admin → Hosts → Add host: domain, the tunnel peer as the backend, port. Point the domain's DNS at the node. Caddy issues the certificate on demand; the host page shows DNS, certificate and live requests.

5. Take the node down. docker compose stop caddy on that VPS (or pull the node's plug). Health probing marks it down; after the grace window automatic failover moves its routes to a healthy peer in the same group and pushes the config there, and the owning customer is notified.

6. Watch it come back. Start the node again. It is re-pushed from the database rather than trusted to have kept its own config, so it returns serving exactly what the panel says it should.

Two things worth noticing on the way: Admin → Audit log has every step with actor and IP, and the same six steps are available over the REST API and the Terraform provider - the panel is not the only entry point.


Repository map

cmd/server/         entrypoint (thin)
internal/
  accesslog/        access log ingest, rollups, country/ASN enrichment
  adminscope/       non-super-admin client scope enforcement
  aichat/           multi-provider AI client (Anthropic, OpenAI, Gemini, OpenRouter) + SSE streaming
  aitools/          scoped read-only DB tool-calling for AI assistant
  alert/            alert rule evaluation and notification dispatch
  audit/            audit-log writer (actor, IP, impersonator, timestamp)
  auth/             Argon2id, sessions, TOTP, Email/SMS OTP, WebAuthn, API keys, password reset
  backup/           S3/SFTP/FTP backup targets and restore drill
  caddyapi/         Caddy Admin API client + JSON config builder
  captcha/          multi-provider CAPTCHA verifier (Turnstile / hCaptcha / reCAPTCHA v3)
  chatstore/        AI conversation persistence
  cloudflare/       Cloudflare API token + CF-Connecting-IP trust toggle
  config/           env loader + validation
  customfields/     operator-defined metadata fields for clients and hosts
  deployment/       installation profiles (homelab / smallteam / advanced / provider)
  dns/              pre-flight DNS resolver + per-route resolver controls
  domain/           business logic per aggregate (routes, plans, services, nodes, …)
  geoip/            MaxMind GeoLite2-Country DB download and distribution to nodes
  httpserver/       chi router, handlers, middleware (CSRF, CSP, security headers, etc.)
  installstate/     wizard state file + AES-256-GCM crypto helpers
  instasync/        master/slave HPG config replication
  jobs/             background scheduler (key rotation, rollup, geoip update, …)
  mail/             SMTP send + email templates
  metrics/          Caddy /metrics Prometheus scraper + delta aggregator
  mtls/             per-tenant CA management, cert issue/revoke, RBAC verifier
  nodejoin/         one-time join-token mint + redeem
  oauth2x/          OAuth2 social login (GitHub, Google)
  oidc/             coreos/go-oidc wrapper, DB-backed config
  security/         scoped security checks shared across handlers
  store/            DB pool + goose migration runner
  systemevents/     operational event history storage
  view/             html/template sets per audience (install, auth, admin, app)
  wafevents/        WAF audit log ingest and event storage
  webhook/          outbound webhook delivery and retry
  wireguard/        Curve25519 keypair gen, config writer, IP allocator
cmd/node-agent/      Go agent on each Caddy node (WG sync, log forward, WAF events, GeoIP)
deploy/
  docker-compose.yml    manager stack (app + mariadb + redis + caddy + node-agent + optional wg)
  caddy/                Caddy node image (xcaddy with cache-handler + L4 modules)
  remote-node/          drop-in compose for an external Caddy node
  wireguard/            WG sidecar image (alpine + wg-tools + watch loop)
migrations/             goose .sql files (auto-applied on boot)
scripts/                node-join.sh - bash bootstrap for remote nodes
docs/                   all the docs you'll find linked below

Screenshots

Install wizard Admin dashboard
Install wizard welcome Admin dashboard
Host list New host form
Host list New host
Stats World map
Stats World map
AI assistant Audit log
AI chat Audit

Full install wizard walkthrough: docs/install_video/install_wizard.webm


Documentation

Running more than one app replica? The upgrade to v1.4.4 must be a non-rolling cutover (drain old replicas → purge legacy Redis sessions → start new replicas): a pre-1.4.4 binary ignores restricted-admin session confinement and cannot be fenced. Upgrades after this one roll normally - replicas fence themselves by session generation: only a replica that can actually serve - bound listener included - advertises one, the newest generation serves, and older replicas stop serving (503, existing connections included) and shut down. Downgrades still need a full stop. See "Upgrading" in docs/DEPLOY.md.

Doc What's in it
docs/INSTALL.md Step-by-step first-deploy
docs/DEPLOY.md Production deploy + Portainer + reverse-proxy tips
docs/MULTI_NODE.md WireGuard mesh + one-command node join
docs/ARCHITECTURE.md How the pieces talk
docs/API.md REST API v1 contract
docs/SPEC.md Functional specification
docs/SECURITY.md Threat model and security model
docs/ROUTES.md Domain/alias ownership proof, custom Caddy JSON, per-route caching
docs/WAF.md Web Application Firewall (Coraza)
docs/GEOIP.md GeoIP country filtering
docs/DNS_PROVIDERS.md DNS-01 challenge providers for wildcard TLS
docs/CLOUDFLARE.md Cloudflare API token + real client IP behind the CF proxy
docs/MTLS.md Mutual TLS / client certificate auth
docs/MANUAL_CERTS.md Import + serve your own TLS certificates (no ACME)
docs/TERRAFORM.md Terraform provider (hpg_node, hpg_client, hpg_service, ...)
docs/ANALYTICS.md Access log analytics
docs/ROADMAP.md Shipped features and planned work
docs/FEATURE_MATRIX.md HPG vs alternatives comparison
docs/TROUBLESHOOTING.md Common issues + fixes
CHANGELOG.md Notable changes
CONTRIBUTING.md How to develop / submit changes
SECURITY.md Reporting vulnerabilities

License

See LICENSE. For open-sourcing guidance see docs/OPENSOURCING.md.

About

Caddy Proxy Manager - a self-hosted, multi-tenant alternative to Nginx Proxy Manager. Point domains at backends with automatic HTTPS, WAF, GeoIP, WireGuard mesh, and an API + Terraform provider. Single Go binary.

Topics

Resources

Contributing

Security policy

Stars

12 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages