Rob Zolkos

February 21, 2024

(How I) Deploy Solid Queue with Capistrano

I manage a small number of Rails apps that haven't yet migrated to Kamal for deployment but I wanted to migrate to solid_queue asap to reduce the service burden of running redis.

These applications are deployed with Capistrano on Ubuntu LTS servers and the solid_queue is managed by linux's systemd.  There isn't much information out there on how to deploy solid_queue with Capistrano so hopefully this helps those of you that still use this popular way of deploying Rails applications.

The systemd service is defined in a file located in your deploy users home folder

/home/<deploy user>/.config/systemd/user/solid_queue.service

It looks like this (you may want to update Ruby versions and paths and such)

[Unit]
Description=solid_queue for app
After=syslog.target network.target

[Service]
Type=simple
Environment=RAILS_ENV=production
WorkingDirectory=/home/<deploy user>/apps/<app name>/current
ExecStart=/usr/local/bin/chruby-exec ruby-3.3.0 -- bundler exec rake solid_queue:start
ExecReload=/bin/kill -TSTP $MAINPID
ExecStop=/bin/kill -TERM $MAINPID

Environment=MALLOC_ARENA_MAX=2

RestartSec=1
Restart=on-failure

SyslogIdentifier=solid_queue

[Install]
WantedBy=default.target

Once you have this file in place you need to enable it, start it and ensure that it runs even when your deploy user is logged out.

Reload the daemon so the new service is loaded
systemctl --user daemon-reload

Start the service
systemctl --user start solid_queue.service

Enable the service to start at boot
systemctl --user enable solid_queue.service

Enable the service to keep running when deploy user is logged out
sudo loginctl enable-linger <deploy username>

That should be the systemd part done.  Next up is getting Capistrano configured to stop and start the solid_queue service during a deployment.

Here is the config I use in my deploy.rb file to get solid_queue systemd service to restart.

set :solid_queue_systemd_unit_name, "solid_queue.service"

namespace :solid_queue do
  desc "Quiet solid_queue (start graceful termination)"
  task :quiet do
    on roles(:app) do
      execute :systemctl, "--user", "kill", "-s", "SIGTERM", fetch(:solid_queue_systemd_unit_name), raise_on_non_zero_exit: false
    end
  end

  desc "Stop solid_queue (force immediate termination)"
  task :stop do
    on roles(:app) do
      execute :systemctl, "--user", "kill", "-s", "SIGQUIT", fetch(:solid_queue_systemd_unit_name), raise_on_non_zero_exit: false
    end
  end

  desc "Start solid_queue"
  task :start do
    on roles(:app) do
      execute :systemctl, "--user", "start", fetch(:solid_queue_systemd_unit_name)
    end
  end

  desc "Restart solid_queue"
  task :restart do
    on roles(:app) do
      execute :systemctl, "--user", "restart", fetch(:solid_queue_systemd_unit_name)
    end
  end
end

# SolidQueue hooks
after "deploy:starting", "solid_queue:quiet"
after "deploy:updated", "solid_queue:stop"
after "deploy:published", "solid_queue:start"
after "deploy:failed", "solid_queue:restart"

That is all I needed to do.  No extra gems.  It all works smoothly, you will see the solid_queue workers shutdown and then start again after the app is deployed.

Let me know if you see anything that can be improved.

About Rob Zolkos

Ruby on Rails developer here to help solo devs and small teams amplify their impact. Embracing the Rails way with Hotwire and Stimulus. Email: robzolkos@hey.com Twitter: https://twitter.com/robzolkos