Service and logs
The daemon runs as a background service on each machine. This page covers how it’s installed on each platform, where it keeps its state and logs, who can connect to it, and its flags.
arugulad install copies the binary to ~/.local/bin and installs the
service. Run it again to upgrade; programs in your panes keep running
during the upgrade. Flags after -- go to the daemon on every start, and
later installs and updates keep them (--reset-args drops them):
arugulad install -- --owner you@example.comPer platform
Section titled “Per platform”Each platform runs the daemon with its own service manager.
The desktop app’s daemon. If you installed only the desktop app, it runs
the daemon from inside itself, as a Login Item: see
The Mac app’s daemon. Its log is
~/Library/Logs/arugulad.log.
A launchd agent. arugulad install (which the install script and
Homebrew use) writes ~/Library/LaunchAgents/arugulad.plist, which starts
the daemon at login and after a crash.
- Logs:
~/Library/Logs/arugulad.log - Restart:
launchctl kickstart -k gui/$UID/arugulad - Stop:
launchctl bootout gui/$UID/arugulad
macOS has no systemd, so each pane’s small helper process keeps its
terminal while the daemon is gone (the plist sets --keep-panes): a
restart, an update or a crash leaves the programs running. Stopping the
daemon for good ends them a minute later, if no daemon has come back. Panes
run your login shell from the user database.
A Mac with no GUI login (one you reach only over ssh, where that user
hasn’t logged in to the desktop since it booted): arugulad install puts
the same agent in the background session. It keeps running after you log
out, but after a reboot it doesn’t start until you log in to the desktop or
run arugulad install again (arugula --ssh does that for you when the
daemon isn’t running), and the install says so.
From boot: arugulad install --system installs a LaunchDaemon,
/Library/LaunchDaemons/arugulad.$USER.plist, that runs the daemon as you
from boot, with nobody logged in. Run it as yourself, not as root: it runs
sudo for the two steps that need it. It replaces the agent; run arugulad uninstall first to go back. sudo launchctl kickstart -k system/arugulad.$USER restarts it.
A systemd user service, ~/.config/systemd/user/arugulad.service. With
lingering on (loginctl enable-linger $USER), it starts at boot, before you
log in.
- Logs:
journalctl --user -u arugulad - Restart:
systemctl --user restart arugulad - Stop:
systemctl --user stop arugulad
Each pane’s program runs in its own systemd scope, so a daemon restart, update or crash leaves it running, and the new daemon adopts it. Stopping the service ends the panes’ programs, as a reboot does.
Without systemd (a container, another init), the install script
installs the binaries and prints the command that starts the daemon. Pass
--keep-panes, so programs survive the daemon’s restarts as on macOS:
nohup ~/.local/bin/arugulad --keep-panes >>~/arugulad.log 2>&1 &A scheduled task, arugulad, that starts the daemon when you log on.
Logging off ends it, and its panes close a minute later.
- Logs:
arugulad.login the state directory arugulad install --system, from an elevated terminal, starts it at boot instead; programs there can’t use Credential Manager.
Each pane has a small helper process of its own, so panes keep running while
the daemon restarts or updates. The desktop app has no Daemon menu on
Windows; use arugulad in a terminal.
arugulad uninstall stops and removes whichever service arugulad install
set up; the binaries and your state stay
(Uninstalling). arugula status says which service
runs the daemon, its version, binary and log, and whether it’s joined to
Arugula control.
The daemon keeps layout, output, checkpoints and settings in its state directory, private to you (the folder is 0700, its files 0600):
$ARUGULA_STATE_DIRif it’s set;- else
$XDG_STATE_HOME/arugula; - else
~/.local/state/arugula; - on Windows,
%LOCALAPPDATA%\arugula\state.
--state-dir moves it.
Who gets in
Section titled “Who gets in”On this machine, only you can connect: the CLI through a Unix socket in
your private state directory, and anything on the TCP port only if it shows
the daemon’s local token (local-token in the state directory, made at
the first start). Loopback is shared by every account and program on the
machine, so being on it isn’t enough.
- Your browser gets the token as a cookie from a sign-in link:
arugula webopenshttp://127.0.0.1:7681through it (--printprints the link, to open by hand or through anssh -Lforward). Once per browser; it stays signed in until the token changes. The desktop app signs its own window in. - Programs send it as
Authorization: Bearer {token}(arugula --host http://127.0.0.1:7681does that for you). MCP clients usearugula mcpor anarugula mcp token. - A new token signs every browser and program out: delete
local-tokenand restart the daemon.
Over your tailnet, the daemon asks Tailscale who each caller is and lets in
only the login that owns the machine (--owner names another). Behind
tailscale serve, the identity it adds is believed only from Tailscale’s
own connection. Tagged nodes, Funnel and the internet never get in.
Through Arugula control, only your approved devices get in, over end-to-end encrypted connections.
Updates
Section titled “Updates”The daemon updates itself when you ask (Update now, or arugulad update),
and keeps your flags: see Install. If an update fails
before the restart, the old daemon carries on and says why in update.log
in the state directory.
Daemon flags
Section titled “Daemon flags”arugulad --help lists every flag. These are the ones you’re likeliest to
want. Pass them with arugulad install -- FLAGS so the service keeps them;
on a Mac where the desktop app runs the daemon, see
The Mac app’s daemon.
| Flag | Environment variable | What it does |
|---|---|---|
--listen ADDR |
ARUGULA_LISTEN |
where the page and API listen (default 127.0.0.1:7681; keep it on loopback) |
--owner LOGIN |
ARUGULA_OWNER |
the Tailscale login allowed in (default: the machine’s owner) |
--name NAME |
ARUGULA_NAME |
this machine’s name in host lists |
--state-dir DIR |
ARUGULA_STATE_DIR |
where state lives |
--shell "CMD ARGS" |
the command new panes run (default $SHELL -l) |
|
--no-shell-integration |
start shells without prompt and command marks | |
--no-manager-env |
don’t copy systemd’s user environment into new panes | |
--keep-panes |
ARUGULA_KEEP_PANES |
without systemd: keep programs running across restarts |
--no-claude-ide |
ARUGULA_NO_CLAUDE_IDE |
don’t be Claude Code’s IDE (no diff cards) |
--no-update-check |
ARUGULA_NO_UPDATE_CHECK |
don’t check for new releases |
--control URL |
ARUGULA_CONTROL |
the Arugula control that Getting started joins (default https://control.arugula.io) |
--direct-url URL |
ARUGULA_DIRECT_URL |
an address devices can reach this machine at directly |
--public-host NAME |
extra host names to accept | |
--allow-origin ORIGIN |
other origins whose pages may use this daemon | |
--block-listen, --block-domain, … |
ARUGULA_BLOCK_LISTEN, … |
browser blocks on ports |
--code-server PATH, --editor-idle SECONDS |
ARUGULA_CODE_SERVER, ARUGULA_EDITOR_IDLE |
editor blocks |
--peer URL, --token FILE, --sync |
ARUGULA_PEER, ARUGULA_TOKEN_FILE |
a box that can only dial out |
Next steps
Section titled “Next steps”- Environment variables: what panes and blocks start with.
- Troubleshooting: when the daemon doesn’t answer.