Rails on App Engine: Tips and Tricks

At last year’s Rails Conference we announced the beta of Google App Engine for Ruby. Today we announced that App Engine Flex has gone GA. As we have gotten closer to GA, I’ve built a few non-trivial Rails applications and deployed them to GAE. I’ve also given some one-on-one help to others deploying to App Engine or Google Cloud for the first time. In this process, we’ve found and fixed a few bugs, and I’ve built a list of common things that trip people up on their first deploys.

Basic Architecture and Gems Used

I’ve worked on two main sites during the last six months. One is for a local non-profit. The other, Tind-Purr is for a demo and my amusement. Both applications are using Ruby 2.3 and Rails 5. Both applications have a MySQL database. Tind-Purr uses SQLite for Dev and Test and Cloud SQL in production. The non-profit wants to maintain their own database for production but uses Cloud SQL for Staging.

User uploaded images are central to both websites. I’ve used Paperclip to support file uploads with both apps. I chose Paperclip because I had used it before. Tind-Purr stores images in Google Cloud Storage using Fog and the Fog-Google gem.

Tind-Purr has some simple background task processing. I am using ActiveJob with Google Cloud Pub/Sub as a backend. The background task is designed to feed a queue of “pictures that need votes” for the frontend, so it needs to run after every upload and on a regular interval as well.

The non-profit website required authentication, roles, and an admin interface. Authentication was done with Devise again because I had used it before. Roles and authorization are implemented with CanCanCan, the successor to CanCan. RailsAdmin is my go-to for quick and dirty admin interfaces. I also needed to support payments and did that with Stripe.

On the frontend, the non-profit requested that I use Bootstrap. I used Materialize for Tind-Purr. In both cases, I used SimpleForm because CSS is not my strength.

Common Gotchas

To use App Engine, you will need two things: A Google Cloud account and the Google Cloud SDK. Once you’ve taken care of these two pieces, you can navigate to your app directory on your dev machine and type gcloud app deploy to start your first deployment. gcloud will analyze your directory structure and ask you to confirm that you have a Rails application. Then it will write out a file called app.yaml. that configures the App Engine settings for your application.

Database Creation and Migrations

For folks who are used to other cloud providers, the way Google handles database tasks is a bit different. To run any of the rake db commands for production, you must connect your dev environment to the production database by doing RAILS_ENV=production rake db:[create|migrate|seed]. This means you have to do the database creation and any necessary migrations before you do gcloud app deploy. You also must have config/database.yml set up to connect to production on the machine you are using for deployment.

There are several database options available on Google Cloud. More information about those options and how to connect to Rails is available as part of our Ruby Tutorial.

Environment Variables

The most common issue I’ve seen with App Engine Rails deployments is people who forget or don’t know how to set SECRET_KEY_BASE and other environment variables. If you look in your config/secrets.yml you will see something that looks like this:

# secrets.yml

development:
  secret_key_base: 2b8e0636d75c...

test:
  secret_key_base: d9d3ba88a59a...

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

You can set environment variables in app.yaml. Here’s the app.yml I use for Tind-Purr.

# app.yaml

entrypoint: bundle exec rackup -p $PORT
env: flex
runtime: ruby
health_check:
  check_interval_sec: 10
env_variables:
  SECRET_KEY_BASE: 'your_key_goes_here'

This is also where you can put things like API keys for third-party services and Rails configurations like RAILS_ENV and RACK_ENV. You can use different app.yaml files for different environments if you choose. Just specify the file when you deploy: gcloud app deploy staging.yaml.

Assets

The last big thing that I often forget is to do my asset compilation locally before I deploy to app engine. RAILS_ENV=production rake assets:precompile takes care of it.

Advantages

App Engine is a different way of running Rails Apps for me. I am used to writing deployment scripts and running them within some CI/CD pipeline. Alternatively, for little apps I’m used to just cloning the code and starting Rails. For the app I did for a non-profit, App Engine let me get my early versions up in front of customers quickly and also let me stage changes for approval when they had bugs to fix.

Debugging

For debugging my App Engine deployments, I included the stackdriver gem. This is a drop-in gem that automatically pushes Rails logs to Stackdriver Logging. Since the logs are in Stackdriver Logging, you get monitoring from Stackdriver Error Reporting automatically. Also, the Stackdriver gem hooks your Rails App into Stackdriver Trace as well which can be useful when debugging performance issues.

Auto-scaling

When I got Tind-Purr spun up for the first time I sent out an email to my team requesting folks upload their cat pictures. Watching App Engine automatically scale up as more folks came online was almost magical.

I also really appreciated that the deployment was pretty easy. Once the database and assets were taken care of all I had to do was type gcloud app deploy and up it went. When I have an update, I just deploy again and traffic is automatically routed to the new version once it is healthy.

Learn More

If you want to learn more about running Rails on App Engine, or on Google Cloud in general check out this guide.