Run Timon on a headless server
A server that never sleeps (a VPS, a home server, a NAS) is the best home for agents that work while you are away: scheduled Kanban tasks, overnight series and long missions keep running whatever your own computer does. This page puts the pieces together: install Timon as a background service, reach it safely, keep it up to date, and drive it from the Timon you already use on your computer.
- Install Timon on the server and run it as a service:
timon service install, thentimon service status. - Create your account through an SSH tunnel, before opening any access (step 0 of Remote access).
- Publish it on your private Tailscale network with
tailscale serve, over HTTPS. - On your computer, turn on the Machines plugin and pair the server with a one-time code (
timon device pairon the server prints one): the interface you already use switches to it in the same window. See Machines. - To update, install the new package, then on the server:
timon update apply(moves the service onto it),timon update status, andtimon update approve <daemon-version>.
1. Install and run as a service
On a Debian or Ubuntu server, install the supplied deb package: it also installs the timon command. From an authorised source checkout, follow Linux. In both cases, the daemon runs as a service of the operating system's supervisor, not inside a window:
timon service install --dry-run # shows what will be installed
timon service install
timon service status
timon doctor
On Linux the service is a systemd user service, with user lingering requested at installation: it starts at boot, before anyone signs in, and survives the end of your SSH session. Install it as the account that will own Timon's data, never as root. See Running in the background for the details, and for timon service stop / timon service start.
A server must not sleep: nothing runs while it does, and Timon does not wake it. On a machine without a desktop, sudo systemctl mask sleep.target suspend.target disables suspend.
The AI providers' command-line tools (claude, codex…) and their sign-in live on the server too: sign them in there, from an SSH session under the same account, once. A machine you drive from elsewhere keeps the providers it was configured with; nothing about them travels between machines.
2. Create your account first
The web interface and the daemon only listen on the server itself (the loopback) until you decide otherwise. Create your administrator account through an SSH tunnel, which nobody else can use, before opening any other access:
ssh -N -L 3100:127.0.0.1:3100 you@your-server
Then open http://127.0.0.1:3100 on your computer and complete the first run. Replace 3100 with the web interface port of your installation: timon app status prints it. The full walkthrough, including the one-time setup token, is in Remote access.
3. Reach it over HTTPS with tailscale serve
Tailscale links your own devices in a private network and gives the server an HTTPS address that only they can open. No port is opened to the internet. On the server:
sudo tailscale up
sudo tailscale serve --bg --https=443 http://127.0.0.1:3100
tailscale serve status
MagicDNS and HTTPS certificates must be on in the Tailscale admin console. Then add "daemon": { "behindTlsProxy": true } to ~/.timon/timon.config.json and restart the daemon, so session cookies are only sent over HTTPS. Open https://<server>.<your-tailnet>.ts.net from any of your devices: it is the full Timon interface, installable as an app on your phone.
tailscale serve shares with your tailnet only. Tailscale Funnel publishes on the public internet: never use it for Timon.
A reverse proxy with a certificate (Caddy, nginx) or a Cloudflare Tunnel works too: Remote access describes each method, with its firewall rules and its pitfalls.
4. Drive it from the Timon on your computer
With the Machines plugin, the Timon you already use on your computer (desktop app, browser or phone app) switches to the server in the same window, and back. Nothing is merged: each machine keeps its own agents, projects, settings and missions.
The interface on your computer talks to the server's daemon, so the daemon also needs an HTTPS address on your tailnet. The simplest way: on the server, in Settings → Access & network → Devices → Authorize a new device, choose Make this machine reachable (Connected devices); the card then shows the address. On a server with no desktop, Timon cannot show a password prompt to install Tailscale or let your account manage it: the card gives the commands to run over SSH (curl -fsSL https://tailscale.com/install.sh | sudo sh, then sudo tailscale set --operator=$USER), and Sign in to Tailscale shows the sign-in link to open on any computer. By hand, publish it on a second HTTPS port (replace 3000 with the daemon port of your installation, shown in the Daemon listener row of Settings → Access & network → Web interface → Technical details):
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
tailscale serve status
The server's address for Machines is then https://<server>.<your-tailnet>.ts.net:8443.
- HTTPS is required. A machine is never paired or reached over plain HTTP, even on a private network: put
tailscale serve, or a reverse proxy with a valid certificate, in front of the daemon. Plain HTTP is only accepted for a Timon on the same computer. - Keep the daemon behind the account. Once the first account exists, the daemon refuses every request without a signed-in account, a paired device or the daemon token, except the few that check their own proof: signing in, pairing a device and signed webhooks. That is why step 2 comes first. Keep
daemon.listenon127.0.0.1:tailscale servereaches it there. - Sign your webhooks. Behind
tailscale serve, Timon refuses any call to a trigger that uses thenonescheme, since it comes through a proxy. Give every trigger the server receives an HMAC signature or a shared token anyway (see Triggers).
Then pair the two:
- On the server's interface (its Tailscale address or the SSH tunnel), open Settings → Access & network → Devices and choose Authorize a new device. A one-time code appears, valid 5 minutes. Over SSH,
timon device pairprints the same code, with the daemon address to use. - On your computer, turn on Machines in Plugins, then open Settings → Plugins → Machines → Add a machine: enter the server's address and the code.
- Pick the server at the foot of the navigation menu. The whole interface becomes the server's; This computer in the same menu brings you back.
Everything about pairing, switching and what a paired device may do is in Machines and Connected devices.
5. Keep it up to date
Plan an update when no mission is running. Updating takes three steps, all on the server:
- Install the new package (for a deb,
sudo apt install ./<package>.deb). This replaces the installed program, not the service: the service runs from its own copy of the release inside the data directory, so the previous version keeps running. - Move the service onto the new package. On a desktop, quitting and reopening the app does it; on a server, run
timon update apply. It restarts the service on the new release, checks that it answers, and puts the previous one back if it does not. - Approve the data migration. A packaged daemon waits for your explicit approval before it updates an existing database; restarting the service does not give it.
As the account that runs Timon (not with sudo; set TIMON_HOME if you use a separate data directory):
timon update apply
timon update status
timon update approve <daemon-version>
timon update apply ends with The service now runs Timon <version>. Check that the daemonVersion returned by timon update status is the version of the package you installed, then approve that exact version. Timon saves a copy of your data before migrating, in backups/pre-<version>/ inside its data directory. All three commands work over SSH. If timon update apply fails, the previous service is restored and your data are not modified; the details are in desktop-runtime/update-apply.log inside the data directory.
As long as the service lags behind the installed package, timon update status and timon doctor say so and name the command to run. Then check:
timon service status
timon doctor
Update the interfaces that drive the server as well. When a machine runs a Timon too old for the interface you switch from, the switch is refused and the interface says to update that machine; a machine newer than the interface works, with a banner suggesting to update the interface. See Upgrading for backups and restores.
What does not run on a headless server
- The built-in browser of a project is run by the Timon desktop app. A server where only the background service runs has none: open the desktop app on that machine (in a desktop session) if its projects need it.
- Push notifications from the server reach your phone through the Timon that serves your interface. If that Timon (your computer) is off, the server's cards wait in its own notification centre. See Machines.
- Tools that need an open desktop session (a sign-in through the system credential store, a window): see Limits while no session is open.
Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
timon service status reports the service stopped after you log out of SSH | User lingering is off | sudo loginctl enable-linger $USER, then timon service start |
| The machine must be reached over HTTPS when adding the server | The address starts with http:// | Publish the daemon with tailscale serve (section 4) and use its https:// address |
| No Timon machine answers at this address | Something other than a Timon daemon answers at this address | Use the address published for the daemon port (:8443 above), with no path, and check tailscale serve status |
| The machine does not see this connection as HTTPS | A reverse proxy in front of the daemon drops X-Forwarded-Proto, or daemon.behindTlsProxy is off behind a proxy on another host | Forward X-Forwarded-Proto: https and set daemon.behindTlsProxy to true, then restart the daemon |
| The machine refused this code | Code mistyped, expired (5 minutes) or already used; or the address is the server's web interface, not its daemon | Check that the address is the daemon's (:8443 above), then authorize a new device on the server and use the new code at once |
| This machine runs a Timon version too old for this interface | The server was not updated | Update the server (section 5), then switch again |
timon update status still shows the previous version after you installed the new package | The package replaces the installed program, not the service, which still runs the previous release | timon update apply, then timon update status and timon update approve <daemon-version> (section 5) |
| The server's interface asks to approve an update and nothing else works | A new package was installed and the migration is not approved | timon update status, then timon update approve <daemon-version> on the server |
For anything else, the daemon log has the answer: journalctl --user -u <unit> -n 100; systemctl --user list-unit-files 'timon-*' lists the unit names. See also Troubleshooting.