Tier-3 Tunnels
Use external tunnels when your web app, notebook, dashboard, or other HTTP service is running outside a PSILON-managed VM. The common PSI CMS Tier-3 case is a process on t3ui06.psi.ch or t3ui07.psi.ch, but the same setup works from any machine where your zrok2 environment is enabled.
This is separate from PSILON VM sharing. Use the dashboard Share button for services running inside PSILON-managed VMs. Use this page for services running on Tier-3 UI hosts such as t3ui06.psi.ch or t3ui07.psi.ch.
This page starts after you already have shell access to the machine where your service will run. For PSI Tier-3 login, bastion, host, and SSH setup details, use the PSI Tier-3 access documentation: Connecting to the user interface hosts by jumping through the bastion host connection.
Dashboard
Open the dashboard and go to Tunnels:
- retrieve your zrok2 publisher token;
- view tunnels owned by your zrok2 account;
- view tunnels shared with you;
- add or remove collaborators for tunnels you own.
Tunnel ownership follows your zrok2 account, not the machine where the tunnel was started.
Install the helper
On the host that runs your service, install zrok2 and auto-tunnel.sh into your user environment:
bash <(curl -sL https://docs.psilon.dev/install-auto-tunnel.sh)The installer supports Linux and macOS. It reuses an existing zrok2 when the required PSILON version is already available, otherwise it installs the required release into ~/.local/bin.
Make sure ~/.local/bin is on your PATH, then open a new shell or source your shell profile. The helper checks for newer published versions when it starts and updates itself automatically when its install directory is writable. Set AUTO_TUNNEL_SELF_UPDATE=0 to disable that check for a run.
Enable zrok2
In app.psilon.dev/tunnels, click Create / Retrieve Token and run the bootstrap commands shown there. They have this form:
zrok2 config set apiEndpoint https://zrok2.share.psilon.dev
zrok2 enable <enable-token>
zrok2 config set defaultNamespace publicValidate the setup:
auto-tunnel.sh doctorShare one port
Start your web service on the target host. For example, to smoke-test a local HTTP service on port 8888:
python3 -m http.server 8888In another shell on the same host, expose that port:
auto-tunnel.sh open 8888The command prints the public URL when the tunnel is ready.
To preview the tunnel without starting it:
auto-tunnel.sh open --dry-run 8888Jupyter Notebooks
Jupyter’s token remains required, but the token alone is not enough for plain proxy sharing. Jupyter also checks the HTTP Host header, and a PSILON share reaches the notebook with a host such as my-python-demo.share.psilon.dev.
The auto-tunnel.sh helper handles this automatically for running Jupyter servers. It detects the notebook port from Jupyter runtime files or jupyter server list, starts the share through zrok2’s Caddy backend with a local Host header rewrite, and internally rewrites the bare share root to the tokenized Jupyter path. That means the plain tunnel link in the PSILON dashboard should work, while auto-tunnel.sh list also prints a direct URL that includes the Jupyter token.
Start Jupyter as you normally do on the target host. The helper only needs Jupyter to be running when you open or watch the port. If you choose the bind address and port explicitly, use a local bind address such as 127.0.0.1 and note the port number.
In another shell on the same host, expose the notebook port:
auto-tunnel.sh open 8888Open the printed share URL or the tunnel link in the PSILON dashboard. For Jupyter, the helper-managed tunnel should either add the token internally or print a URL that includes the token:
https://my-python-demo.share.psilon.dev/tree?token=<jupyter-token>The helper does not need Jupyter to be installed when you run the initial PSILON setup. It only needs to discover Jupyter when the notebook is already running and you open or watch the port.
If you run zrok2 share public manually in plain proxy mode, start Jupyter with --ServerApp.allow_remote_access=True, or for older classic Notebook 6 installs, --NotebookApp.allow_remote_access=True. This disables Jupyter’s DNS-rebinding Host header protection for that server process, so use it only for notebook servers you intend to expose through PSILON. Keep Jupyter’s token enabled, grant PSILON tunnel collaborators deliberately, and stop the notebook and tunnel when you are done.
Watch for ports
Preview what watch mode would do:
auto-tunnel.sh scanor:
auto-tunnel.sh watch --dry-runDry-run output includes the port, process ID, process name, sharing mode, public share name, and local target. It does not require an enabled zrok2 environment and does not start or stop any tunnels.
On macOS especially, you may see desktop or sync helper processes with local listening ports. Narrow discovery to the ports you actually want:
auto-tunnel.sh scan --include-ports 8888,8501
auto-tunnel.sh watch --include-ports 8888,8501You can also ignore noisy ports:
auto-tunnel.sh watch --exclude-ports 5000,7000,9000-9099Watch mode detects new listening ports owned by your user and starts tunnels automatically:
auto-tunnel.sh watchOn Linux, watch mode uses ss for port discovery. On macOS, it uses the system lsof command.
Use watch mode intentionally from an interactive session. Do not start it automatically from every shell startup file.
List and stop tunnels
List active tunnels:
auto-tunnel.sh listStop one tunnel:
auto-tunnel.sh stop 8888Stop all tracked tunnels:
auto-tunnel.sh stop allAccess model
Share URLs require PSILON/CERN sign-in. The owner can access the tunnel and can grant access to collaborators from the Tunnels dashboard. Users who are not the owner or a collaborator are denied.
Troubleshooting
- If
zrok2is not found or the wrong version is reported, rerun the install command and confirm~/.local/binis on yourPATH. - If
zrok2is not enabled, rerun the commands in Enable zrok2. - If sharing fails with
ziti.share.psilon.dev:1280: connect: connection refused, the public Ziti controller port is not reachable from that machine. - If sharing fails with
timeout waiting for message replyorchannel closedwhile binding throughrouter.share.psilon.dev:3022, report it to the operator. The server-side Ziti router may need to resync with the controller. - If sharing fails with
shareConflict, stop the existing share or choose a different public name. - If the authenticated share page shows a zrok bad gateway, the public share exists but no publisher is connected. Confirm the local HTTP service is still listening and keep
auto-tunnel.shorzrok2 share ... --headlessrunning; restart the tunnel if needed. - If a Jupyter share returns
403and the notebook console logsBlocking request with non-local 'Host', updateauto-tunnel.shand restart the tunnel. For manual plain-proxy shares, restart Jupyter with--ServerApp.allow_remote_access=True. - If no URL appears quickly, check the log file shown by
auto-tunnel.sh. - If watch mode does not pick up a service, run
auto-tunnel.sh scanand confirm the process is listening on TCP and is owned by your user. Linux needsss; macOS needslsof.
Manual zrok2 commands
The auto-tunnel.sh helper is the default path. You can also run zrok2 directly for one-off checks or when you want to control the public name yourself.
Start the service in one shell. This example serves the current directory on port 8888:
python3 -m http.server 8888In another shell on the same machine, create a public name and start the tunnel:
zrok2 create name my-python-demo
zrok2 share public http://127.0.0.1:8888 -n public:my-python-demo --headlessUse a unique lowercase name with letters, digits, and hyphens. The bootstrap command sets the default namespace to public, so zrok2 create name my-python-demo is equivalent to zrok2 create name my-python-demo -n public. The share command still names public:my-python-demo so zrok2 uses that exact public name.
The share command runs in the foreground and prints the public URL when it is ready. Keep it running while the service should stay reachable; press Ctrl-C to stop sharing.
If the command fails because the name is already in use by another share, list your shares and delete the stale share token:
zrok2 list shares
zrok2 delete share <share-token>If you no longer want the reserved public name, delete it after the share is stopped:
zrok2 delete name my-python-demo