Self-hosted service
wiredoor/wiredoor avatar
wiredoor/wiredoor

Wiredoor: a self-hosted ingress layer that dials out over WireGuard

Self hosted ingress-as-a-service platform that allows you to expose applications and services running in private or local networks to the internet

1,620 stars78 forksTypeScriptApache-2.0

At a glance

What is it?
Wiredoor bundles a WireGuard tunnel, an NGINX reverse proxy and a web UI into one ingress-as-a-service server, so private services reach the internet without inbound firewall rules. The design is sound for homelabs and small clusters, but the traffic path and the public server make it the wrong tool for some setups.
Who is it for?
Adopt Wiredoor if you already run a Linux VPS with a public IP and want HTTP services, Kubernetes dashboards or IoT endpoints reachable without opening ports on the private side. Do not adopt it if your services must stay unreachable from the public internet, or if you need a managed control plane, because Wiredoor Server itself is the entry point for all external traffic.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem Wiredoor addresses: inbound reachability without inbound ports

Most private services live behind NAT or a firewall that does not accept inbound connections. The usual fixes are port forwarding, a cloud load balancer, or a full site-to-site VPN. Wiredoor takes a fourth route. The README describes it as a self-hosted ingress-as-a-service platform that exposes applications running in private or local networks to the internet through reverse VPN connections powered by WireGuard, with services published through a built-in NGINX reverse proxy. The intended audience is stated plainly: developers, operators or teams that want full control of their ingress without relying on public cloud solutions. The operative word is self-hosted. You run the entry point yourself, on a host you control, and the private node initiates the tunnel. That inverts the usual direction of connection setup, which is what removes the need to open ports on the private side. It does not remove the need for a publicly reachable machine somewhere, and the README is explicit that Wiredoor Server is the entry point for all external traffic, so its public IP or domain must be accessible from the internet.

How the traffic path works: WireGuard in, NGINX out

The architecture visible in the repository description and README has two cooperating pieces. Wiredoor Server runs on a publicly reachable host and terminates inbound connections on ports 80 and 443, plus a UDP port for the VPN, default 51820. The private node runs wiredoor-cli, which registers the node with the server and connects it automatically over WireGuard. Once that tunnel exists, the server can reach services on the private node, and NGINX on the server side performs the reverse proxy step that maps an external hostname to an internal address and port. The CLI call wiredoor http myapp --domain app.your.domain.com --port 3000 is the concrete expression of that mapping: a local process listening on port 3000 becomes reachable at a public hostname. Certificates are handled on the server side, with Let's Encrypt for public domains and a self-signed fallback for local or internal names, and the README states that renewal and configuration are automatic. Websocket connections are supported out of the box. The topics list on the repository adds the remaining vocabulary: api-server, ingress-service, management-system, nginx, reverse-proxy, tunneling, vpn, wireguard. Nothing in the supplied material describes the internal API surface or the database behind the management system, so the control plane beyond the CLI and web UI is not something I can characterise.

Getting the server and the CLI running

The quickstart splits cleanly into a server side and a node side. On the server, clone the separate docker-setup repository, copy the example environment file, and edit it. The README names the keys you are expected to set: an admin email and password, a VPN public hostname or IP, and optionally TCP_SERVICES_PORT_RANGE, ADDITIONAL_TCP_SERVICES_PORTS, or both. Then docker compose up -d brings the stack up, and the web UI is reachable at https://your_wiredoor_domain_or_ip using those admin credentials. The constraint attached to the port settings is easy to miss and worth repeating: every configured TCP service port must also be published in the ports: section of docker-compose.yml. Setting TCP_SERVICES_PORT_RANGE without editing the compose file produces a server that accepts the configuration and never receives the traffic. On the private node, the installer is a shell script fetched with curl and piped to sh, or a package from the GitHub releases page. Then wiredoor login --url=https://your_deployed_wiredoor_domain_or_ip prompts for the admin credentials, asks for a node name defaulting to the current hostname, registers the node on the server and connects it over WireGuard. Exposing a service is a single command. The README attaches a warning to the domain argument: app.your.domain.com must point to the public Wiredoor server's IP address, not to the private node.

Where the design constrains you

