All notes → Rails – From Heroku to Cloud Run

Moving Rails to GCP Cloud Run

TL;DR

Intro

A couple of years ago i was working for a company which only wrote software in Ruby on Rails. It didn't matter if another language would have been the better tool to use, homogenous Rails development was the only priority.

I actually enjoyed my time there, the team was great and the project i maintained was quite fun to work on. What i didn't like was Ruby.

When i work for a company though i usually try to write my own software during that time in the same language. Just to become better in the language itself and to try something new; things i like i keep, things i don't like i get rid of. So that is why i have a couple of old projects laying around in Rails which i gradually going to rewrite properly.

Usually i like don't having to much to think of when running software in the clouds. That is why i chose Heroku for all Rails projects. Since Heroku has quite a good setup for running Ruby and Rails. Heroku though has become a non-viable solution for me in the recent weeks and months. So now i am moving all the Rails apps to the Google Cloud and let them run alongside my other stuff. That means setting up Cloud SQL and Cloud Run(for its simplicity) to run Rails apps.

The Setup

I always have stuff running in Docker setups. So i won't go over how to run Rails in a container. I won't either go into much detail about how to setup Cloud SQL itself; just a quick tip: if you want to import your SQL dump, you need to put it into a storage bucket from which Cloud SQL will access it.

Considerations

GCP Cloud Run provides some pre-defined environment variables, which can't be overriden and should be used when setting up your container image. For example: PORT is a pre-defined variable. It is by default set to 8080. If you create your image think about exposing/listening on the PORT env var.

You must activate that your Cloud Run service should use Cloud SQL in the Connections tab.

Rails itself logs preferred to files. You can change that behaviour by setting the environment variables RAILS_LOG_TO_STDOUT to enabled.

To allow that your "packs" are also served correctly also set RAILS_SERVER_STATIC_FILES to enabled.

Running a container in GCP Cloud Run

If you have your Dockerfile ready it is important that you build for linux/amd64 – just mentioning that since the new Apple SoCs (M1, ...) came around a few people have had trouble with the fact they can push there image to registries but are not able to run them anywhere – a quick PSA if you will. This can be done locally for example by running.

docker buildx build --platform linux/amd64 -t image-name:tag .

When pushing images to GCP you have several options. You can use the GCP source repositories and hook them up to a build pipeline which pushes the image to the registry. You can push your images manually (where i would recommend to give the image name a little thought; since for example if you prefix it with eu.gcr.io you can have it stored on the EU registry only).

Connecting to a Cloud SQL database with Rails

Cloud SQL requires the usage of a specific socket file name. This must be considered when configuring your app for production use.

...

production:
<<: *default
username: <%= ENV["DB_USER"] %>
password: <%= ENV["DB_PASSWORD"] %>
database: <%= ENV["DB_NAME"] %>
host: "<%= ENV.fetch("DB_SOCKET_DIR") { '/cloudsql' } %>/<%= ENV["INSTANCE_NAME"] %>"

This configuration requires the setting of the environment variables:

If you are a fan of the DATABASE_URL variable, i would recommend putting it into a GCP secret (via secret manager). In that form the configuration would look like the following. Keep in mind though that the auto-generated passwords for Cloud SQL user accounts can cause trouble when parsing such a DSN string.

postgresql://user:password@/db_name?host=/cloudsql/instance_name

Conclusion

As you can see, it takes some configuration but comparatively not much effort to run Rails in GCP Cloud Run.

One last note: Cloud Run allows you to scale down to 0 instance, this will cause a cold start problem. So if your application is crucial enough. Pay a little more and have one container always running.