Serverless Rails Applications

The fly.io Rails Machine API page has been updated to demonstrate serverless deployments. For more information on serverless see The Serverless Server.

What does this mean?

While serverless is a bit of a misnomer, what it means in practice is that when an app is not in use, no RAM or CPU is allocated to the app. This is also sometimes called scale to zero. The requirements for deploying an application on fly.io and having it scale to zero and restart when next accessed are deceptively simple:

  1. Run on machines platform, not nomad.
  2. Application needs to exit when idle.

I say deceptively simple as the machines platform has differences in deployment, and application servers generally aren’t set up to exit. Except for Phusion Passenger. The following three lines of configuration are all it takes to have your rails app scale to zero and to invoke another script to clean up other resources:

passenger_ctl hook_detached_process /etc/nginx/hook_detached_process;
passenger_min_instances 0;
passenger_pool_idle_time 300;

And here is the hook itself:

#!/usr/bin/env ruby

status = `passenger-status`

processes = status[/^Processes\s*:\s*(\d*)/, 1].to_i

system 'nginx -s stop' if processes == 0

That’s all it takes! No application code is required. Restarting your application is noticeable (I am seeing times of 2 to 3 seconds), but tolerable.

Pulling on a string

fly launch is great, if all your needs are met by an optional PostgreSQL database. But if you want sqlite3 you have to run a few commands and modify a few configuration files. Sidekiq? Repeat the process. Redis? Ditto. Litefs, passenger, serverless, …

Pretty soon your whole day is shot and you have yet to deploy your first application.

What’s worse is that getting your application up and running is oftentimes like pulling on a string. You may not care about what web server is used beyond knowing that passenger can scale to zero, but installing passenger means you need to install and configure nginx. And the nginx configuration needs to be aware of action cable if you are using that. And action cable needs a patch so that it doesn’t take down the server when upstart redis times out.

Take a look at the current set of templates the fly.io-rails gem currently supports. Open a random one and look for if statements.

The fly:app generator intends to take Rail’s Convention over Configuration and apply it to fly.io for Rails apps.

Where do we go from here?

Obviously as other web servers add support for scale to zero, support will be added.

Another example of pulling on a string is that use of action cable will defeat the purpose of scale to zero as clients are always connected. AnyCable can be used to address this, but requires more configuration. Ideally that can be reduced to a simple --anycable option when generating a fly:app.

Scale to zero is great, but how about scale to two (or more)? What is great about serverless is that you don’t need to skimp on CPU or RAM - give your application what it needs to fly when it needs it, and release these resources when idle. That being said, there are limits. Rails is RAM hungry, but if your application uses puppeteer or equivalent to generate reports, then you need to make room for Chrome which is a beast on its own. Perhaps it is best to put that beast on its own machine and spin it up when needed. Fortunately, that is easy to do. And puppeteer has its own configuration requirements. Scanning for “puppeteer” in both the Gemfile and package.json and adding an if statement to the Docker.erb can take care of this for all future applications.

Along the way, I’ll be experimenting with starting and restarting companion machines during the release processing step and using terraform, and the generator should be able to handle both cases.

Now there undoubtedly are plenty of things I haven’t thought of yet. And plenty of things other people use that I might not. See something missing? Open issues and pull requests on fly.io-rails! And let me know if you have something working, once I’m convinced that I am not the only person using this gem I’ll move the repository over to superfly.

1 Like