Howto Install Router

Hy folks,
i am new to ziti - i want to install ziti on two regions to get a resilient mesh for connecting my services.

DE: Controller & Router
FR: Router
... that should be my base-environment

From home (i also have static IP there) i want to add my local network via tunnler (edge?)

I have studied the documentation for several hours, but its really confusing to me.

tunnler, edge, edge-tunnler, fabric, router, edge-router, router with tunnler - hä?

I installed the controller successfully in DE, want to add a router on the same server as well as on the FR server.
When installing i cam across the following issues:

Sorry, I really don't want to be nasty or unkind, but I'm missing a simple big picture here, an overview of all these things. Can someone help me? Thank you... cheers

Hey there and welcome, @berndinox. I welcome your insights on the deployment docs. I'm working on those as we speak.

It sounds like you followed the Linux controller guide and are ready to also follow the Linux router guide.

I'll assume you're adding the first router to your ziti network. There are many router options and the defaults will work for most cases, certainly for the first router.

At a minimum, you need to set two things in bootstrap.env file before the first run.

  • Set ZITI_CTRL_ADVERTISED_ADDRESS to the FQDN of the controller
  • Set ZITI_ENROLL_TOKEN to the enrollment token you got from administratively creating the router

Do you see how to get the token with ziti edge create edge-router?

If you say --tunneler-enabled when you create the router then you can also set ZITI_ROUTER_MODE=host in bootstrap.env before the first run, or modify the generated /var/lib/ziti-router/config.yml if you've already run it once.

1 Like

Hi there!

I also started 1-2 weeks ago. Thats also for me a little bit confusing...

Have you amd64 or arm64 architecture?

I have made a manual for me but it is based in the Foundry Auto Enroll Script.

If you have amd64 i can post it here.

@berndinox You also asked about the router modes.

Router: fabric or edge

The ziti router is versatile, and typical routers provide fabric and edge. Fabric links between routers compose the mesh data plane. Identities dialing or binding services use the edge function, which acts as an on-and-off-ramp for the fabric.

Tunneler: Enabled or not?

The router can act as a TCP/UDP proxy. Ziti calls this "tunneling."

Router Mode: none, tproxy, proxy host

This is the proxy mode (default: none). tproxy is transparent proxy and requires extra permissions and DNS setup. host is reverse proxy mode and doesn't need any special permissions or DNS. proxy is a special mode that binds a specific ziti service to a TCP port as a raw socket proxy.

@fre4ki X86 - would be great if you can share your docs :slight_smile:

@qrkourier thanks for your replay!

So if i‘d like to create a „base Network“ with some POPs, for getting short paths and high resilience i would configure those nodes as Fabric Routers. One of those also holds the controller.

At the Location (my homelab) where i would like to Access differen services i would join as Private Router with tunnel Mode (Host Mode).

The Clients accessing the services will just need the Tunnel Client (eg ZitMobile Edge)

Does this makes sense?
BR Bernd

The auto enroll tool is another way to generate a router config. It's intended for use on a Linux VM.

If you decide to use it, ensure bootstrapping is disabled in service.env, i.e., ZITI_BOOSTRAP=false.

Instead of bootstrapping, you'll need to run the auto enroll tool with the --installDir /var/lib/ziti-router option to place the generated config in the ziti-controller.service working directory.

Whats the recommended way?
Please also see my Response above :slight_smile:

Thanks a lot for your Input!!

1 Like

Let's start with generating a basic config from env vars with the Linux package's built-in bootstrapper. You can always tailor the generated config in /var/lib/ziti-router/config.yml no matter what you used to generate that file, but the auto enroll tool is predates and is mutually exclusive to the Linux router package (DEB/RPM). I just raised this GitHub issue to explore maybe using the auto enroll tool together with the Linux package. For now, I recommend that you use the Linux package described in the deployment guide.

That's a good idea: place a ziti router strategically to short-circuit the data paths.

No problem running controller and router on the same host as long as you use separate TCP ports. They're separate by default.

Right, the router in your homelab network with tunnel mode "host" gives you a reverse proxy into that private network.

Correct: the clients can run any Ziti tunneler to reach the private services in your homelab.

1 Like

Great, thanks for your recommendation.
Just one last question, whats the impact / happening when switching a Router to „Fabric“?

„ Not including the edge section will start the controller in "fabric only" mode and will not support any edge functionality or concepts (edge SDK connectivity).“

What does „edge“ functionality provide me?

When i want to connect a native App outside of my homelab this reads like „i need edge service on my main mesh network“ - is that assumption right?

If you remove the edge config section, the router will only serve as an intermediary fabric node in the data mesh. It will not provide any service dialing or termination for identities because those are functions of the ziti edge.

Everything will work if all routers provide both fabric and edge and all identities are allowed by policy to use all routers. The reasons for disabling fabric link or edge listeners or limiting which identities can use which routers are nuanced and probably aren't important for small scale deployments.

