- What is OpenZiti
- What is Zero Trust Network Access (ZTNA)
- OpenZiti vs VPN: what is the difference
- What you need to install OpenZiti
- Installing the OpenZiti controller
- OpenZiti web console (ZAC)
- Installing the OpenZiti router
- First OpenZiti service: reach a home NAS without open ports
- Tunneler in the home network
- Connecting clients: Windows, macOS, Android, iOS, Linux
- OpenZiti as a VPN alternative: access to the whole home network
- OpenZiti checks and troubleshooting
- OpenZiti security: MFA, revoking access and backups
- Two-factor authentication
- Revoking access
- Backups and upgrades
- Quick OpenZiti test in Docker
- OpenZiti vs WireGuard, Tailscale and NetBird
- OpenZiti FAQ
OpenZiti is a free, open-source Zero Trust Network Access (ZTNA) platform licensed under Apache 2.0 and developed by NetFoundry. Instead of opening a whole network like a VPN does, OpenZiti grants access to specific services only, and only to the users and devices allowed by policy. The service itself needs no open inbound ports.
Updated in September 2026 for OpenZiti 2.0: installing the controller and router from the official packages, the ZAC console, a first service with no open ports, home network access instead of a VPN, troubleshooting, and a comparison with WireGuard, Tailscale and NetBird. The old Docker Compose demo walkthrough is outdated.
What is OpenZiti
OpenZiti is an overlay network on top of the regular internet. Every participant connects to it with outbound connections, so a server needs neither a public IP nor port forwarding as long as a tunneler runs next to it. An OpenZiti network is made of a few parts:
- controller — the brain of the network: it stores identities, services and policies, issues certificates and decides who may connect where;
- edge routers — carry encrypted traffic between clients and services; at least one router must be reachable from the internet;
- identities — certificate-based (x509) credentials for a user, device or server, issued through a one-time JWT enrollment token;
- tunnelers — Ziti Desktop Edge for Windows and macOS, Ziti Mobile Edge for Android and iOS, and
ziti-edge-tunnelfor Linux; they intercept application traffic and send it into the OpenZiti network; - SDKs — libraries for Go, C, Java and Kotlin, Swift, C#, Python and Node.js that embed zero trust directly into an application without a tunneler.

