Thruster is a new zero config proxy from 37signals. Here’s how to add it to an existing Rails projects deployed with Kamal.
Thruster and Kamal
Thruster solves 4 things as a proxy for Puma:
- HTTP/2 support
- HTTPS with Let’s Encrypt
- HTTP caching of public assets
X-Sendfile
support and compression
This makes sense as it was created to handle self-hosted ONCE products without Kamal. However, we don’t really need all of that if we already run Rails with Kamal. We handle TLS either directly with Traefik or Cloudflare, and offload storage concerns to CDNs.
So do we need Thruster at all?
X-Sendfile
Thruster still improves serving application assets like stylesheets and JavaScripts thanks to its X-Sendfile
support.
When you serve a file from Rails (e.g. with send_file
in a controller), an X-Sendfile
header is set. This header is processed by Rack::Sendfile
and depending on the proxy, it’s served with the proxy or by this Rack middleware.
And that’s where Thruster comes in. Without any specific configuration we can offload sending these files to Thruster.
Installation
You can add Thruster as a gem to your Gemfile:
# Gemfile
gem "thruster"
And run bundle install
:
...
Using rails 7.1.2
Using devise-otp 0.6.0
Using invisible_captcha 2.1.0
Using pay 7.1.1
Installing thruster 0.1.4 (arm64-darwin)
Bundle complete! 46 Gemfile dependencies, 171 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
...
Make sure you haven’t scoped Thruster to just development as this gem has to be available in production.
Since Thruster is written in Go, you’ll need to make sure the right architecture is used. In my Gemfile.lock
I have this:
diff --git a/Gemfile.lock b/Gemfile.lock
index c869d96..f0a2a76 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -422,6 +422,9 @@ GEM
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
thor (1.3.0)
+ thruster (0.1.4-aarch64-linux)
+ thruster (0.1.4-arm64-darwin)
+ thruster (0.1.4-x86_64-linux)
timeout (0.4.1)
turbo-rails (1.5.0)
actionpack (>= 6.0.0)
@@ -498,6 +501,7 @@ DEPENDENCIES
standard
stimulus-rails (>= 0.7.3)
stripe (~> 10.6.0)
+ thruster
turbo-rails (>= 0.9.0)
tzinfo-data
web-console (>= 4.1.0)
Configuration
Now that we have the gem in, we edit our Rails Dockerfile. The idea of Thruster is to just wrap Puma with the thrust
command:
# Dockerfile
...
# 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"]
We set HTTP_PORT
for Thruster to keep Traefik port 3000
as a default and TARGET_PORT
to Puma port.
This also means we now have to boot Puma on 3001
:
# config/puma.rb
...
if Rails.env.production?
port ENV.fetch("PORT", 3001)
end
...
And update position of the arguments in bin/docker-entrypoint
:
#!/bin/bash -e
# The position of ./bin/rails server now changed
if [ "${4}" == "./bin/rails" ] && [ "${5}" == "server" ]; then
./bin/rails db:prepare
fi
exec "${@}"
Thruster will handle all the requests for Puma from now on.
That’s it!
Get Test Driving Rails and make your tests faster and easier to maintain.