And sorry for my stupid questions :sweat_smile:

Are you kidding!? Bring it on. There are no stupid questions here.

Good morning,

is it useful to install crowdsec or other IDS/IPS systems to the Controller/Public Router?

Or is it not necessary?

IDS and IDP are still relevant tools for a ziti system. I'd be interested if you feel like sharing your configs in a new post about using ziti with a popular open-source IDS like Snort or Suricata.

For example, ziti has a configurable rate limit for TLS negotiations. You could program the IDS to alarm when the limit is breached.

  handshakeTimeout: 15s

    # if disabled, no tls handshake rate limiting with be enforced
    enabled: true
    # the smallest window size for tls handshakes
    minSize: 5
    # the largest allowed window size for tls handshakes
    maxSize: 5000
    # after how long to consider a handshake abandoned if neither success nor failure was reported
    timeout: 30s

@berndinox here is my manual.

Set Hostname and Timezone

hostnamectl set-hostname
timedatectl set-timezone Europe/Berlin

Optional Tools

apt install nano mc htop btop iputils-ping net-tools tcpdump nmap iptraf-ng iperf3 -y

Ziti requirements

apt install tar hostname jq curl iptables -y


export EXTERNAL_IP="$(curl -s"

Installation : Controller

export ZITI_HOME="/etc/ziti/ct"

Using Public-Ports for Edge-Clients


Start the Installation

source /dev/stdin <<< "$(wget -qO-"; expressInstall

Install Service Controller

sudo mv "${ZITI_HOME}/${ZITI_CTRL_NAME}.service" /etc/systemd/system/ziti-controller.service
sudo systemctl daemon-reload
sudo systemctl enable --now ziti-controller

check Service Controller

sudo systemctl -q status ziti-controller --lines=0 --no-pager

Installation : Public-Router

cd ~
tar xf ziti_router_auto_enroll.tar.gz

Register Router and start Onboarding Process

export ROUTER_NAME="MyRouterName"
export ADM_PW="MyControllerAdminPassword"
export EXTERNAL_IP="$(curl -s"

sudo ./ziti_router_auto_enroll -f \
--controller MyControllerFQDN \
--controllerFabricPort 45880 \
--controllerMgmtPort 443 \
--adminUser admin \
--adminPassword $ADM_PW \
--routerName $ROUTER_NAME \
--installDir /etc/ziti/pr \
--ctrlPingCheckInterval 10 \
--heartbeatIntervalSeconds 10 \
--autoTunnelListener \
--csrCountry DE \
--csrProvince BY \
--csrLocality 'MyLocation' \
--csrOrganization 'MyOrg' \
--csrOrganizationalUnit 'MyOrgUnit'

Services Reload

systemctl daemon-reload



Check Service after Reboot

systemctl status ziti-router

1 Like

Thanks for sharing your manual, @fre4ki .

For context, this shows a way to install both the controller and router in Linux with the expressInstall() quickstart which predates and is mutually exclusive to installing the production Linux packages. The packages provide the binary, systemd service, and optionally generate a config, and they probably were not yet available when most people started using the quickstart(s).

The auto-enroll tool is also mutually exclusive to the Linux router package because it installs the binary and a systemd service.

Here are the production deployment guides for Linux: Deploying on Linux | OpenZiti

In that category are migration guides for controller and router. Follow the migration examples if you started with the expressInstall() quickstart and want to migrate to the production Linux package and service.

for me it was the simplest way to start. It is already described in the quickstart but maybe it helps.

My Design was a own Controller VM, 2 Public Routers (all Ubuntu 24.04, Controller with arm64 and 2 Public-Routers with x86).

Regarding to my manual, is there a way to use also the IPv6 address in the certificate?

At the moment just the IPv4 address will be in the certificate.

x509's SAN extension permits an IPv4 or IPv6 value for an IP subject alternative name, but I recommend using a DNS SAN instead, so you're never locked into a specific IP address.

Since IPv6 is allowed in IP SANs, the next question is how I can use an IPv6 address for my controller.

The answer depends on how you create your controller configuration. All controller deployments I've seen have some helper script that issues the controller's certificate.

Most use an env var like ZITI_CTRL_ADVERTISED_ADDRESS or EXTERNAL_DNS that set the DNS SAN on the controller's server certificate. Some have a separate var specifically for setting an IP SAN.

The configuration generators of the Linux controller and router production deployments do not currently support IP SAN, only DNS SAN.

If there's no other way to create a DNS record, you can work around that with a "magic wildcard" DNS SAN like, which resolves an AAAA request for any subdomain, e.g., "ctrl1", to IPv6 address 2a01:4f8:c17:b8f::2.

Here's another example that works with local IPv6 address ::1:

Let me know if IP SAN support is important to your use case. I have an idea for making that possible in the production deployments.

EDIT: disclaimer about always use a DNS name you control for something important. This magic wildcard DNS provider is great for testing, and fine for labs when not using any publicly routable IP addresses.