Installation & Services
Install headless Throne as a systemd service, a systemd user unit, a Docker container, a macOS LaunchDaemon or a Windows service.
Headless Throne is the normal Throne program started with --headless. Install Throne as described in Installation, then let a service manager run it. This page shows how for each system:
| System | Service manager | Runs as | Data directory |
|---|---|---|---|
| Debian, Ubuntu, Raspberry Pi OS, Fedora, openSUSE | systemd system unit | root (or the user throne) |
/var/lib/throne |
| Any Linux, without root | systemd user unit | you | ~/.config/Throne |
| Docker | Docker or Docker Compose | root in the container | the volume /var/lib/throne |
| macOS | launchd LaunchDaemon | root | /Library/Application Support/Throne |
| Windows | Windows service | LocalSystem | C:\ProgramData\Throne |
After the installation, connect thronectl as described in Managing with thronectl.
Debian, Ubuntu, Raspberry Pi OS, Fedora, openSUSE
The .deb and .rpm packages from the releases page and the package repositories install Throne to /opt/Throne, the systemd unit throne.service and the environment file /etc/throne/throne.env. Pick the package by the system's C library (glibc):
| Package | Needs | Runs on, for example |
|---|---|---|
Throne-<version>-debian-amd64.deb, …-fedora-amd64.rpm |
glibc 2.34 | Debian 12, Ubuntu 22.04, Fedora, openSUSE Tumbleweed |
…-amd64-system-qt.deb, …-amd64-system-qt.rpm |
glibc 2.34 and the system's Qt 6 | The same, with Qt from the distribution |
Throne-<version>-debian-arm64-system-qt.deb |
glibc 2.34 and the system's Qt 6 | Raspberry Pi OS bookworm, Debian 12, Ubuntu 22.04 on arm64 |
Throne-<version>-debian-arm64.deb, …-fedora-arm64.rpm |
glibc 2.38 | Ubuntu 24.04, Debian 13, Raspberry Pi OS trixie |
Each package depends on the glibc version its files need, so the package manager refuses a package that is too new for the system instead of installing a Throne that cannot start. On a Raspberry Pi with Raspberry Pi OS bookworm (glibc 2.36), use the arm64 system-qt package.
The packages without system-qt bring their own Qt, but they still depend on the system's graphics, font and X11 libraries, because the one Throne program also contains the desktop app. The package manager installs those libraries. Nothing needs a display.
Install the package and start the service:
sudo apt install ./Throne-<version>-debian-amd64.deb # or: sudo dnf install ./Throne-<version>-fedora-amd64.rpm
sudo /opt/Throne/Throne --check # optional: can this computer run it?
sudo systemctl enable --now throne
- The package never enables or starts the unit by itself. An upgrade restarts the unit when it runs, and removing the package stops and disables it.
/var/lib/throne(profiles, settings and the API key) stays when you remove the package, even withapt purge. Delete it yourself to start over.systemctl start thronereturns once Throne has started. See systemd: readiness, status and watchdog./etc/throne/throne.envhas a commented example for each variable the unit can use. Uncomment a line and runsudo systemctl restart throne. Upgrades keep your edits. Never put an API key in this file;THRONE_API_KEY_FILEnames a key file instead.- Logs:
journalctl -u throne -f.
To change the command line, add a drop-in with sudo systemctl edit throne:
[Service]
ExecStart=
ExecStart=/opt/Throne/Throne --headless --data-dir=%S/throne --debug
The unit runs Throne as root. Privileges on Linux compares this with the other ways to run it.
Other Linux installs
The install script puts Throne in /opt/Throne, and the portable ZIP goes wherever you unpack it. Neither installs a unit. Install the unit yourself:
curl -fsSLo throne.service https://raw.githubusercontent.com/throneproj/Throne/dev/dist/linux/throne.service
sudo install -m 644 throne.service /etc/systemd/system/throne.service
sudo systemctl daemon-reload
sudo systemctl enable --now throne
The unit runs /opt/Throne/Throne. If Throne is in another folder, change that path with a drop-in, as shown for the AUR package below. /etc/throne/throne.env is optional for this unit. To have the examples, save dist/linux/throne.env there.
The AUR package is maintained separately and installs Throne to /usr/lib/throne. If it does not ship a unit, install the one above and point it at that path with a drop-in (sudo systemctl edit throne):
[Service]
ExecStart=
ExecStart=/usr/lib/throne/Throne --headless --data-dir=%S/throne
Privileges on Linux
TUN and the kill switch need privileges. Profiles with an Extra Core need a normal user to run that core as, because Throne never runs an Extra Core as root. Choose how the unit runs:
| How the unit runs | TUN | Kill switch | Extra Core profiles | Good for |
|---|---|---|---|---|
| As root (the unit as shipped) | Yes | Yes | No | Gateways and dedicated boxes |
As throne with capabilities (caps.conf) |
Yes | No (unsupported) |
Yes | A proxy box without the kill switch |
As throne with a setuid-root ThroneCore |
Yes | Yes | Yes | Everything, without running Throne itself as root |
As throne with nothing else |
No | No | Yes | Proxy only (the mixed inbound) |
Capabilities. Download caps.conf, then:
sudo useradd --system --no-create-home --home-dir /var/lib/throne --shell /usr/sbin/nologin throne
sudo install -Dm644 caps.conf /etc/systemd/system/throne.service.d/caps.conf
sudo systemctl daemon-reload && sudo systemctl restart throne
systemd hands /var/lib/throne over to throne at the next start. ThroneCore must have no setuid bit and no file capabilities in this setup, because running such a file drops the capabilities that the unit grants.
Setuid core. Use a drop-in with only User=throne and Group=throne under [Service], and make the core run as root:
sudo chown root:root /opt/Throne/ThroneCore && sudo chmod u+s /opt/Throne/ThroneCore
A package upgrade replaces ThroneCore without the setuid bit. On Debian and Ubuntu, this keeps the bit across upgrades:
sudo dpkg-statoverride --update --add root root 4755 /opt/Throne/ThroneCore
Do not add NoNewPrivileges=, or a hardening option that implies it, to the unit: it would stop the setuid core.
thronectl status shows what this Throne can do in capabilities.tun and capabilities.kill_switch. The core checks for itself whether it can open /dev/net/tun and has CAP_NET_ADMIN, so a root container without NET_ADMIN reports false instead of failing later.
systemd user unit
Without root, Throne runs as a user unit in your own session. Download throne-user.service, then:
install -Dm644 throne-user.service ~/.config/systemd/user/throne.service
systemctl --user daemon-reload && systemctl --user enable --now throne
loginctl enable-linger "$USER" # start at boot, without a login
- The unit uses the desktop app's data directory,
~/.config/Throne, so it has the same profiles and settings. Quit the desktop app first. While the unit runs, the desktop app says that Throne is running in the background and exits. Runsystemctl --user stop thronebefore you open the desktop app. - TUN and the kill switch need a setuid-root core. The desktop app sets it up the first time it asks for privileges (see Privileges), and
Throne --checkprints the commands. Throne --health,Throne --print-connectionandThrone --checkfind this Throne without options when you run them as your user.
Docker
The image ghcr.io/throneproj/throne:<version> (also latest) is built for linux/amd64 and linux/arm64. It contains Throne in /opt/Throne, thronectl in /usr/local/bin and tini as init. It keeps its data in the volume /var/lib/throne, exits with code 75 for a restart and uses Throne --health as its health check.
Start from the compose.yaml in the Throne repository. It uses the latest tag; put a release tag there to update on your own schedule.
curl -fsSLO https://raw.githubusercontent.com/throneproj/Throne/dev/docker/compose.yaml
docker compose up -d
docker compose exec throne thronectl --key-file /var/lib/throne/config/api.key status
The important parts of that file:
services:
throne:
image: ghcr.io/throneproj/throne:latest # or a release tag
restart: unless-stopped
stop_grace_period: 45s
cap_add: [NET_ADMIN] # Tun and the kill switch
devices: ["/dev/net/tun:/dev/net/tun"] # Tun and the kill switch
volumes: ["throne-data:/var/lib/throne"]
ports: ["2080:2080"] # the mixed (HTTP and SOCKS) inbound
volumes:
throne-data:
Notes:
- The proxy port. The mixed inbound listens on
127.0.0.1by default, which a published port cannot reach. Runthronectl settings set inbound_address=0.0.0.0and start the profile again. Docker's published ports bypass host firewalls such asufw: on a host that the internet can reach, publish only the LAN address, for example"192.168.1.2:2080:2080". - TUN and the kill switch need
cap_add: [NET_ADMIN]and the device/dev/net/tun. Without them the container still serves the mixed inbound. - Host network. With
network_mode: host(and noports:), TUN and the kill switch act on the whole host, and only one Throne on the host can run TUN. See Running beside a desktop Throne. - Stopping. Keep
stop_grace_period: 45s. Docker's default of 10 seconds can kill Throne before it has stopped its core. Withrestart: unless-stopped, Docker starts Throne again after every exit, even afterthronectl shutdown; stop it withdocker compose stop. - Health. Docker only marks an unhealthy container. It does not restart it.
- Limits. Extra Core profiles do not run, because the container runs as root. In rootless Docker and other user-namespaced containers the kill switch fails with
unsupported(see How Throne stays reachable). - Options. Put variables under
environment:, or add flags withcommand:, which Docker appends after--headless. Change the data directory only withTHRONE_DATA_DIR, because the health check finds Throne through it. - thronectl without
--key-file. AddTHRONE_KEY_FILE: /var/lib/throne/config/api.keyunderenvironment:, anddocker compose exec throne thronectl statusworks as is. Throne itself ignores that variable. - Importing a file. Pipe it in with
-T:docker compose exec -T throne thronectl import - < links.txt. - The API from outside the container. The API listens on the container's loopback. To reach it from the network, set
THRONE_API_LISTEN: "*:9095"with aTHRONE_API_ALLOWlist and publish port 9095. Admin requests, such as imports and settings, then also needTHRONE_API_ADMIN_LAN: "1", because every request through a published port comes from outside the container. Behind Docker's NAT all clients share one address, so one client with an old key locks out the others for a minute. - Logs:
docker compose logs -f throne.
macOS
A LaunchDaemon runs Throne as root at boot. TUN and the kill switch work; Extra Core profiles do not. Move Throne.app to /Applications first, download moe.Throne.headless.plist, then:
sudo mkdir -p /Library/Logs/Throne
sudo install -m 644 -o root -g wheel moe.Throne.headless.plist /Library/LaunchDaemons/
sudo xattr -dr com.apple.quarantine /Applications/Throne.app # unsigned builds only
sudo launchctl bootstrap system /Library/LaunchDaemons/moe.Throne.headless.plist
- To stop and unload it:
sudo launchctl bootout system/moe.Throne.headless. - Its data is in
/Library/Application Support/Throneand its output in/Library/Logs/Throne/throne.log. - The plist runs the program file directly, so launchd follows the process. Never start headless Throne with
open: that hands it to LaunchServices and returns at once. - launchd starts Throne again 10 seconds after an exit with an error. It cannot leave out exit codes, so it also retries every 10 seconds the exits that a new start does not fix: another Throne using the data directory (code 3), a bad option (code 64) and a configuration error (code 78). Read the log when Throne keeps starting and exiting.
- To see how it is doing:
sudo /Applications/Throne.app/Contents/MacOS/Throne --health.
Windows service
Throne.exe --headless --service runs as a native Windows service under the LocalSystem account, so TUN and the kill switch work.
-
Install Throne for all users with the installer, into
Program Files. A service must not run a program that a normal user can replace, so do not use a per-user installation or a ZIP in your user folder. -
Download
install-service.batanduninstall-service.bat. -
From an elevated Command Prompt, run:
install-service.batFor Throne in another folder, give the path:
install-service.bat "D:\Apps\Throne\Throne.exe".
The script creates the service Throne, starts it after boot (delayed start) and sets its recovery actions:
sc.exe failure Throne reset= 86400 actions= restart/3000/restart/10000/restart/60000
sc.exe failureflag Throne 1
failureflag 1makes Windows apply the recovery actions when Throne exits with an error code, not only after a crash. That is how restart requests (code 75) and the watchdog exits bring Throne back. Each one counts as a failure: within a day, the first restart comes after 3 seconds, the second after 10, and later ones after 60.sc.exe stop Throneandthronectl shutdownstop it for good (code 0). When Windows shuts down, it gives Throne up to 45 seconds.- After it stopped with an error,
sc.exe query ThroneshowsWIN32_EXIT_CODE : 1066, and Throne's own exit code asSERVICE_EXIT_CODE. - Its data is in
C:\ProgramData\Throneand its log inC:\ProgramData\Throne\config\logs\throne.log. uninstall-service.batstops and deletes the service. The data inC:\ProgramData\Thronestays.- Stop the service before you update Throne.
A scheduled task in Task Scheduler is no substitute for the service: Windows stops a task with a hard kill, so Throne cannot stop its core cleanly, and it cannot restart a task depending on its exit code.
The data folder
The service keeps C:\ProgramData\Throne for SYSTEM and Administrators only. It creates the folder that way and sets that access again at every start, also for the files in it. If the folder already exists but belongs to another user, lets other users write to it (a folder you create by hand under C:\ProgramData does), is a link or junction, or contains a file or folder that is a link or junction, the service exits with code 78 and names the problem in the log. Remove that folder and start the service again. A --data-dir that normal users can write to only gets a warning in the log.
Commands from a prompt
Run Throne.exe --health, --print-connection, --check and thronectl from an elevated prompt, and give the Throne.exe commands --data-dir C:\ProgramData\Throne. An administrator is not LocalSystem, so its default data directory is a different one. Only Administrators and SYSTEM can open the service's pipe and read its key file. From a prompt that is not elevated, --health logs [Instance] Not talking to … access denied and exits with code 2.
Throne.exe is a Windows GUI program, so the Command Prompt does not wait for it. Throne writes its output to the console of the program that started it. To wait for it and read its exit code:
start "" /wait "C:\Program Files\Throne\Throne.exe" --health --data-dir C:\ProgramData\Throne
echo %ERRORLEVEL%
$p = Start-Process 'C:\Program Files\Throne\Throne.exe' -ArgumentList '--health', '--data-dir', 'C:\ProgramData\Throne' -Wait -PassThru
$p.ExitCode
--print-connection prints POSIX export lines. In PowerShell, set the values yourself:
$env:THRONE_ADDR = '127.0.0.1:9095'
$env:THRONE_KEY_FILE = 'C:\ProgramData\Throne\config\api.key'
thronectl status
Running from a console
To test headless Throne without the service:
start "" /wait "C:\Program Files\Throne\Throne.exe" --headless
Ctrl+C stops it cleanly, and a second Ctrl+C kills the core at once. Closing the console window, taskkill /PID <pid> without /F, signing out and shutting down also stop it cleanly. Outside the service, the data directory is %LOCALAPPDATA%\Throne (C:\ProgramData\Throne when it runs as LocalSystem). A restart starts a new Throne process and exits with code 0, so start /wait returns while the new process keeps running.