Daemon Mode
Running TSLink as a background service with cross-platform autostart
Overview
TSLink can run as a background daemon process, so you don't need to keep a terminal window open. It supports automatic startup on macOS, Linux, and Windows.
Running as a Daemon
Default tslink add and tslink share already ensure the background gateway is running. Complete any needs_login / auth_url enrollment handoff, then inspect it with tslink status --json and wait for the service with tslink url <name> --wait. Running serve again while that gateway is running returns a conflict.
For a deliberate manual setup, first confirm there is no managed installation or running gateway. If converting an existing setup, follow verified shutdown below. Register services without installing a daemon, then start the gateway yourself:
tslink add app --proxy localhost:3000 --no-daemon-install
tslink serve --daemonThe manual gateway runs in the background and redirects stdout/stderr to log files. It continues running after you close the terminal, but this workflow does not register login autostart or crash supervision. Complete enrollment and wait for readiness before using its services.
Checking Status
See whether the daemon is running, authentication state, and service count:
tslink statusExample output:
→ tslink: running (pid 12345)
→ tailnet: authenticated
→ services: 3 registeredStopping the Daemon
Stop the background gateway:
tslink stopOn macOS/Linux, this sends SIGTERM and waits up to five seconds for the process to exit. A timeout returns an error without forcing termination; the process may still be running. On Windows, it requests immediate OS process termination and then waits for confirmation of exit; it does not drain requests gracefully. Check the result and tslink status --json before maintenance.
stop does not remove autostart. An installed macOS LaunchAgent can restart the process after a stop; a Linux user unit with Restart=on-failure remains stopped after a successful graceful exit. Use verified shutdown when you need the gateway to stay stopped.
Signal Handling
TSLink listens for OS signals to coordinate clean shutdowns:
| Signal | Platform | Behavior |
|---|---|---|
SIGTERM | Unix (macOS, Linux) | Initiate shutdown of service listeners and nodes, then exit |
SIGINT | Unix (macOS, Linux) | Same as SIGTERM (allows Ctrl+C in foreground mode) |
| Process kill | Windows | Immediate process termination via OS process kill |
During Unix graceful shutdown, TSLink closes its control plane and shuts down each service node:
- Cancel the node's work and stop accepting HTTP requests.
- Give each proxy/file HTTP server up to five seconds to drain in-flight requests; if that server's shutdown fails or times out, close it.
- Close the node's listener, tsnet node, and handler resources. Raw TCP does not use the HTTP drain step.
- Keep service state for the next run, remove runtime/PID records, and exit.
The HTTP budget is per service node, separate from the CLI's five-second process-exit wait. Shutting down several nodes can take longer; a CLI timeout is not proof that all connections or the process have stopped.
Autostart on Login
TSLink can register itself to start automatically when you log in:
tslink installmacOS — LaunchAgent
Creates a plist at ~/Library/LaunchAgents/com.tslink.daemon.plist.
- Starts TSLink on login
KeepAlive=true— restarts automatically if the process crashes, and also aftertslink stopwhile the LaunchAgent remains installedThrottleInterval=30— throttles restart loops to 30 seconds- Manages
stdout/stderrlogging - Loaded and managed via
launchctl
Run tslink uninstall before tslink stop when the intent is to disable autostart rather than trigger a launchd restart.
To check status after install:
launchctl list | grep tslinkRemoving Autostart
tslink uninstallThis removes the platform-specific autostart configuration. macOS/Linux also attempt to stop their managed job; a refusal, error, or warning must be resolved before treating it as removed. Windows disables the owned scheduled task, stops its supervisor and child, verifies no running instance, then deletes the task. A Startup-only fallback removes the script without owning a current process. A manually started process also needs a separate stop. Use the sequence below when the goal is to stop serving.
Verified Shutdown
Run each step separately and inspect its result before continuing:
- Run
tslink uninstall --jsonto remove managed autostart first, avoiding macOSKeepAliverestarting the gateway. Halt on an error or an unverified-removal warning. - Run
tslink stop --jsonto stop any remaining process. Halt on an error; a Unix timeout can leave the process running. - Run
tslink status --json. Continue maintenance only whenokis true,data.daemon_runningis false, anddata.supervision.installed,autostart, andrestart_on_exitare all false. Resolve an inconclusive result before modifying configuration or starting another gateway.
Removing autostart and stopping a process do not delete registered services or credentials.
Restarting After Configuration Changes
For a managed setup, complete verified shutdown, change the global configuration or credentials, then run tslink install again with any previously selected install options. On macOS/Linux, install starts the managed job. On Windows, install registers the next sign-in only; to resume in the current session after confirmed shutdown, run tslink serve --daemon with the matching runtime options, or wait for the next sign-in. Check tslink status --json, complete any enrollment handoff, and use tslink url <name> --wait for readiness.
For a manual setup with no autostart registration, stop and verify data.daemon_running: false before changing settings, then restart with tslink serve (foreground) or tslink serve --daemon (background). Keep new registrations on --no-daemon-install if you intend to remain manual.
Process Lifecycle
TSLink uses PID-based lifecycle management:
- When
tslink serve --daemonstarts, it forks the process and writes its PID to~/.config/tslink/tslink.pid. tslink statusreads the PID file to check if the process is still running.tslink stopreads the PID and sends a termination signal.
This ensures only one instance of the gateway runs at a time. If you try to start a second instance, TSLink will detect the existing PID and warn you that a gateway is already running.
Structured Logging
tslink logs reads structured gateway diagnostics. For retained requests and access changes, use access history.
Log File Locations
| File | Content |
|---|---|
~/.config/tslink/logs/tslink.out.log | Daemon stdout |
~/.config/tslink/logs/tslink.err.log | Gateway lifecycle, warnings and errors on stderr |
~/.config/tslink/access-log/ | Bounded asynchronous HTTP/file/TCP/guest JSONL events |
~/.config/tslink/mcp-audit.json | Independently bounded mutation intent/completion and lifecycle journal |
Access Log Format
Schema-version-1 history events use typed time, kind, app/service, identity and decision fields; HTTP records add method/status/bytes/duration and sanitized path. Public Funnel does not identify a person. Query merges retained daemon segments and the journal. Missing completion means unknown outcome; drops and crash gaps prevent a complete-audit guarantee. Inspect current access-log health in status/doctor.
Following Logs
tslink logs --source err --last 100
tail -f ~/.config/tslink/logs/tslink.err.log
tslink access log --app photos --since 24h --jsonAdmin API and Dashboard
The admin dashboard and REST API are roadmap/experimental. Use the CLI with --json for shipped automation.
Recommended Setup
For the default background workflow:
# Register each service; ordinary add ensures the gateway is running
tslink add app --proxy localhost:3000 --json
tslink add files --dir ~/shared --json
tslink add mydb --tcp localhost:5432 --jsonInspect each add result. If it reports needs_login, open its auth_url and complete the tailnet's enrollment/approval requirements before continuing. Then run tslink url <name> --wait for each service and inspect tslink status --json; daemon liveness alone does not establish service readiness. Do not run another serve or install as a follow-up to successful default add.
Autostart availability follows the platform's login/lingering rules above; inspect data.supervision.autostart_scope in status --json for the observed scope. Manage services with tslink add and tslink remove — registry changes apply via hot reload without restarting the gateway.