unitup is a zero-dependency CLI & library to run Node.js, Python, Ruby, PHP, Bun, Deno, Go, Elixir, Shell scripts, and compiled native executables as native background services across Linux (systemd), macOS (launchd), and Windows (Windows Services).
npm install -g unitup
# Install & start service natively on Linux, macOS, or Windows
unitup install server.js --name api --start
unitup status api
unitup logs api --follow
# Preview generated native service configuration without installing
unitup install server.js --name api --dry-run
Exits immediately after configuring services. No resident unitup master process continuously eating CPU or RAM.
First-class adapters for Linux (systemd), macOS (launchd), and Windows (SCM).
Out-of-the-box support for Node.js, Python, Ruby, PHP, Bun, Deno, Go, Elixir, Shell, and native binaries.
Use --dry-run to inspect exact generated unit files or XML plists before installing.
Built strictly with Node.js standard libraries. Zero heavy third-party npm dependencies or native bindings.
Full support for existing Linux systemd workflows, timers, and legacy metadata without breaking changes.
Unitup provides unified command syntax while leveraging the native service architecture on each operating system:
| Feature | Linux (systemd) |
macOS (launchd) |
Windows (Windows Services) |
|---|---|---|---|
| Install / Uninstall | systemd user/system unit |
launchd agent/daemon .plist |
Windows Service (sc.exe) |
| Start / Stop / Restart | systemctl |
launchctl kickstart / kill |
sc.exe start / stop |
| Auto-Start on Boot | systemctl enable |
RunAtLoad: true |
Automatic Service Startup |
| Environment Variables | Environment= |
EnvironmentVariables plist dict |
Process Environment Forwarding |
| Logs | journalctl |
File-based streaming (~/.config/unitup/logs/) |
File-based streaming (~/.config/unitup/logs/) |
| Crash Recovery | Restart=on-failure |
KeepAlive plist dictionary |
Host supervisor auto-restart & uptime reset |
| User Services | ~/.config/systemd/user/ |
~/Library/LaunchAgents/ |
Supported |
| System Services | /etc/systemd/system/ (--system) |
/Library/LaunchDaemons/ (--system) |
Native Windows Service |
| Dry-Run Inspection | --dry-run (systemd unit) |
--dry-run (plist XML) |
--dry-run (service JSON) |
unitup is not a PM2 replacement or an independent process manager. While PM2 runs its own master daemon, unitup is strictly a thin, transparent CLI layer on top of native OS service managers.
| Feature | unitup | PM2 |
|---|---|---|
| Background Process (Daemon) | No resident daemon. OS directly supervises processes. | Runs a persistent process-management daemon. |
| System Integration | Native OS service managers (systemd, launchd, Windows Services). |
Custom process monitoring via PM2's internal daemon. |
| Multi-Runtime Support | Node, Python, Ruby, PHP, Bun, Deno, Go, Elixir, Shell, Native Binaries. | Node.js-focused process manager with support for running other commands. |
| Privileges Required | Does not require elevated permissions for user services. | Boot integration commonly requires running PM2 startup with elevated privileges. |
| Dependencies | 0 Runtime Dependencies (Node.js standard modules only). | Dozens of 3rd party npm packages. |
| Log Management | Native journald (Linux) / Cross-platform streaming tail files. |
Manages .pm2/logs files (requires pm2-logrotate plugin). |
Unitup generates native service definitions directly from a canonical command + args model:
[Unit]
Description=unitup service: worker
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/user/apps
ExecStart=/usr/bin/python3 /home/user/apps/worker.py
Restart=on-failure
RestartSec=3
Environment=APP_ENV="production"
[Install]
WantedBy=default.target
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>Label</key>
<string>dev.unitup.worker</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/python3</string>
<string>/home/user/apps/worker.py</string>
</array>
<key>WorkingDirectory</key>
<string>/home/user/apps</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key><false/>
<key>Crashed</key><true/>
</dict>
</dict>
</plist>
| Runtime | Extension / Auto-Detection | Command Executed |
|---|---|---|
| Node.js | .js, .mjs, .cjs or shebang #!/usr/bin/env node |
node <script> |
| Python | .py or shebang #!/usr/bin/env python3 |
python3 <script> |
| Ruby | .rb or shebang #!/usr/bin/env ruby |
ruby <script> |
| PHP | .php or shebang #!/usr/bin/env php |
php <script> (Supports -S 0.0.0.0:8080) |
| Bun | Explicit --runtime bun or shebang #!/usr/bin/env bun |
bun <script> |
| Deno | Explicit --runtime deno or shebang #!/usr/bin/env deno |
deno run <script> |
| Go | .go or shebang #!/usr/bin/env go |
go run <script> |
| Elixir | .ex, .exs or shebang #!/usr/bin/env elixir |
elixir <script> |
| Shell | .sh or shebang #!/bin/bash / #!/bin/sh |
bash <script> |
| Native Executable | Explicit --runtime native |
Direct binary execution (./server) |
unitup doctorRuns cross-platform diagnostics (Platform, Service manager, Node.js path, data directory, permissions, lingering, and detected runtimes).
unitup doctor
unitup install / add <script|exec> [options]Generates and installs a new native background service.
# Flags:
# --name Service name (default: script basename)
# --runtime Specify runtime (node, python, ruby, php, bun, deno, shell, go, elixir, native)
# --command Explicit binary executable (bypasses auto-detection)
# --env KEY=val Set environment variable
# --env-file Load environment file
# --dry-run Preview generated service file without installing
# --system Install as system-wide service (LaunchDaemons / root systemd)
# --start Enable and start service immediately
# --force, -f Force overwrite of currently running service
unitup install server.js --name api --start
unitup install worker.py --name worker --group backend --env APP_ENV=production --start
unitup install server.js --dry-run
unitup list / unitup lsLists all managed services in a clean aligned table.
unitup list --group backend
unitup inspect <name>Displays full service configuration overview without revealing secrets or environment variables.
unitup inspect api
unitup start / stop / restart <name|@group>Controls service state individually or in batch using @group syntax.
unitup start api
unitup restart @backend
unitup stop api
unitup enable / disable <name>Enables or disables service startup on boot.
unitup enable api
unitup disable api
unitup status / logs <name>Inspects process status with live metrics or streams service logs with advanced filtering.
unitup status api --verbose
unitup logs api --follow
unitup logs api --lines 50 --grep "ERROR"
unitup uninstall / remove <name|@group> [--force]Stops, disables, and deletes a service or group. Active running services require --force / -f to prevent accidental deletion.
unitup uninstall api
unitup uninstall @backend --force
unitup supports native systemd timer scheduling on Linux without running continuous worker processes or cron daemons.
# Create interval schedule (--every 30m)
unitup schedule cleanup.js --every 30m --persistent --start
# Create calendar schedule (--calendar daily with random delay jitter)
unitup schedule backup.py --calendar daily --random-delay 10m --persistent --start
# List all schedule timers
unitup schedules # or unitup timers
# Detailed schedule status
unitup schedule-status cleanup
unitup exports full ESM and CommonJS interfaces with complete TypeScript declarations (index.d.ts).
import {
ServiceManager,
getAdapter,
getPlatformCapabilities,
createService,
getServiceStatus,
listServices
} from "unitup";
// High-level service manager (auto-resolves current OS adapter)
const manager = new ServiceManager();
// Install and start service
await manager.install({
name: "api",
script: "./server.js",
env: { NODE_ENV: "production" }
}, { start: true });
// Check status
const status = await manager.status("api");
console.log(`State: ${status.state}, PID: ${status.pid}, Platform: ${status.platform}`);
// Inspect capabilities
const caps = getPlatformCapabilities();
console.log(`Service Manager: ${caps.serviceManager}`);