Remote access via Tailscale
Skulk's dashboard and API are served over plain HTTP on port 52415. On your local network that works fine, but when you want to check on your cluster from your phone, restart a node from a coffee shop, or run inference from another machine, you need a way to reach those nodes without opening ports to the internet.
Tailscale solves this cleanly. It creates a private overlay network where every device gets a stable 100.x.x.x address. Once your cluster node and your phone (or laptop) are both on the same tailnet, you can open http://100.x.x.x:52415 exactly like you would at home: encrypted, no port forwarding, no VPN configuration.
What you get
- Dashboard from anywhere: full cluster view, observability, traces, model placement
- Operator panel: mobile-friendly cluster control from your phone: node health, memory, GPU, temperature, and tap-twice node restart
- API access: run inference or call management endpoints from any device on your tailnet
- Works with Headscale: self-host the control plane if you prefer
Your phone or laptop just needs the Tailscale app; it doesn't run a Skulk node. Only the machines that actually run Skulk need tailscaled installed and configured. One node on Tailscale is enough to reach the whole cluster dashboard, because the dashboard already shows every node's state regardless of whether those nodes are on Tailscale.
Setup
1. Install Tailscale on your cluster node
On the machine running Skulk:
# macOS: install from tailscale.com/download or:
brew install tailscale
# Linux:
curl -fsSL https://tailscale.com/install.sh | sh
Then connect it to your tailnet:
tailscale up
Note the IP address:
tailscale ip -4
# e.g. 100.101.102.103
2. Install Tailscale on your remote device
On your phone, tablet, or laptop, install the Tailscale app and log in to the same Tailscale account. No configuration beyond logging in.
- iOS / Android: search "Tailscale" in the App Store / Play Store
- macOS / Windows / Linux: tailscale.com/download
3. Open the dashboard
In any browser on your remote device:
http://100.101.102.103:52415
Replace 100.101.102.103 with your node's Tailscale IP from step 1. You get the full Skulk dashboard: chat, cluster view, observability, everything.
Save the http://100.x.x.x:52415 URL on your phone. iOS and Android both let you add it to your home screen as a web app shortcut.
Operator panel
The dashboard includes a mobile-first operator view designed for exactly this scenario: checking on your cluster and restarting nodes from a small screen.
Navigate directly to:
http://100.101.102.103:52415/operator
Bookmark that URL on your phone: iOS and Android both let you add it to your home screen as a web app shortcut so it opens instantly.
The operator panel shows:
- Cluster summary: total nodes, aggregate memory usage, average GPU utilization, average temperature
- Per-node cards: role (master/worker), memory bar, GPU usage, temperature, active placements
- Tap-twice restart: tap "Restart" on any node card; a "Confirm?" prompt appears; tap again within 3 seconds to send the restart command. Accidental taps do nothing.
Restarts are sent over the cluster's pub/sub channel, so you can restart any node (including remote ones) from any node's dashboard.
API access over Tailscale
The full Skulk API is available at the same address:
# From any device on your tailnet:
curl http://100.101.102.103:52415/v1/models
# Run inference:
curl http://100.101.102.103:52415/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "...", "messages": [{"role": "user", "content": "Hello"}]}'
# Check Tailscale connectivity status:
curl http://100.101.102.103:52415/v1/connectivity/tailscale
Verify it's working
Check the node's Tailscale status in the startup logs:
INFO Tailscale: running | IP 100.101.102.103 | my-node.tailnet-abc.ts.net
Check in the dashboard: Observability → Node tab → Runtime section shows the Tailscale row with the node's IP and DNS name.
Check via the API:
curl http://100.101.102.103:52415/v1/connectivity/tailscale | python3 -m json.tool
{
"running": true,
"selfIp": "100.101.102.103",
"hostname": "my-node",
"dnsName": "my-node.tailnet-abc.ts.net",
"tailnet": "tailnet-abc.ts.net",
"version": "1.66.1"
}
Troubleshooting
Can't reach 100.x.x.x:52415
First confirm Tailscale can reach the node at all:
ping 100.101.102.103
If ping fails, the devices aren't on the same tailnet: check that both are logged into the same Tailscale account (or Headscale server) and that Tailscale is running on both.
If ping succeeds but port 52415 doesn't respond, Skulk may not be running. SSH in (also works over Tailscale) and check:
# macOS:
launchctl print gui/$(id -u)/foundation.foxlight.skulk | grep "state ="
# Linux:
systemctl --user status skulk
Tailscale: not running in the Skulk logs
tailscaled is installed but not running on the cluster node:
# macOS: open the Tailscale app, or:
sudo tailscaled &
tailscale up
# Linux:
sudo systemctl start tailscaled
tailscale up
Tailscale ACLs blocking the port
By default all devices on the same tailnet can reach each other. If you've customised your ACL policy, make sure TCP 52415 (API/dashboard) is allowed between your devices.
Using Headscale
Headscale is a self-hosted Tailscale control server. Skulk treats it identically to Tailscale's own servers, with no config changes needed. Join each device to your Headscale instance:
tailscale up --login-server https://your-headscale-server.example.com
Then follow the same setup steps above.