Use Puma to deploy your Rails APP with SSL

Background

We at peaq develop several Rails applications. Some of them are installed on premise on a customer server. Although such applications are not publicly accessible, you want to secure their connections by SSL though.

The typical recommendation for deploying Rails-Apps says, you should set up NGINX or Apache to serve the assets and use those web servers as reverse proxies for routing all dynamic requests to the Rails application-server (like Unicorn or Puma). But  typically such private applications do not have heavy load and it would be overkill to deploy them with this approach. It would be handy to have an application server, which handles SSL directly. The good answer: It is possible with Puma!

Configure your Rails-App to use Puma

First you need to configure Puma to be your application  server. There’s a good chance, that you can skip this step as Puma is the default application server since Rails 5. But for all which are still using Rails 4, add the following line to your Gemfile:

gem 'puma'

Create your SSL certificates

Once Puma is your default application server, it is time to create the SSL certificates. We are using openssl for this. There are 5 steps, you need to execute:
Step 1: Generate your private key

openssl genrsa -des3 -passout pass:x -out server.pass.key 2048

Step 2: Remove passphrase from key

openssl rsa -passin pass:x -in server.pass.key -out server.key

Step 3: Generate a CSR (Certificate Signing Request)

openssl req -new -key server.key -out server.csr

Step 4: Generating a Self-Signed Certificate

openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt

Step 5: Copy the CSR- and KEY-Files to your config directory

cp server.crt server.key /path_to_your/rails_app/config

Test: Start your Rails-App with SSL

Once Puma is your application server, simply start the server with the following options, and you have SSL connection to your Rails App:

puma -b 'ssl://127.0.0.1:3000?key=/path_to_your/rails_app/config/server.key&cert=/path_to_your/rails_app/config/server.crt'

Configure Puma

If the test is successful, Puma could be configured with a config file called config/puma.rb. The advantage of doing it this way is, that you don’t need to remember all the SSL-options for starting your server. Here is a working puma.rb configured for SSL:

# Config Parameters
app_root = ENV['APP_ROOT'] || '.'
rails_env = ENV['RAILS_ENV'] || 'production'
ip_addr = ENV['IP_ADDR'] || 'localhost'
port = ENV['PORT'] || 3000
ssl_key = ENV['SSL_KEY']
ssl_cert = ENV['SSL_CERT']

# Set Puma parameters
preload_app!
rackup DefaultRackup
environment rails_env
pidfile "#{app_root}/tmp/pids/puma.pid"
state_path "#{app_root}/tmp/pids/puma.state"
threads 0, 16
workers 2
stdout_redirect "#{app_root}/log/puma.log", "#{app_root}/log/puma.err", true if rails_env.downcase == 'production'

# HTTP or HTTPS
if ssl_key.present? && ssl_cert.present?
  bind "ssl://#{ip_addr}:#{port}?key=#{ssl_key}&cert=#{ssl_cert}"
else
  bind "tcp://#{ip_addr}:#{port}"
end