The Tinkwell .deb package installs the tw CLI and runtime but does not register a service. Running the coordinator under systemd is recommended for production deployments on a server, a Raspberry Pi, or any unattended Linux host: you get automatic startup on boot, restart on crash, and centralized logging through journald.
This page is a copy-pasteable walkthrough. It assumes Tinkwell is already installed via the Debian package.
Run the coordinator under an unprivileged service account rather than root.
sudo useradd --system \
--home /var/lib/tinkwell \
--shell /usr/sbin/nologin \
tinkwell
sudo mkdir -p /var/lib/tinkwell /etc/tinkwell
sudo chown -R tinkwell:tinkwell /var/lib/tinkwell/var/lib/tinkwell is the service's home directory; the coordinator stores runtime state and plugins under it. /etc/tinkwell holds the ensemble configuration.
sudo cp ensemble.tw /etc/tinkwell/ensemble.tw
sudo chown tinkwell:tinkwell /etc/tinkwell/ensemble.twAny include paths inside ensemble.tw should be absolute or relative to /etc/tinkwell/ so they resolve regardless of the working directory.
Create /etc/systemd/system/tinkwell.service:
[Unit]
Description=Tinkwell coordinator
Documentation=man:tw(1)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=tinkwell
Group=tinkwell
WorkingDirectory=/var/lib/tinkwell
ExecStart=/usr/bin/tw start /etc/tinkwell/ensemble.tw
Restart=on-failure
RestartSec=5s
Environment=DOTNET_NOLOGO=1
[Install]
WantedBy=multi-user.targettw start runs the coordinator in the foreground, which is what Type=simple expects. Do not pass -B / --background in the unit; that would detach and systemd would treat the service as exited immediately.
sudo systemctl daemon-reload
sudo systemctl enable --now tinkwellVerify it's running:
sudo systemctl status tinkwell
tw ping
tw statusIf tw ping fails from your interactive shell while the service is running, see Named-pipe visibility below.
The coordinator writes to stdout/stderr, which systemd captures in journald:
# Follow live
sudo journalctl -u tinkwell -f
# Last 200 lines
sudo journalctl -u tinkwell -n 200
# Since the last boot
sudo journalctl -u tinkwell -bAfter editing /etc/tinkwell/ensemble.tw:
sudo systemctl restart tinkwellThe unit above is intentionally minimal. The following directives add defense-in-depth and are safe for most deployments:
[Service]
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/var/lib/tinkwell /etc/tinkwell
LockPersonality=true
RestrictRealtime=truetw uses .NET named pipes to talk to a running coordinator. On Linux, these live under /tmp/CoreFxPipe_<pipe-name>. If you add PrivateTmp=true to the unit, the service gets a private /tmp namespace and the pipe will not be visible to your interactive shell - tw ping, tw status, etc. will fail with a "coordinator not reachable" error.
If you need both PrivateTmp isolation and interactive CLI access, either:
- run the CLI as the
tinkwelluser (sudo -u tinkwell tw status), or - override the pipe name via
--pipeon both sides and use a pipe path that is reachable from both namespaces.
For most single-purpose hosts, omitting PrivateTmp is the pragmatic choice.
sudo systemctl disable --now tinkwell
sudo rm /etc/systemd/system/tinkwell.service
sudo systemctl daemon-reload
sudo userdel tinkwell # keeps /var/lib/tinkwell intact
sudo rm -rf /var/lib/tinkwell # only if you also want to wipe stateUninstalling the .deb (sudo dpkg -r tinkwell) does not remove the service file, the user, or /var/lib/tinkwell - they were created here, by hand.