sm2
A universal application operations agent, written in Go.
Run and supervise apps in any language β simple to use, reliable, and a single binary.
sm2.dev
π§ sm2 is in active development (pre-release). It works and is tested, but
commands, flags and config may still change before a stable v1.0.0. Try it,
and please report anything rough.
sm2 is a single Go binary that runs your applications, watches them, and brings
them back when they die. It pairs a small CLI with a background agent that talks
over a local Unix socket β no daemon to configure, no runtime to install.
Install
Quick install (Linux & macOS, no Go required):
curl -fsSL https://raw.githubusercontent.com/abdorizak/sm2/main/install.sh | bash
It downloads the prebuilt binary for your OS/architecture, verifies its checksum,
and installs it to /usr/local/bin. The command is sm2.
Other ways to install
With Go:
go install github.com/abdorizak/sm2/cmd/sm2@v0.1.0-dev.6
Manual download: grab the archive for your platform from the
releases page and extract it.
Windows is not supported (sm2 uses Unix process groups, signals and sockets).
Quick start
sm2 start web -- npm run start # run & supervise (command after --)
sm2 start api --restart always -- ./api # flags go before --
sm2 status # boxed, colored table
sm2 logs web --follow # tail output
sm2 save # snapshot for reboot survival
Pass the command positionally after --. sm2's own flags (--restart, -e, β¦)
go before --. --cmd "<shell>" is an optional escape hatch for one-liners with pipes/&&.
sm2 is a single binary β it starts and talks to a background agent for you;
there is no separate daemon to run.
Examples
sm2 start abdorizak.dev --restart always -- npm run start
sm2 start billing-api -i 3 -- ./billing-server
sm2 start email-worker --restart on-failure -- python worker.py
sm2 start cache -- redis-server --port 6380
sm2 start nightly-report --cron-restart "0 3 * * *" -- ./report.sh
sm2 start landing --watch --dir /srv/landing -- npm run dev
sm2 start metrics --max-memory-restart 300M --namespace infra -- ./metrics
sm2 start bot -e TOKEN=xoxb-β¦ -- node telegram-bot.js
Features
- Universal process manager β Go, Node, Python, Rust, shell, any executable.
PID tracking, process groups, graceful stop (SIGTERM β SIGKILL).
- Auto-restart β
always / on-failure / never, with max-retries and
fixed or exponential backoff.
- Live monitoring β per-process CPU, memory, uptime and restart counts.
- Declarative config β describe your stack in
sm2.yaml; config reload
reconciles the running set (start new, stop removed, restart changed).
- Restart triggers β
--max-memory-restart, --watch (file changes),
--cron-restart (schedule).
- Reboot survival & self-healing β the agent auto-saves its live process
list and resurrects it if it restarts;
save / resurrect / startup
(launchd & systemd) for boot.
- Notifications β Discord webhooks on start/stop/crash/restart, low disk space, and agent shutdown.
- Single binary β auto-spawned agent, zero external dependencies.
Commands
|
|
start stop restart delete reset signal |
lifecycle (target a name, all, or --namespace) |
status (ls/ps) describe logs flush ping |
inspect |
config notify set save resurrect startup unstartup kill update |
config & boot |
Run sm2 <command> --help for usage, or see the full reference in the docs site.
Notifications
Get Discord pings on start/stop/crash/restart. Set it up without a config file
(persists to ~/.sm2/notify.json, survives restarts):
sm2 notify discord --webhook "https://discord.com/api/webhooks/β¦"
sm2 notify test # send a test message
sm2 notify status
sm2 notify discord --disable
Or declare notifications.discord in config and sm2 config reload. Last action wins.
Messages are rich, color-coded embeds (app Β· event Β· host Β· details), and delivery is
reliable: sm2 honors Discord's rate limit (Retry-After on 429) and retries transient
failures with backoff, so important events aren't silently dropped. When the agent shuts
down it sends an agent stopping notice and flushes any queued events before exiting.
Disk-space alerts
sm2 watches the server's free disk space out of the box β one statfs call per
minute, effectively free. If Discord notifications are enabled, you'll be warned
before the disk runs out; there's no second switch to remember:
sm2 set disk.threshold 15 # alert when free space drops below 15% (default 10)
sm2 set disk.path /var # watch a different filesystem (default: the one holding ~/.sm2)
sm2 set disk.monitor off # opt out entirely
You get a β οΈ disk space low embed when free space crosses the threshold, a reminder
every 6 hours while it stays low, and a πΎ disk space recovered notice once it climbs
back above the threshold (with a small margin so it doesn't flap). Settings persist to
~/.sm2/disk.json and survive agent restarts.
Log rotation
By default sm2 appends each app's stdout/stderr to ~/.sm2/logs/<name>.{stdout,stderr}.log.
Turn on rotation so those files manage themselves instead of growing forever:
sm2 set logs.max_size 50M # rotate a log once it passes 50 MB
sm2 set logs.retain 7 # keep 7 rotated files, prune the rest
sm2 set logs.compress true # gzip rotated files (web.stdout.log.1.gz)
sm2 set logs.interval "0 0 * * *" # also rotate daily at midnight (optional cron)
sm2 set # show the current settings
sm2 set logs.rotate off # turn rotation back off
sm2 set logs.rotate now # rotate every log immediately
Setting any logs.* option turns rotation on. Settings persist to ~/.sm2/logrotate.json
and survive restarts. The agent checks sizes every 30s (and on the cron schedule, if set);
rotation is copy-truncate, so apps keep logging without a restart. If Discord
notifications are enabled, sm2 also pings you when a log is rotated for exceeding its limit.
You can declare the same thing in config:
logs:
rotate: true
max_size: 50M
retain: 7
compress: true
interval: "0 0 * * *" # optional
Configuration
Describe your stack in sm2.yaml or sm2.toml β sm2 picks the parser
by file extension. sm2 config init -c sm2.toml writes a TOML starter.
agent:
name: production
apps:
api:
command: "./api"
restart:
policy: always
max_retries: 5
instances: 2
max_memory_restart: 300M
environment:
PORT: "8080"
notifications:
discord:
enabled: true
webhook: "https://discord.com/api/webhooks/β¦"
Environment & reload
An app's environment is a base it inherits plus per-app overrides
(environment: in config, or -e KEY=VALUE on start); overrides win.
- Change env in config β
sm2 config reload restarts the apps whose env (or
anything else) changed, leaving the rest running.
- Pull your current shell env into a running app β
sm2 restart <app> --update-env.
A plain sm2 restart keeps the agent's older environment, so use --update-env
after you export something new.
sm2 reload is an alias of restart. sm2 restarts the process (a brief blip) β
it is not a zero-downtime rolling reload.
Development
make build # compile to ./bin/sm2
make test # Go unit tests
make test-cli # end-to-end CLI smoke test
make test-all # both
make install # install to $GOPATH/bin
The landing page and documentation site live in a separate repository.
License
Apache-2.0 Β© abdorizak