Skip to main content

Running a tunnel as a background service

A background service keeps a tunnel running all the time: it survives SSH logouts, reboots and crashes. The tuna service command (short form tuna svc) creates the service with the system's own tools, starts it right away and restarts the tunnel 30 seconds after a crash.

You can install http, tcp, ssh, app, postgres, mysql, redis, kubernetes and 1c tunnels and the webhook and smtp triggers as services. tuna start can't run as a service: it restarts tunnels on its own and mixes their logs. For several tunnels, create one service per tunnel — install prints the commands you need.

User and system services​

Where the service ends up depends on who runs install:

SystemWithout privilegesAs root or administrator
Linux with systemdsystemd --user, unit in ~/.config/systemd/user/system unit in /etc/systemd/system/
Linux with OpenRC (Alpine, Gentoo)—script in /etc/init.d/
macOSLaunchAgent in ~/Library/LaunchAgents/LaunchDaemon in /Library/LaunchDaemons/
FreeBSD—script in /usr/local/etc/rc.d/
Windows—Windows service

For a user service on Linux, the CLI enables linger (loginctl enable-linger): without it, systemd stops the service after you log out of SSH and doesn't start it at boot. If enabling linger fails, the CLI warns you and suggests sudo loginctl enable-linger <user>.

tip

By default, macOS has power management policies that put the computer to sleep, which makes the service unavailable. This behavior can be configured if you want to use a macOS computer in server mode. You can view the current power plan with the pmset -g command. For continuous background operation, set the following values:

sudo pmset -a sleep 0
sudo pmset -a autorestart 1
sudo pmset -a networkoversleep 1
sudo pmset -a tcpkeepalive 1
sudo pmset -a powernap 0
sudo pmset -a womp 1

Creating a service​

Log in with tuna login and pass the tunnel command after --:

tuna service install -- tuna http 8080 --subdomain=web

The service starts right away. The default name is tuna. To run several services, give each one its own name with the --name flag or the TUNA_SERVICE_NAME variable:

tuna service install --name=db -- tuna tcp 5432

By default, the Request Inspector is off in a service: nobody watches it in the background. If you need it, pass --inspect in the tunnel command explicitly. postgres, mysql, and redis tunnels always run the inspector, and install warns about it. If you don't need request debugging, install tuna tcp as the service instead.

Service key​

For each service, install issues a separate key from your session. If the command has --subdomain, --domain or --port, the key is bound to that address; otherwise it can only start tunnels of its own type, HTTP or TCP. The key is stored in a file that only the service user can read, and it never appears in the process arguments. tuna service uninstall revokes it on the server.

If you aren't logged in on the machine — it's a server you reached over SSH, or install runs under sudo and can't see your session — the CLI shows a link and a code: open the link on any device where you're logged in to tuna and confirm the key. Without a terminal, in a script, the CLI takes the key from TUNA_API_KEY, the --api-key flag or the configuration file. That key needs the tunnel scope (tunnels:connect).

System service as another user​

A system service runs as root by default (LocalSystem on Windows). It's safer to run it as an unprivileged user — set one with the --user flag or the TUNA_SERVICE_INSTALL_USER variable:

sudo tuna service install --user=www-data -- tuna http 8080 --subdomain=web

On Windows, a built-in account works for a service without a password:

tuna service install --user 'NT AUTHORITY\LocalService' -- tuna http 8080 --subdomain=web

Managing services​

In a terminal, tuna service without a subcommand opens the terminal UI with the list of services. From there you can create, edit, start, stop, restart and remove a service and view its logs.

KeyAction
Enterlogs of the selected service
iservice card: command, user, log file
nnew service
eedit the command
s / x / Rstart / stop / restart
dremove the service and revoke its key

For scripts, the same actions are available as subcommands. All of them accept --name, tuna by default:

tuna service list # all services, --json for JSON
tuna service status --name=web # exit code 1 if the service isn't running
tuna service start --name=web
tuna service stop --name=web
tuna service restart --name=web
tuna service uninstall --name=web # stop, remove and revoke the key

Changing the command​

tuna service edit changes the tunnel arguments and restarts the service. The name, user, environment variables and autostart stay the same:

tuna service edit --name=web -- tuna http 8081 --subdomain=web

You can't change the tunnel type — reinstall the service for that. If the address changes (--subdomain, --domain, --port), the CLI issues a new key and revokes the old one.

Logs​

tuna service logs --name=web # last 100 lines
tuna service logs --name=web -n 500
tuna service logs --name=web -f # follow new lines

tuna service logs finds the log on each system by itself:

SystemWhere the logs are
Linux with systemdjournald, journalctl [--user] -u <name>
Linux with OpenRC, FreeBSD/var/log/tuna/<name>.log
macOS~/Library/Logs/tuna/<name>.log, for a system service /Library/Logs/tuna/<name>.log
Windows%ProgramData%\tuna\logs\<name>.log

Running in Docker Compose​

Instead of a service, you can run the tunnel in a container. Create compose.yml:

---
name: tuna
services:
web:
image: yuccastream/tuna:latest
command: http 3000 --subdomain=web
restart: always
network_mode: "host"
environment:
- TUNA_API_KEY=<your_key>

The inspector is off by default in the official image too. To turn it on, see Inspector in Docker.

Important

network_mode: "host" (the same as --net=host) works only on Linux by default. On Windows and macOS it's a beta feature of Docker Desktop: in Settings, on the Features in development tab, select Enable host networking.

docker compose up -d # start
docker compose logs -f # logs
docker compose down -v # stop