The public server is a single point of failure and a single point of exposure. Because Wiredoor Server is the entry point for all external traffic, its availability determines whether every exposed service is reachable, and its address is what every service domain must resolve to. Losing that host takes down all published services at once, regardless of how many nodes are connected. That is a different failure shape from a mesh VPN, where a lost relay degrades rather than severs. The port configuration has a second sharp edge. TCP services outside the HTTP path depend on TCP_SERVICES_PORT_RANGE and ADDITIONAL_TCP_SERVICES_PORTS being declared in the environment and mirrored in the compose file, which means adding a database or MQTT listener is a two-file edit plus a container restart, not a dashboard action. The README lists example values of 32760-32767 and 1883,8883,5432,5683,5684, which suggests the intended cases are message brokers, databases and IoT protocols rather than arbitrary port ranges. There is also a documentation gap that the README itself acknowledges: a section headed Coming soon promises more documentation, production setup examples and deployment tools. For a project whose server sits on the public internet, production hardening guidance is the material you would most want to read before exposing anything real, and it is not there yet. Finally, exposing a service through Wiredoor means it becomes reachable from the public internet by design. If the requirement is private access only, this is the wrong tool and a conventional VPN client is the right one.

OAuth2 in front of a service, and what it does not cover

Wiredoor integrates oauth2-proxy to put OAuth2 authentication in front of a domain or service, with Google, GitHub and similar providers named as options, and the setting is per domain and per service rather than global. That granularity matters in practice. A Kubernetes dashboard and an internal metrics endpoint rarely warrant the same access policy, and a per-service switch lets you put a provider login in front of the former while leaving the latter on network-level controls. The limitation is that this is an authentication gate at the proxy, not an authorisation model. The README does not describe roles, group mapping or per-user policies, so anything finer than authenticated-or-not has to live in the upstream application. Treat the OAuth2 feature as a way to keep anonymous traffic off a service, not as a replacement for the application's own access control.

How this differs from Cloudflare Tunnel and from plain WireGuard

The closest managed alternative is Cloudflare Tunnel, which also avoids inbound ports by having a local daemon dial out to a provider's edge. The difference is where the traffic terminates and who holds the certificate keys. With Cloudflare, the edge is Cloudflare's and the hostname and TLS termination live in their account; with Wiredoor, the edge is your VPS, the NGINX instance is yours, and the Let's Encrypt certificates are issued to your server. That is the whole point of the project's self-hosted framing, and it is also the whole cost: you own the uptime, the patching and the abuse handling for a machine that is, by definition, publicly reachable. The other comparison is plain WireGuard. A WireGuard tunnel gives you network reachability between peers, and nothing above it. You still need NGINX or Caddy, a certificate workflow, and a way to map hostnames to internal addresses. Wiredoor is essentially that assembly, packaged with a web UI, a CLI and a node registry, which is why the repository carries ingress-service and management-system as topics. If you already run that assembly and are happy maintaining it, Wiredoor replaces working configuration with a system you have to learn. If you have not built it, Wiredoor is the shorter path.

Maintenance, release cadence and the licence change

The release history in the supplied material shows v1.7.1 in April 2026, v1.7.2 in June 2026 and v1.7.3 in July 2026, with the repository's last push in September 2026. That is a steady minor-release rhythm rather than a long-stable line, which means upgrade work is an ongoing cost and not a one-off. Because the server is a Docker Compose stack from a separate docker-setup repository, upgrades involve pulling new images and re-checking that your local edits to the environment file and the ports: section still match what the new compose file expects. The port mirroring requirement is the part most likely to break silently across an upgrade. On licensing, the README states that as of version 1.5.1 Wiredoor is licensed under Apache-2.0, that versions before 1.5.1 remain under the MIT License, and that from 1.5.1 onward contributions and distributions must comply with Apache-2.0. The stated reason is alignment with the CNCF ecosystem. For most users this is a permissive licence either way, but if you are redistributing a modified build or embedding the code, the version you forked determines which terms apply, and that is a question for your own legal review rather than something to settle from a README.

Editorial conclusion

Adopt Wiredoor if you already run a Linux VPS with a public IP and want HTTP services, Kubernetes dashboards or IoT endpoints reachable without opening ports on the private side. Do not adopt it if your services must stay unreachable from the public internet, or if you need a managed control plane, because Wiredoor Server itself is the entry point for all external traffic. Before committing, verify two things on your own host: that every port listed in TCP_SERVICES_PORT_RANGE and ADDITIONAL_TCP_SERVICES_PORTS is also published in the ports: section of docker-compose.yml, and that your domain resolves to the Wiredoor server rather than the private node.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. wiredoor/wiredoor on GitHub
Community notes

Community notes