systemd units: Puma + Sidekiq

What bosun add generates, the shipped reference units, and optional socket activation.

sudo bosun add <app-name> writes and enables two systemd units per app automatically — you normally never write one by hand. This doc covers what gets generated, the shipped reference copies, and optional socket activation.

Standalone mode (no argus-tty): User=webapp-svc Group=webapps below is actually User=<your account> Group=bosun-apps — the generated units already have the right names for your host. See standalone.md.

What bosun add generates

write_unit() in bin/add.sh writes both units directly (not from a template file):

/etc/systemd/system/puma-<app>.service
  /etc/systemd/system/sidekiq-<app>.service

Both run as User=webapp-svc Group=webapps, WorkingDirectory=<app>/current, EnvironmentFile=-<app>/shared/.env, Restart=on-failure. They're enabled but not started by add — there's no release yet (current doesn't exist until the first bosun deploy).

Restart both at once with bosun restart <app-name> (added alongside this doc — admin/deployer tier, same as deploy/cleanup), or individually via the already-whitelisted sudo systemctl {start,stop,restart} <unit>.

The shipped reference copies

Two files ship at /usr/lib/bosun/deploy/:

Both are templates with the literal placeholder app_name — replace every occurrence with your actual app name before using either by hand.

Puma's own log redirect, and why the generated unit doesn't also redirect stdout/stderr: config/puma.rb (the app's own file) already does its own stdout_redirect to shared/log/puma.log. If the systemd unit also set StandardOutput=/StandardError= to that same path, systemd would create/open it as root before dropping privileges to User=webapp-svc, and Puma's later reopen of that now-root-owned file would fail with EACCES. Leave those unset (defaults to the journal) and let Puma own the file itself. Unit-level events (start/stop/restart, failures to launch) still show up in the journal regardless:

sudo journalctl -t puma-<app> -f

Socket activation (optional)

puma-app_name.socket.sample lets systemd own the Unix socket file (shared/tmp/sockets/puma.sock) and hand Puma an already-open file descriptor, instead of Puma creating/binding the socket itself at boot. Buys zero dropped connections across a Puma restart. To use it:

  1. Copy puma-app_name.socket.sample to /etc/systemd/system/puma-<app>.socket, replacing app_name.
  2. Remove or comment out the bind "unix://..." line in the app's config/puma.rb — with socket activation, systemd owns that path; Puma trying to bind it too defeats the point (and can fail outright).
  3. Add Requires=puma-<app>.socket under [Unit] in puma-<app>.service so the socket is always up before Puma starts.
  4. sudo systemctl daemon-reload
  5. sudo systemctl enable --now puma-<app>.socket (starting just the .socket activates on first connection; start both units if you don't need on-demand activation).

This is entirely optional — config/puma.rb binding its own socket directly (the default bosun add leaves in place) is simpler and fine for most setups; socket activation only matters if dropped connections during a Puma restart are a real concern for this app.