Using systemd to monitor Puma

This took me longer than I would have liked, and I'd love to be able to look this up in the future, so I figured it might help someone else as well.

The first experiment was to create a Puma service file.

# /etc/systemd/system/puma.service
[Unit]
Description=Puma Rails Server
After=network.target

[Service]
Type=simple
User=deploy
WorkingDirectory=/var/www/app/current
ExecStart=/home/deploy/.rbenv/bin/rbenv exec bundle exec puma -C /var/www/app/shared/config/puma.rb
ExecStop=/home/deploy/.rbenv/bin/rbenv exec bundle exec pumactl -S /var/www/app/shared/tmp/pids/puma.state stop
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target

The WorkingDirectory is the path to the app running the application. In my case, the ExecStart and ExecStop uses rbenv but you can replace it with something else. 

Now we need to enable the service (the puma docs helped me here: https://github.com/puma/puma/blob/master/docs/systemd.md#usage):

# After installing or making changes to puma.service
systemctl daemon-reload

# Enable so it starts on boot
systemctl enable puma.service

# Initial startup.
systemctl start puma.service

# Check status
systemctl status puma.service

# A normal restart. Warning: listener's sockets will be closed
# while a new puma process initializes.
systemctl restart puma.service

All is good and well, but how do I get Capistrano to restart it? My first attempt yielded errors:

cap production puma:smart_restart
00:00 puma:restart
      01 sudo /bin/systemctl restart puma.service
      01 sudo: no tty present and no askpass program specified
(Backtrace restricted to imported tasks)
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as deploy@example.com: sudo exit status: 1
sudo stdout: Nothing written
sudo stderr: sudo: no tty present and no askpass program specified

SSHKit::Command::Failed: sudo exit status: 1
sudo stdout: Nothing written
sudo stderr: sudo: no tty present and no askpass program specified

Tasks: TOP => puma:restart
(See full trace by running task with --trace)

Oh great, how do I get a non-sudo user to execute a sudo command?

I spent much more time on this than I would have liked, but the solution is simple. I needed help to figure out the comma-separated list of allowed commands.

# /etc/sudoers.d/deploy
deploy ALL = NOPASSWD: /bin/systemctl restart puma.service, /bin/systemctl start puma.service, /bin/systemctl stop puma.service

So almost ready; the last thing is to provide the service name to capistrano: 

set :puma_service_unit_name, "puma.service"

Let's give it another go:

cap production puma:smart_restart
00:00 puma:restart
      01 sudo /bin/systemctl restart puma.service
    ✔ 01 deploy@example.com 0.634s

Make sure you verify the start time of the Puma process to make sure it does restart (the start time should change).