Run the controller on 443 with the console on an internal port
End state: the Ziti controller (edge client API, management API, and router control plane) is served on external
443, while the Traefik ingress fronting the Ziti Console is on an internal-only port (8443 in the examples).
Applies to NetFoundry Self-Hosted 1.1.0 and later, except 2.0.0, where the console's HTTPS redirect ignores the
moved port.
Prerequisites
- K3s already running. This procedure reconfigures the Traefik ingress that K3s ships, so K3s must be up before you
start Step 1 — do not rely on
nf-quickstartto install it here, because Traefik must exist first. If you don't have K3s yet, install it the same way the installer does, then continue:curl -sfL https://get.k3s.io | sh -s - --disable local-storagenf-quickstartdetects and reuses the existing K3s cluster. - A DNS name for the controller that resolves to the node's public address. IP addresses are not supported as the controller advertise address.
- NetFoundry registry credentials (
REGISTRY_USERNAME/REGISTRY_PASSWORD) — required to install the Ziti Console Enterprise. - The support stack installed (the default). The installer deploys the Enterprise console only alongside the support stack.
Step 1: Move the Traefik ingress to 8443 (before installing)
On a single K3s node, the controller's LoadBalancer service and the Traefik ingress LoadBalancer both want host
port 443 — they cannot both have it. Move Traefik's HTTPS (websecure) entrypoint off 443 first, so 443 is
free for the controller. Create a K3s HelmChartConfig that overrides the entrypoint:
cat <<'EOF' | kubectl apply -f -
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
ports:
websecure:
exposedPort: 8443
EOF
If kubectl get helmchartconfig traefik -n kube-system already returns one, add the ports block to its
valuesContent with kubectl edit helmchartconfig traefik -n kube-system instead, so the settings already in it are
kept.
K3s reconciles Traefik automatically. Confirm the ingress now serves on 8443 and 443 is free:
kubectl get svc traefik -n kube-system -o jsonpath='{.spec.ports}' | tr ',' '\n' | grep -A2 websecure
# websecure ... "port":8443 ...
curl -sk -o /dev/null -w "%{http_code}\n" https://localhost:8443/ # 404 (Traefik up, no route yet)
curl -sk -o /dev/null -w "%{http_code}\n" https://localhost:443/ # connection refused (443 free)
Step 2: Install with the controller on 443
Run the quickstart and set the controller client API port to 443.
Interactive:
nf-quickstart
When prompted for "What would you like to use as the clientApi port?" enter 443. Provide your controller DNS
name and registry credentials when asked, and leave the support stack enabled.
Non-interactive (env file):
cat > env-quickstart.sh <<'EOF'
export CTRL_ADDR=controller.example.com # your controller DNS name
export CLIENT_PORT=443
export REGISTRY_USERNAME='<your-registry-username>'
export REGISTRY_PASSWORD='<your-registry-password>'
EOF
nf-quickstart -y -f env-quickstart.sh
After the install, the host port map looks like this:
| Host port | Service | Exposure |
|---|---|---|
443 | Ziti controller (edge client API, management API, router control plane) | External |
8443 | Traefik ingress → Ziti Console (Enterprise) | Internal only |
3022 | Ziti router edge listener (client data plane) | External (if clients connect through this appliance) |
The Enterprise console is reachable at https://<controller-dns>:8443/.
Step 3: (Recommended) Disable the legacy console on 443
The controller ships a legacy console (ZAC) served on the controller's own port at
https://<controller-dns>:443/zac/. Because it rides the controller port, it is reachable externally on 443. For
this deployment pattern — where the console must be internal-only — disable it so nothing console-related is exposed on
443. The Enterprise console on 8443 is unaffected.
Persist the setting in controller-values.yml — do not use a transient helm --set. nf-upgrade re-applies the
controller from controller-values.yml, so a --set flag is reverted (and ZAC comes back) on the next upgrade. Edit
controller-values.yml (in the install directory, reachable with nf) and add:
console:
enabled: false
Then re-apply only the controller:
nf-upgrade -y --controller
kubectl rollout status deploy/ziti-controller -n ziti
Because the setting lives in controller-values.yml, it survives future nf-upgrade runs.
Step 4: Firewall / security group
"Internal only" is enforced at your network boundary — the port split does not restrict access by itself:
- Open
443inbound from the internet (controller edge/management API + router control plane). - Open
3022inbound if endpoints connect to the router on this appliance. - Do NOT open
8443to the internet — leave it reachable only from inside your network so the console stays internal.
Verification
Run these from a host on the internal network (so 8443 is reachable):
H=controller.example.com
curl -sk -o /dev/null -w "controller :443/version -> %{http_code}\n" https://$H:443/version # 200
curl -sk -o /dev/null -w "legacy ZAC :443/zac/ -> %{http_code}\n" https://$H:443/zac/ # 404 (disabled in Step 3)
curl -sk -o /dev/null -w "console :8443/ -> %{http_code}\n" https://$H:8443/ # 302 (login redirect)
Confirm the controller answers a Ziti login on 443:
ziti edge login $H:443 -u admin -p <admin-password>
The admin password is stored in the ziti-controller-admin-secret secret; nf-install-notes prints it.
Notes
- Console DNS. On K3s the console ingress uses the controller DNS name by default. To give the console its own
internal hostname, edit
ingress.tlsHostsinziti-console-enterprise-values.yml(in the install directory, reachable withnf) and runhelm upgrade ziti-console-enterprise ./helm-charts/ziti-console-enterprise/ -n ziti --values ziti-console-enterprise-values.yml. - Any ingress port works.
8443is used here as an example; substitute any free port in theHelmChartConfigand your firewall rules. - HTTPS redirect follows the port. The console's HTTP-to-HTTPS redirect targets Traefik's HTTPS port, read at
install and on each
nf-upgrade. If you move the port after installing, runnf-upgradeagain. See Console HTTPS redirect port.
Validated end-to-end on a clean K3s v1.35.5 install with NetFoundry Self-Hosted 1.1.0 (ziti-controller chart 3.2.0):
controller reachable and Ziti login succeeding on 443, legacy ZAC returning 404 after Step 3, and the Enterprise
console reachable only on 8443.