Render makes it quick to deploy a Ruby on Rails app, but if you're moving an existing Rails app over to Render, it might not be obvious where to start. We've created a language and framework-agnostic migration guide, but I thought it would help to provide a Rails-centric example.
This post will walk through migrating a non-trivial Ruby on Rails app to Render. To make it even more realistic, we'll use an open source, production Rails application running on Heroku: https://git-scm.org. This is the main informational site about the
On Heroku, an app is the parent for other resources – e.g., a Postgres database, a Redis instance, or multiple processes. Render doesn't have this hierarchical relationship (yet). You can deploy an independent managed Redis instance or a standalone PostgreSQL database if that's all you need. Instead, Render provides Blueprints (its implementation of Infrastructure-as-Code) to allow you to orchestrate multiple services. For example, here's how you might define and integrate a PostgreSQL database and a Web Service on Render with a Blueprint:
Deploying this Blueprint to Render will create a Web Service and a PostgreSQL database and ensure that the Web Service has the unique database connection string for
git
project. From it, you can download the latest version of git
, search the documentation, and learn everything and more about git
. It's a Rails app that uses PostgreSQL for data persistence, Redis for caching, Elasticsearch for fast site searches, and scheduled jobs to pull-in documentation from new git
releases. The code for the site is open source, and we can see that it's currently using Rails v6.11.
But first, why might you want to migrate your Rails app from Heroku to Render? Check out our comparison page. It explains all the benefits you'll get – like free private networking, HTTP/3, and free DDoS protection, among many other things.
Concept Mapping
Before going through the migration, let's map some Heroku concepts to Render concepts.Web Process (within a Heroku app) | |
---|---|
Worker Process (within a Heroku app) | |
Dyno | An instance of your service on Render |
Heroku Postgres | |
Heroku Redis | |
Heroku Scheduler | |
Config Vars | Environment Variables2 |
my-rails-db
. No copying and pasting necessary!
With that conceptual framing out of the way, let's walk through the process of deploying the git-scm Rails app to Render.
Deploy git-scm Code on Render
We will deploy the code using Render's native Ruby environment. Let's start building out arender.yaml
file that we'll place at the root of the repository. This file is the Blueprint that defines and integrates all the components of the working production application. If you want to follow along interactively with the rest of this blog post, fork the git-scm repository.
One important thing to note is that we could use the Render Dashboard to create and configure each component of this app. However, codifying the app's architecture in a render.yaml
reduces the chance of human error, reduces repetitive point-and-click configuration, and gives us an overview of the architecture in a single place.
Let's jump into creating our render.yaml
. Here's what we'll do.
- Create a Web Service
- Add a Database
- Add Redis
- Update Build Steps
- Add Bonsai Elasticsearch
- Add Cron Job
- DRY It Up
Create a Web Service
We'll begin by defining a Web Service for the Rails app in ourrender.yaml
.
Let’s make sure each line is clear.
name
is a name for our service to make it easy to find on the Render Dashboard. It's also used to generate an.onrender.com
URL.type
tells Render that we'd like to create a Web Service.env
specifies that we'd like to use Render's native Ruby environment. This environment includes OS packages that common Ruby libraries need in addition to Ruby and Rails specific environment variables.buildCommand
tells Render the command to run to pull in all the dependent libraries specified in theGemfile
. The default value isbundle install
, but you can modify it.startCommand
tells Render the command to run to start the Rails app.SECRET_KEY_BASE
is an environment variable that Rails requires, andgenerateValue: true
tells Render to generate a base64-encoded 256-bit secret for its value on the first deploy.
Add a Database
Now we need a PostgreSQL database for the app to write to or read from. Let's add that to ourrender.yaml
.
That was simple, but how do we connect this database to the Web Service?
The highlighted lines above create an environment variable whose value is the connection string for the database.
Add Redis
This Rails app uses Redis for caching, so let's add it. And similar to the database, we now need to tell the Rails Web Service how to access the Redis instance. The highlighted lines create an environment variable whose value is the connection string for the Redis instance.Update Build Steps
When Rails apps are deployed, a few extra commands are commonly run to precompile static assets and run a database migration. Heroku's Ruby buildpack handles these behind the scenes for you, but Render encourages being more transparent and gives you more control. Let's create abin/render-build.sh
file containing the following
And then we'll change the buildCommand
in the render.yaml
from bundle install
to bin/render-build.sh
.
Add Bonsai Elasticsearch
The git-scm.org site uses an Elasticsearch cluster managed by Bonsai to make the extensivegit
documentation quickly searchable. We can sign-up for a free sandbox Elasticsearch cluster and configure our Rails app to use that. The git-scm code expects to find a URL to access the Elasticsearch cluster in the BONSAI_URL
environment variable.
You might have expected to see the URL for our Elasticsearch cluster as the value of the BONSAI_URL
environment variable. However, we don't want to paste the URL there because it contains secrets that we don't want to save in a file in our repository, similar to a database connection string. Instead, sync: false
tells Render to ask us for the value of this environment variable during the first deploy of the Rails app.
Add Cron Job
This Rails app runs a few commands nightly to keep the site content up-to-date:bundle exec rake preindex
updates the site with the documentation from newgit
releases.bundle exec rake downloads
updates the links on the site to download the most recent version ofgit
.bundle exec rake remote_genbook2
pulls the Pro Git book into the site.bundle exec rake search_index
indexes the man page content in Elasticsearch.bundle exec rake search_index_book
indexes the Pro Git book content in Elasticsearch.
schedule
property and the GITHUB_API_TOKEN
environment variable. schedule
is a cron expression that defines when to run the job – in this case, we're running it every day at 3 am UTC. The GITHUB_API_TOKEN
is needed by the rake
tasks that the Cron Job runs because they download man pages and the Pro Git Book from a few different GitHub repositories.
DRY It Up
As I mentioned previously, there's some duplication in therender.yaml
between the Web Service and Cron Job. We can reduce some of this duplication using an Environment Group, which is a set of environment variables that can be maintained in one place and shared with multiple services. Let's move the SECRET_KEY_BASE
, GITHUB_API_TOKEN
, and BONSAI_URL
environment variables to an Environment Group.
Now we can remove the definition of those environment variables from the Web Service and Cron Job and add the following property to their envVars
objects.
We're now ready to deploy a fully working version of the git-scm.org site to Render! Here's a fork of the git-scm.org repository to which I've added our render.yaml
and build script. Initiating a Blueprint deploy on Render with this render.yaml
will deploy all the site's components – Web Service, Cron Job, PostgreSQL Database, and Redis – and connect them to each other. You'll need to create a Bonsai Elasticsearch cluster and add a value for the BONSAI_URL
environment variable for site search to work.
Background Worker
Another common component of Ruby on Rails apps is a background worker that executes outside of your app's HTTP request/response cycle. It is often used for things like sending emails or SMS messages, or generating invoice PDFs. The git-scm.org site doesn't use any background workers, but Render has native support for them. For example, here's a guide that explains how to use the Sidekiq job scheduler library with a Rails app on Render.Other Considerations
Here are some other tips for a successful Ruby on Rails deploy on Render:- Add a
.ruby-version
file to the root of your repository if you don't have one already. Render will use this to determine which version of Ruby to install when deploying your app. It should contain a single line with a Ruby version number. - You might want to copy the value of the
SECRET_KEY_BASE
environment variable from Heroku to Render so your users aren't logged out of your app after the migration. Doing this will allow any cookies your Heroku app set on user browsers to be readable by your app running on Render. - If you don't know all the components of your Ruby on Rails app on Heroku, looking in these files can give you some clues:
Procfile
,app.json
,heroku.yml
. You may not have these files, but if you do, they will help you understand the architecture of your app.
Ready to Try Render?
If you're thinking about migrating your Rails app from Heroku to Render, rest assured that there are many Rails apps running successfully on Render. Hopefully this post helps you understand the process. If you have questions not addressed here, please ask them on our user community or contact Render's awesome support engineers.Footnotes
- 6.0, 6.1, and 7.0 are being actively updated by the Rails core team as of today. Support for the previous version, Rails 5.2, will be discontinued in less than two weeks. ↩
- Render also provide Environment Variable Groups, which allow you to share a set of environment variables with multiple services. ↩