What is Zero Trust Network Access (ZTNA)
Zero Trust Network Access (ZTNA) is a “never trust, always verify” security model. In a classic network, a device that gets inside the perimeter through a VPN or the local network can reach almost everything. In the ZTNA model there is no trusted network at all: every connection to every service is authenticated and checked against policy, wherever the user is.
The main practical benefit of ZTNA is that services stop being visible from the internet. A port scanner will not find your admin panel, SSH or home NAS because they have no open ports. A stolen password is not enough either: without a certificate-based identity and a matching policy the service simply cannot be reached.
OpenZiti vs VPN: what is the difference
- Access to services, not networks. A VPN connects a device to a whole subnet; OpenZiti connects it only to the services allowed by policy.
- No inbound ports. A VPN server needs an open port, while services behind OpenZiti need no port forwarding: the tunneler dials out to the router itself.
- Identities instead of shared keys. Each device has its own certificate, which you can revoke without touching anyone else.
- Private DNS names. A service can get a name like
nas.ziti; the tunneler resolves it and routes the traffic into the network. - TCP and UDP only. ICMP is not carried over OpenZiti, so
pingto a service will not work — test access withcurlor a browser instead.
If all you need is a fast tunnel between your own servers, WireGuard is simpler. OpenZiti wins when you need precise control over who can reach what and want to hide services from the internet.
What you need to install OpenZiti
- A VPS with a public IP running Ubuntu 24.04 or 26.04, Debian 12 or 13 — official OpenZiti packages exist for the Debian and Red Hat families. The smallest plan is enough for a personal network;
- a domain name for the controller, for example
ziti.example.com, with an A record pointing to the server. The bootstrap script requires a permanent address: it is written into certificates and is hard to change later; - open ports
1280/tcpfor the controller and3022/tcpfor the router; - a device in your home network for the tunneler — a mini PC, a Raspberry Pi or any Linux server — if you want to reach home services.
Before installing, harden the server with the VPS security checklist: updates, SSH key login, UFW and Fail2ban.
Installing the OpenZiti controller
The official script adds the OpenZiti repository and installs the ziti CLI. Then install the controller package — run it in a normal SSH session so the bootstrap script can ask its questions:
curl -sS https://get.openziti.io/install.bash | sudo bash -s openziti
sudo apt-get install openziti-controller
On AlmaLinux and Rocky Linux use dnf install openziti-controller instead of apt-get. If the setup did not start on its own, run the script manually:
sudo /opt/openziti/etc/controller/bootstrap.bash
The script asks for:
- Are you joining an existing cluster? —
N, we create a new network. In OpenZiti 2.0 the controller always runs in cluster mode, even as a single node; - Permanent external address — the controller DNS name, for example
ziti.example.com; - Node name and Trust domain — defaults are fine;
- Port —
1280; - Admin password — let the script generate a random one and save it;
- Console — yes, to enable the ZAC web console;
- Certificate renewal timer — yes, certificates will be renewed automatically every month.
After a successful bootstrap the ziti-controller service starts on its own and the script prints a ready-made ziti edge login command. Open the port and check that the controller is listening:
sudo ufw allow 1280/tcp
sudo ss -tlnp | grep ziti
OpenZiti web console (ZAC)
The Ziti Admin Console (ZAC) is a separate package and is served on the same port as the controller:
sudo apt-get install openziti-console
Open https://ziti.example.com:1280/zac/ and log in as admin with the password from the bootstrap. The browser will warn about the certificate because the controller uses its own CA. To remove the warning, issue a Let’s Encrypt certificate and set it in alt_server_certs in /var/lib/ziti-controller/config.yml — see the console documentation.
Everything below done through the CLI can also be done in the console under Identities, Services, Service Policies and Routers. Commands are faster and easier to repeat.
Installing the OpenZiti router
The router can run on the same VPS. First log in to the controller and create the router — the command saves its enrollment token to a file. The -t flag enables the router’s built-in tunneler:
ziti edge login ziti.example.com:1280 -u admin -p 'your_password'
ziti edge create edge-router vps-router -t -o /tmp/vps-router.jwt
Install the router package and run the bootstrap:
sudo apt-get install openziti-router
sudo /opt/openziti/etc/router/bootstrap.bash
The script asks for the router’s permanent external address (a DNS name or the server IP), the port — 3022 by default — and the enrollment token: give it the path /tmp/vps-router.jwt. The router reads the controller address from the token. Open the port and check that the router is online — the ONLINE column should say true:
sudo ufw allow 3022/tcp
ziti edge list edge-routers
Finally, allow identities and services to use the routers. For a personal network the simplest option is to allow everything — fine-grained access is still controlled by service policies:
ziti edge create edge-router-policy all-routers-all-identities \
--edge-router-roles '#all' --identity-roles '#all'
ziti edge create service-edge-router-policy all-routers-all-services \
--edge-router-roles '#all' --service-roles '#all'
First OpenZiti service: reach a home NAS without open ports
Goal: open a NAS web UI at 192.168.1.10:5000 in the home network from a laptop, without port forwarding on the home router and even without a public IP. Home Assistant, SSH, the Proxmox web UI or any other TCP service is published the same way.
Create two identities: laptop for the laptop with the clients attribute, and home-gw for the home tunneler with the home-hosts attribute. Attributes let you write policies for groups instead of individual devices:
ziti edge create identity laptop -a clients -o laptop.jwt
ziti edge create identity home-gw -a home-hosts -o home-gw.jwt
Now describe the service. The intercept.v1 config tells the client which address to intercept (nas.ziti:5000), and host.v1 tells the tunneler where to send the traffic in the home network:
ziti edge create config nas.intercept intercept.v1 \
'{"protocols":["tcp"],"addresses":["nas.ziti"],"portRanges":[{"low":5000,"high":5000}]}'
ziti edge create config nas.host host.v1 \
'{"protocol":"tcp","address":"192.168.1.10","port":5000}'
ziti edge create service nas --configs nas.intercept,nas.host -a home-services
And two policies: Dial lets clients connect to services with the home-services attribute, and Bind lets the home tunneler host them:
ziti edge create service-policy home-dial Dial \
--service-roles '#home-services' --identity-roles '#clients'
ziti edge create service-policy home-bind Bind \
--service-roles '#home-services' --identity-roles '#home-hosts'

