Skip to content

feat: add system service management support (install/start/stop/restart/logs/status) #659

@chirino

Description

@chirino

Feature Request

Add built-in support for running T3 Code as a persistent background service managed by the host OS's service manager (e.g. launchd on macOS, systemd on Linux, Windows Service on Windows).

Currently, users who want T3 Code to run persistently — for example, to keep it accessible remotely over Tailscale or a local network — must wire this up manually. A service subcommand would significantly lower the barrier for self-hosted use.

Proposed Subcommands

t3 install    # Register the service with the OS service manager
t3 uninstall  # Deregister and remove the service
t3 start      # Start the service
t3 stop       # Stop the service
t3 restart    # stop + start
t3 logs       # Stream the service log output
t3 status     # Check whether the service is running
t3            # open browser to t3

Expected Behavior

The service should run the T3 server with flags appropriate for headless/remote use (--host, --port, --no-browser).
Auth tokens and other relevant environment variables should be forwarded so the service inherits the correct runtime context.
The service should auto-start at login/boot and restart automatically on crash.
Log output should be captured and accessible via t3 service logs.

Here is a version of the above that I use for os x:

#!/bin/bash
set -e

T3_SCRIPT="$(realpath "$0")"

# change this:
T3_HOME=/Users/chirino/sandbox/t3code

# If the script is placed in the project root, then this would work:
# T3_HOME=$(dirname $T3_SCRIPT)

SERVICE_LABEL="gg.ping.t3"
PLIST_PATH="$HOME/Library/LaunchAgents/${SERVICE_LABEL}.plist"

cmd_run() {
  cd $T3_HOME
  AUTH_TOKEN_ARG=()
  if [ -n "$T3_AUTH_TOKEN" ]; then
    AUTH_TOKEN_ARG=(--auth-token "$T3_AUTH_TOKEN")
  fi
  exec bun run --cwd apps/server start -- --host "$(tailscale ip -4)" --port 3773 --no-browser "${AUTH_TOKEN_ARG[@]}"
}

cmd_install() {
  mkdir -p "$HOME/Library/LaunchAgents"
  mkdir -p "$HOME/Library/Logs"
  cat > "$PLIST_PATH" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>${SERVICE_LABEL}</string>
    <key>ProgramArguments</key>
    <array>
        <string>${T3_SCRIPT}</string>
        <string>run</string>
    </array>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>${PATH}</string>
        <key>HOME</key>
        <string>${HOME}</string>
        <key>T3_AUTH_TOKEN</key>
        <string>${T3_AUTH_TOKEN}</string>
        <key>T3_HOME</key>
        <string>${T3_HOME}</string>
    </dict>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>${HOME}/Library/Logs/t3.log</string>
    <key>StandardErrorPath</key>
    <string>${HOME}/Library/Logs/t3.log</string>
</dict>
</plist>
EOF
  launchctl load "$PLIST_PATH"
  echo "t3 installed and started as a login background service."
  echo "Logs: ~/Library/Logs/t3.log"
}

cmd_uninstall() {
  if launchctl list "$SERVICE_LABEL" &>/dev/null; then
    launchctl unload "$PLIST_PATH"
  fi
  rm -f "$PLIST_PATH"
  echo "t3 background service removed."
}

cmd_start() {
  launchctl load "$PLIST_PATH"
  echo "t3 background service started."
}

cmd_stop() {
  launchctl unload "$PLIST_PATH"
  echo "t3 background service stopped."
}

cmd_restart() {
  cmd_stop
  cmd_start
}

cmd_logs() {
  tail -f "$HOME/Library/Logs/t3.log"
}

cmd_status() {
  if launchctl list "$SERVICE_LABEL" &>/dev/null; then
    echo "t3 is running."
  else
    echo "t3 is not running."
  fi
}

case "${1}" in
  run)       cmd_run ;;
  install)   cmd_install ;;
  uninstall) cmd_uninstall ;;
  start)     cmd_start ;;
  stop)      cmd_stop ;;
  restart)   cmd_restart ;;
  logs)      cmd_logs ;;
  status)    cmd_status ;;
  "")    open "http://$(tailscale ip -4):3773" ;;
  *)
    echo "Usage: t3 {run|install|uninstall|start|stop|restart|logs|status}"
    exit 1
    ;;
esac

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-juliusneeds-triageIssue needs maintainer review and initial categorization.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions