Two paths
You can get a systemd-managed Comis daemon two ways: Recommended: the one-line installer. The script athttps://comis.ai/install.sh
creates the comis system user, lays out /etc/comis, writes a managed
comis.service unit, sets up sudoers rules so the service user can start/stop/restart
its own daemon without root, and starts everything. This is the path the official
VPS install guide uses.
# managed-by: comis-installer. If
you edit it by hand, the installer will refuse to overwrite it on upgrade — so
manual edits stick around.
Manual: the steps below. Use this when you need a unit file under your own
control (different paths, different hardening, different user). The rest of this
page walks through that.
Prerequisites
Before setting up the systemd service, make sure you have:- Node.js 22 or newer installed on your server
- Comis built — run
pnpm buildin the Comis directory - A dedicated system user for running Comis (created in step 1 below)
Setup
Create a system user
Create a dedicated The
comis user that has no login shell. This is a security
best practice — the daemon runs under its own user with limited permissions.-r flag creates a system user (no home directory, no login). The
-s /sbin/nologin flag prevents anyone from logging in as this user.Install Comis
Copy your built Comis files to a system directory and create the data directory:
/opt/comis/— where the application code lives (read-only at runtime)/var/lib/comis/— where the database, logs, and runtime data are stored (read-write)
Create the configuration
Create a directory for the config file and environment variables:Create the environment file at
/etc/comis/env with your API keys and config path:Install the service file
Create the systemd unit file at Then reload systemd to pick up the new file:
/etc/systemd/system/comis.service:--permission also disables fd-based fs APIs (fsync, fchmod, fchown) at the daemon-process level. Credential file writes are best-effort durability (no fsync) and file permissions are best-effort. This is guarded at all call sites — the daemon will not crash — but the tradeoff is documented in Node Permissions — Production fd-API Disablement.Start the service
Enable the service (so it starts on boot) and start it immediately:Expected output:
Service file explained
Here is what each important section of the unit file does:Type=exec
systemd considers the service started onceexecve() returns. In-process
liveness is observed by Comis’s ProcessMonitor (event loop delay tracking)
and surfaced on the /health HTTP endpoint; crash recovery is handled by
Restart=on-failure below. Comis does not participate in the systemd
liveness-ping protocol — operators who require kernel-level watchdog
integration can add it via a systemd drop-in.
Restart=on-failure + RestartSec=5s
If the daemon crashes (exits with a non-zero code), systemd waits 5 seconds and then starts it again automatically. This covers unexpected errors, out-of-memory kills, and unhandled exceptions. Normal stops (viasystemctl stop) do not trigger
a restart.
MemoryMax=2G + TasksMax=100
Resource limits prevent the daemon from consuming too many system resources. If memory exceeds 2 GB, systemd kills the process (which then triggers a restart). TasksMax limits the number of threads and processes the daemon can create.Node.js —permission flags
The--permission flag enables the Node.js permission model, which restricts what
the daemon can access:
--allow-fs-read=/opt/comis— can read application code only from/opt/comis--allow-fs-write=/var/lib/comis— can write data only to/var/lib/comis--allow-child-process— can spawn child processes (needed for some tools)
See Node Permissions — Production fd-API Disablement for the impact of
--permission on daemon-process fd-based APIs (fsync, fchmod, fchown).EnvironmentFile=-/etc/comis/env
Loads environment variables (like API keys andCOMIS_CONFIG_PATHS) from the
specified file. The - prefix means systemd will not fail if the file is missing —
it simply skips loading it.
Security hardening directives
The bottom section of the unit file locks down the service:| Directive | What it does |
|---|---|
ProtectSystem=strict | Makes the entire filesystem read-only except explicitly allowed paths |
ProtectHome=yes | Hides all home directories from the service |
NoNewPrivileges=yes | Prevents the daemon from gaining elevated permissions |
MemoryDenyWriteExecute=yes | Blocks creating memory that is both writable and executable |
SystemCallFilter=@system-service | Only allows system calls needed for normal services |
PrivateDevices=yes | Hides physical devices from the service |
Browser tool wiring (when installed with --with-browser / --with-xvfb / --with-cloakbrowser)
The browser-tool flags don’t change the security posture — they widen specific write paths just enough for the chosen browser binary to launch:
| Install flag | ReadWritePaths additions | --allow-fs-write additions |
|---|---|---|
--with-browser (stock Chrome) | ~/.config/google-chrome, ~/.local/share/applications, ~/.config/comis/browser | Same set; mirrored on the Node permission model |
--with-cloakbrowser (stealth Chromium) | ~/.cloakbrowser, ~/.config/chromium, ~/.config/comis/browser | Same; tighter than the Chrome variant (no mimeapps write needed — CloakBrowser patches that out) |
--with-xvfb | (no extra paths — see companion unit below) | (no extra paths) |
~/.local/share/applications for mimeapps.list (Chrome’s default-browser registration; no flag disables it). The cloak variant doesn’t.
comis-xvfb.service companion (when installed with --with-xvfb)
A second managed unit at /etc/systemd/system/comis-xvfb.service runs Xvfb on display :99 as the same comis user:
-nolisten tcp keeps the X server on a Unix socket only; -ac is safe because the socket is owned by the comis user and inaccessible from outside that namespace. The main comis.service unit picks up the display by way of three additional directives:
JoinsNamespaceOf= is load-bearing — without it, PrivateTmp=yes on the main unit gives the daemon its own /tmp namespace and the X11 socket at /tmp/.X11-unix/X99 is unreachable. The pair share /tmp for exactly this purpose.
Manage the companion unit the same way as the main one:
bash install.sh --uninstall (no separate command needed).
Common commands
| Command | What it does |
|---|---|
sudo systemctl start comis | Start the daemon |
sudo systemctl stop comis | Stop the daemon (waits up to 45 seconds for graceful shutdown) |
sudo systemctl restart comis | Stop and start the daemon |
sudo systemctl status comis | Show current status, PID, memory, and recent log lines |
sudo systemctl enable comis | Start automatically on boot |
sudo systemctl disable comis | Do not start on boot |
Viewing logs
systemd sends all daemon output to the journal. Usejournalctl to view logs:
Live logs (follow mode):
The daemon also writes logs to
~/.comis/logs/daemon.log (resolved against the
service user’s home — /var/lib/comis/.comis/logs/daemon.log for an installer-managed
service). See Logging for details on configuring log
levels and rotation.Related pages
Daemon
How the daemon starts, runs, and shuts down.
pm2
Alternative process manager for macOS and Linux.
Docker
Run Comis in a Docker container.
Logging
Configure log levels, rotation, and structured output.
Troubleshooting
Solutions to common issues.
