TSLinkTSLink Docs

Daemon Mode

Running TSLink as a background service with cross-platform autostart

View as Markdown

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:

bash
tslink add app --proxy localhost:3000 --no-daemon-install
tslink serve --daemon

The 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:

bash
tslink status

Example output:

Code
→ tslink: running (pid 12345)
→ tailnet: authenticated
→ services: 3 registered

Stopping the Daemon

Stop the background gateway:

bash
tslink stop

On 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:

SignalPlatformBehavior
SIGTERMUnix (macOS, Linux)Initiate shutdown of service listeners and nodes, then exit
SIGINTUnix (macOS, Linux)Same as SIGTERM (allows Ctrl+C in foreground mode)
Process killWindowsImmediate process termination via OS process kill

During Unix graceful shutdown, TSLink closes its control plane and shuts down each service node:

  1. Cancel the node's work and stop accepting HTTP requests.
  2. 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.
  3. Close the node's listener, tsnet node, and handler resources. Raw TCP does not use the HTTP drain step.
  4. 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:

bash
tslink install

macOS — 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 after tslink stop while the LaunchAgent remains installed
  • ThrottleInterval=30 — throttles restart loops to 30 seconds
  • Manages stdout/stderr logging
  • 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:

bash
launchctl list | grep tslink

Removing Autostart

bash
tslink uninstall

This 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:

  1. Run tslink uninstall --json to remove managed autostart first, avoiding macOS KeepAlive restarting the gateway. Halt on an error or an unverified-removal warning.
  2. Run tslink stop --json to stop any remaining process. Halt on an error; a Unix timeout can leave the process running.
  3. Run tslink status --json. Continue maintenance only when ok is true, data.daemon_running is false, and data.supervision.installed, autostart, and restart_on_exit are 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:

  1. When tslink serve --daemon starts, it forks the process and writes its PID to ~/.config/tslink/tslink.pid.
  2. tslink status reads the PID file to check if the process is still running.
  3. tslink stop reads 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

FileContent
~/.config/tslink/logs/tslink.out.logDaemon stdout
~/.config/tslink/logs/tslink.err.logGateway lifecycle, warnings and errors on stderr
~/.config/tslink/access-log/Bounded asynchronous HTTP/file/TCP/guest JSONL events
~/.config/tslink/mcp-audit.jsonIndependently 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

bash
tslink logs --source err --last 100
tail -f ~/.config/tslink/logs/tslink.err.log
tslink access log --app photos --since 24h --json

Admin API and Dashboard

The admin dashboard and REST API are roadmap/experimental. Use the CLI with --json for shipped automation.

For the default background workflow:

bash
# 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 --json

Inspect 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.

Table of Contents