Tunneler in the home network
Copy home-gw.jwt to your home Linux server or Raspberry Pi, install ziti-edge-tunnel and add the identity:
curl -sS https://get.openziti.io/install.bash | sudo bash -s ziti-edge-tunnel
sudo systemctl enable --now ziti-edge-tunnel.service
sudo ziti-edge-tunnel add --jwt "$(< home-gw.jwt)" --identity home-gw
The tunneler only needs outbound connections to ports 1280 and 3022 on your VPS — nothing has to be opened on the home router. Packages exist for x86_64 and arm64; see the Linux tunneler documentation. The JWT token is single-use: once enrolled it is no longer needed, and a new device gets a new identity.
Connecting clients: Windows, macOS, Android, iOS, Linux
- Windows — install Ziti Desktop Edge, choose to add an identity and select
laptop.jwt; - macOS — Ziti Desktop Edge from the App Store, adding an identity works the same way;
- Android and iOS — the Ziti Mobile Edge app from Google Play or the App Store: add the identity from the JWT file or by scanning the QR code shown on the identity page in ZAC;
- Linux — the same
ziti-edge-tunnelas at home.
Once enrolled, open http://nas.ziti:5000 in a browser. If the page loads, the service works through OpenZiti while the NAS stays invisible from the internet.
OpenZiti as a VPN alternative: access to the whole home network
If you have many services, you do not have to describe each one. A single service can cover the whole subnet — you get something like a VPN, but only for the home network, while all other traffic goes direct:
ziti edge create config lan.intercept intercept.v1 \
'{"protocols":["tcp","udp"],"addresses":["192.168.1.0/24"],"portRanges":[{"low":1,"high":65535}]}'
ziti edge create config lan.host host.v1 \
'{"forwardProtocol":true,"allowedProtocols":["tcp","udp"],"forwardAddress":true,"allowedAddresses":["192.168.1.0/24"],"forwardPort":true,"allowedPortRanges":[{"low":1,"high":65535}]}'
ziti edge create service home-lan --configs lan.intercept,lan.host -a home-services
A host.v1 config with forwardAddress and forwardPort forwards traffic to exactly the address and port the client asked for, but only inside the allowed subnet. The service has the home-services attribute, so the Dial and Bind policies created earlier already apply to it.
- If the network you are on also uses
192.168.1.0/24, addresses will clash. It is better to give the home network an unusual subnet such as192.168.77.0/24; pingto home devices will not work — OpenZiti carries TCP and UDP only;- narrow services like
nas.zitiare safer: a subnet service exposes everything in it to the client. Give subnet access to your own devices only.
If you only need Home Assistant, there is a simpler option — CloudPub for remote access without a public IP.
OpenZiti checks and troubleshooting
The policy-advisor command shows which services an identity can use and through which routers, and list terminators shows who is hosting a service right now. No terminators means the home tunneler is not connected:
ziti edge policy-advisor identities laptop -q
ziti edge list terminators 'service.name="nas"'
journalctl -u ziti-controller.service -n 50
journalctl -u ziti-router.service -n 50
Common problems:
- The service does not show up in the client — the identity has no Dial policy, or the router policies from the router section were not created;
- The service is visible but does not open — there is no Bind policy, the tunneler is offline, or
host.v1has the wrong address or port; - The router or client cannot connect — ports 1280 or 3022 are closed on the VPS, or the controller DNS name does not point to the server;
- Enrollment fails — the JWT token was already used or has expired: recreate the identity;
- ping does not answer — that is expected, ICMP is not carried over OpenZiti.
OpenZiti security: MFA, revoking access and backups
Two-factor authentication
An identity certificate lives on the device, so if a laptop or phone is stolen it helps to add a second factor. OpenZiti does this with an MFA posture check attached to the Dial policy:
ziti edge create posture-check mfa mfa-check -a mfa
ziti edge update service-policy home-dial --posture-check-roles '#mfa'
After that, services open only for identities that enabled MFA in their properties in Ziti Desktop Edge or Ziti Mobile Edge and scanned the QR code with an authenticator app such as Aegis or 2FAS. Other check types — OS version, a running process, Windows domain — are described in the posture checks documentation.
Revoking access
If a device is lost, delete its identity — access is revoked right away and active connections are closed. Other devices keep working; there is no shared key to rotate like with a VPN:
ziti edge delete identity phone
Backups and upgrades
All controller state — PKI, configuration and database — lives in /var/lib/ziti-controller. Losing this directory means re-enrolling every router and device, so keep a copy off the server:
sudo systemctl stop ziti-controller
sudo tar czf /root/ziti-controller-$(date +%F).tar.gz -C /var/lib ziti-controller
sudo systemctl start ziti-controller
OpenZiti packages are upgraded together with the system. Upgrade the controller first, then routers and tunnelers:
sudo apt update && sudo apt upgrade
You can hide other services on your server from the internet the same way — for example, Nextcloud or Matrix Synapse for your family or team.
Quick OpenZiti test in Docker
To just try OpenZiti, you can run the controller and router in a single container. The developers call this a quickstart and recommend it for learning rather than long-term use:
mkdir ziti && cd ziti
wget -O compose.yml https://get.openziti.io/dock/all-in-one/compose.yml
docker compose up -d
The controller listens on port 1280, the router on 3022 and the ZAC console at https://localhost:1280/zac/; the login is admin and the default password is admin. To set your own, create a .env file next to compose.yml with ZITI_PWD=your_password before starting. The demo with “blue” and “red” networks from the old version of this article is still described in the documentation, but for real use install the packages as shown above.
OpenZiti vs WireGuard, Tailscale and NetBird
A quick look at how OpenZiti compares with popular alternatives:
| Solution | Access model | Self-hosted | Ports on the service side | Best for |
|---|---|---|---|---|
| OpenZiti | individual services by policy | yes, fully open source | none needed | real zero trust and SDKs for your own apps |
| WireGuard | VPN tunnel to a network | yes | a UDP port on at least one side | a fast, simple VPN between your own nodes |
| Tailscale | WireGuard mesh with ACLs | no, coordination runs in Tailscale’s cloud | usually none | a quick start without your own server |
| Headscale | self-hosted coordination server for Tailscale clients | yes | a port on the Headscale server | a Tailscale-style network under your control |
| NetBird | WireGuard mesh with policies and SSO | yes, or cloud | usually none | a mesh network with a friendly web UI |
| Cloudflare Zero Trust | ZTNA as a cloud service | no | none needed | teams that want a ready-made cloud service |
OpenZiti takes more effort to set up than Tailscale or NetBird, but it runs entirely on your own servers, exposes no ports for services, and lets you embed zero trust directly into your own application through its SDKs.
OpenZiti FAQ
Is OpenZiti free?
Yes. OpenZiti is open source under the Apache 2.0 license, with no limits on users or devices. NetFoundry sells a cloud service and commercial support built on it.
How is OpenZiti different from a VPN?
A VPN connects a device to a whole network, while OpenZiti connects it only to the services it is allowed to use. Each device has its own certificate-based identity, and services need no open inbound ports.
Does OpenZiti need a public IP?
Only the server running the controller and router needs a public IP, for example a cheap VPS. Home servers and clients do not: tunnelers connect to the router with outbound connections.
Which ports does OpenZiti use?
By default the controller uses 1280/tcp and the router uses 3022/tcp. The ZAC console is served on the controller port under /zac/.
Which OpenZiti clients are available?
Ziti Desktop Edge for Windows and macOS, Ziti Mobile Edge for Android and iOS, and ziti-edge-tunnel for Linux. There are also SDKs for embedding into your own applications.
Why does ping not work over OpenZiti?
OpenZiti carries TCP and UDP only; ICMP does not enter the network. Test access to services with curl, a browser or SSH.
Can I enable two-factor authentication in OpenZiti?
Yes. Create an MFA posture check and attach it to the Dial policy. Users enable MFA in their Ziti client and confirm sign-in with a code from an authenticator app.
How do I revoke access for a lost device?
Delete its identity with ziti edge delete identity or in the ZAC console. Access is revoked immediately, active connections close, and other devices keep working.
No email, no trackers — just the update feed.








