How to run Thruster and Kamal in Rails app

You may have heard of Thruster, the new no-config proxy from 37signals. Many Rails application developers already using Kamal are wondering if they need Thruster and how to integrate it into their existing Rails projects. This article addresses these questions and provides a detailed guide to using Thruster and Kamal in your Rails application.

Understanding Thruster and its benefits

Thruster, as a proxy for Puma, offers four key solutions: HTTP/2 support, HTTPS using Let's Encrypt, HTTP caching for public assets, and X-Sendfile support with compression. It was initially built for managing self-hosted products without needing Kamal. If you're using Rails with Kamal, managing TLS through Traefik or Cloudflare, and using CDNs for storage, you might not think you need Thruster. However, its X-Sendfile support makes it still useful in this case.

How Thruster improves the delivery of application assets

When you use send_file to send a file from Rails to a controller, an X-Sendfile header is created. This header is handled by Rack::Sendfile and is served by the proxy or Rack middleware, depending on the proxy. Here, Thruster steps in. It can take over the job of sending these files without any specific setup.

Integrating Thruster into your Rails project

Start by adding Thruster as a gem to your Gemfile as shown below:

# Gemfile
gem 'thruster', group: [:development, :production]

Then run the bundle install command. Make sure that Thruster isn't scoped for development only, as the gem needs to be available in production.
Then edit your Rails Dockerfile. We need to wrap Puma with the thrust command:

# Dockerfile - above rails 7

.....
# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
# Change Puma port to 3001 to keep Traefik default 3000 for Thruster
ENV HTTP_PORT="3000" \
TARGET_PORT="3001"
EXPOSE 3000
CMD ["bundle", "exec", "thrust", "./bin/rails", "server"]

With this configuration, you can set HTTP_PORT for Thruster and keep Traefik port 3000 as default, while TARGET_PORT is assigned to the Puma port. You now need to boot Puma on 3001:

# config/puma.rb
...
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
port ENV.fetch('PORT') { 3001 }

Finally, update the position of arguments in bin/docker-entrypoint:

#!/bin/bash -e

# If running the rails server then create or migrate existing database
if [ "${4}" == "./bin/rails" ] && [ "${5}" == "server" ]; then
  ./bin/rails db:prepare
fi

exec "${@}"

With these settings, Thruster will handle all requests for Puma.

Conclusion

Thruster, the no-config proxy from 37signals, offers several advantages as a proxy for Puma in your Rails projects. Even though we run Rails with Kamal, adding Thruster to the mix brings improvements, especially when serving application assets. This article provides a streamlined walk-through for setting up and running Thruster in your Rails projects.

Happy thrusting!