Wiring a third long-running process (a sneakers consumer, Docker, anything else) into bosun's own format.
sudo bosun add <app-name> only ever generates two systemd units: puma-<app>.service and sidekiq-<app>.service (see systemd.md). Some apps need a third long-running process — a RabbitMQ consumer (sneakers), a different queue backend, a scheduled worker pool, whatever. This doc covers how to add one in bosun's own format, so it gets the same env loading, logging convention, and deploy-time start/stop handling every other bosun-managed process already gets, instead of a one-off hand-rolled unit that drifts from everything else on the box.
Standalone mode (no argus-tty):
User=webapp-svc Group=webappsin the template below is actuallyUser=<your account> Group=bosun-apps— match whatever/etc/bosun.conf/the puma/sidekiq units on your host already use. See standalone.md.
install/setup areDevelopers/admins have no whitelisted way to create or edit systemd unit files themselves (no sudo tee /etc/systemd/system/... in argus-tty's sudoers whitelist, by design — see the main README's "Who can do what"). bosun add's write_unit() is the only thing that creates unit files, and it only ever calls itself for puma/sidekiq. That leaves two paths for a third service:
systemctl start/stop/restart/status already is), just not something bosun add does automatically for you.docker instead — already fully whitelisted for developers/admins, no unit file needed at all. See "Docker instead of systemd" below.Either way, your app's own deploy override (shared/do-release) is what actually starts/stops/restarts it on every deploy — see "Wiring it into deploy" below.
Ask an admin to create /etc/systemd/system/<name>-<app>.service, following exactly the same template bin/add.sh's write_unit() uses for puma/sidekiq — same user, same env loading, same restart policy:
[Unit]
Description=<name> (<app>)
After=network.target
[Service]
Type=simple
User=webapp-svc
Group=webapps
WorkingDirectory=/var/www/<app>/current
EnvironmentFile=-/var/www/<app>/shared/.env
ExecStart=/var/www/<app>/shared/bin/<name>-exec
Restart=on-failure
RestartSec=3
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable <name>-<app>.service # don't start yet — no exec wrapper exists until the next step
shared/bin/<name>-exec) — this part IS self-serviceUnlike the unit file, shared/bin/ is webapps-group writable — any developer/admin can create this without an admin's help, same as bosun add already does for shared/bin/puma-exec/shared/bin/sidekiq-exec:
#!/bin/bash
# shared/bin/<name>-exec — resolves RUBY_VERSION/RUBY_GEMSET from
# deploy.conf at RUN time, not template time, same as puma-exec/
# sidekiq-exec.
set -euo pipefail
set -a
source "/var/www/<app>/shared/deploy.conf"
[[ -f "/var/www/<app>/shared/.env" ]] && source "/var/www/<app>/shared/.env"
set +a
cd "/var/www/<app>/current"
# RVM's wrapper binaries don't set rvm_path themselves — without it, RVM's
# stock per-ruby .irbrc falls through to requiring a personal ~/irbrc that
# usually doesn't exist, crashing irb/console for everyone.
export rvm_path="/usr/local/rvm"
# Own logfile via shell redirection (>> ... 2>&1) — NOT systemd's
# StandardOutput=file:/StandardError=file:, and NOT a `-L`/`--logfile` CLI
# flag if the process doesn't have one (Sidekiq 6.0+ removed its own
# entirely). This is the one logging convention every bosun-managed
# worker uses — see the sidekiq-exec/sneaker-exec wrappers for other apps
# in this repo's scripts/ directory that already do this.
exec "/usr/local/rvm/wrappers/${RUBY_VERSION}@${RUBY_GEMSET}/bundle" exec <your-command-here> \
>> "/var/www/<app>/shared/log/<name>.log" 2>&1
chmod 755 /var/www/<app>/shared/bin/<name>-exec
sudo systemctl start <name>-<app>.service
tail -f /var/www/<app>/shared/log/<name>.log
# unit: sneaker-myapp.service — ExecStart=/var/www/myapp/shared/bin/sneaker-exec
# shared/bin/sneaker-exec's exec line:
exec "/usr/local/rvm/wrappers/${RUBY_VERSION}@${RUBY_GEMSET}/bundle" exec rake sneakers:run \
>> "/var/www/myapp/shared/log/sneaker.log" 2>&1
If the worker needs its own fixed config (which queue, concurrency) rather than a secret, bake it in as a plain export in the wrapper, same as -q/-c flags get baked into a sidekiq-<name>-exec wrapper — it isn't a secret, so it doesn't belong in shared/.env:
export WORKERS="GhaProcessQueueWorker"
export RM_WORKER="4"
No unit file, no admin needed — docker is already fully whitelisted:
sudo docker rm -f <name>-<app> 2>/dev/null || true
sudo docker run -d --name <name>-<app> --restart unless-stopped \
-v "/var/www/<app>/current:/app" -w /app --env-file "/var/www/<app>/shared/.env" \
your-ruby-image bundle exec <your-command-here>
Your deploy override then calls docker stop|start|inspect <name>-<app> wherever the systemd path below calls systemctl/svc_active.
The built-in do-release only knows about puma-<app>/sidekiq-<app>. A third service needs a per-app override — copy do-release.sample to /var/www/<app>/shared/do-release, chmod +x it, and:
*_worker function/variable to your service's name (e.g. WORKER_UNIT -> SNEAKER_UNIT, matching the unit name from Path 1, or the container name from Path 2).systemctl stop/start/svc_active calls in those renamed functions for docker stop/start/inspect if you went with Path 2.See do-release.sample's own header comment for the exact lines to touch — it already documents this swap in detail. deployment/do-release (the built-in default) now also prints a one-line, non-blocking warning on every deploy for an app with no override at all, as a safety net against a forgotten or mistyped shared/do-release — see its own comment.
Own logfile via shell redirection in the exec wrapper (>> logfile 2>&1), not systemd's StandardOutput=file:/StandardError=file: and not a process's own -L/--logfile flag if it has one. Three reasons this is the standard across every bosun-managed worker:
-L/-P/-d entirely — it expects an external supervisor, i.e. systemd, to own logging).StandardOutput=file: opens the file as root before systemd drops privileges to webapp-svc — if the process later reopens that same path itself (as Puma does for config/puma.rb's own stdout_redirect), the reopen fails with EACCES because the file's now root-owned. See systemd.md's note on exactly this for Puma.tail -f shared/log/<name>.log always works the same way.