Migrating Ruby on Rails applications from Heroku to Openshift

I’ve been using Heroku quite happily for some time now, and I still think of it as of a good platform for development purposes or hosting small web applications. However, I have not used any paid features of Heroku, because the initial step from $0 to $35 for one dyno is a bit too much for my taste.

But fortunately, the IT world constantly evolves and from time to time something new pops up. And if it seems interesting, I wouldn’t be myself if I didn’t try it out.

Is it worth it?

To be precise, we’re talking here about the free version of OpenShift Online - I don’t yet need the Silver Plan with additional features and capabilities. I do not have anything against the Heroku cloud platform. It does exactly what it advertises. But the free version has many limitations which make it slightly unsuitable for any semi-production deployment (by which I mean deployment of a very small application with not too many visits, but which is expected to be online most of the time):

  • The free database tier allows you to store only 10000 rows.
  • If your application is not accessed during about 70 minutes (unless the timeout has changed recently), it is shut down, making the next request take even up to 10 seconds and effectively making many users abandon the visit.

OpenShift is free from this limitations and provides at least three features in addition:

  • SSH access (you may kill some kittens if you don’t know what you’re doing, but hey, it’s there!).
  • Port forwarding (yes, a simple rhc port-forward -a myapp allows you to e.g. connect to your database with any locally available tool).
  • Rather detailed control over deployment process with deployment hooks.

Migration

I do not plan to go here into details about the process of the features, of creating applications, adding cartridges etc. because, as it often happens, Red Hat provides very good documentation. I will, therefore, describe only the basic and essential steps. For anything not covered in this article, please refer to the aforementioned documentation.

So, assuming that you’ve signed up at https://www.openshift.com/, let’s create the application. At this point, you have two choices. Either you install rhc tools, or you create the application and add necessary cartridges through the website.

Let’s further assume that you have a more or less standard Ruby on Rails 3.x application (I plan to migrate to 4.x in a nearby future, and if I find changes to be significant the article will be updated) and you use PostgreSQL which is the default on Heroku.

If you chose rhc, you will need one command to create the application and the PostgreSQL cartridge. Here myapp is the name of your app, and ruby-1.9 and_postgresql-9.2_ are the cartridges (ruby-1.8 and postgresql-8.4 are also available if you need them):

rhc app create myapp ruby-1.9 postgresql-9.2

As a result, you will get an application in your default namespace. You will get all application details as a result of this command.

OpenShift creates a Git repository for you with some default files and settings matching your selected cartridges. In order to merge it with your existing application (in fact probably only config.ru will cause problems and you can just completely abandon the content created by OpenShift), cd to the Git repository of your application and add a new remote (you don’t have to, but it’s just convenient). Replace the URL with your own.

git remote add openshift ssh://...@myapp-mynamespace.rhcloud.com/~/git/myapp.git/

Then pull the changes. If you’re sure that you want to overwrite everything with the version from your original repository, execute:

git pull -Xours openshift master

Otherwise, execute:

git pull openshift master

and resolve any conflicts.

Modify the configuration

In order to have working migrations in the application on OpenShift, you need to add a hook that will execute them after the application is deployed. After pulling changes from the OpenShift repository you should have the .openshift folder and possibly the .openshift/action_hooks/deploy file (if not, create it). Make sure it contains the following code:

#!/bin/bash

pushd ${OPENSHIFT_REPO_DIR} > /dev/null
bundle exec rake db:migrate RAILS_ENV="production"
popd > /dev/null

If you’re interested in what pushd and popd do, the explanation is simple: pushd will put the current working directory on the directory stack and switch to the directory path in the ${OPENSHIFT_REPO_DIR} variable and popd will retrieve the directory from the stack and set it again as the current one. This is because we need to execute bundle in the application’s directory and then come back to the original directory, in order not to disrupt anything that may be executed afterwards.

We still need a correct database configuration for the production environment. Edit application’s config/database.yml and make sure that it configured in a similar fashion:

production:
  adapter: postgresql
  encoding: utf8
  pool: 5
  database: <%=ENV['OPENSHIFT_APP_NAME']%>
  username: <%=ENV['OPENSHIFT_POSTGRESQL_DB_USERNAME']%>
  password: <%=ENV['OPENSHIFT_POSTGRESQL_DB_PASSWORD']%>
  host:   <%=ENV['OPENSHIFT_POSTGRESQL_DB_HOST']%>
  port:   <%=ENV['OPENSHIFT_POSTGRESQL_DB_PORT']%>

If you would like to know the exact values of those variables (but do not specify those values directly in the database.yml - except for the negative impact on security, they just may change transparently at some point), connect via SSH to the application server:

rhc app ssh myapp

and execute this command:

env | grep POSTGRESQL

This will print out all environment variables containing POSTGRESQL in their names.

At this point you can commit your changes and push them to OpenShift with:

git push openshift master

This will take a while because the push will cause OpenShift to deploy the application. You can visit it at the assigned URL, but the database will still be empty.

Import the database from Heroku

Importing and exporting PostgreSQL databases on Heroku is well documented here. Shortly, we need to add the pgbackups addon, dump the database and download the file. So:

heroku addons:add pgbackups

Database dump:

heroku pgbackups:capture

Download the file:

curl -o latest.dump `heroku pgbackups:url`

Now, let’s import it to the PostgreSQL instance of your freshly created OpenShift application. For convenience, we will now use the port-forwarding capability I have mentioned in the beginning. Ensure that you have no locally running PostgreSQL instance and execute:

rhc port-forward myapp

You should get a similar output:

Checking available ports ...
done
Forwarding ports ...

To connect to a service running on OpenShift, use the Local address

Service    Local                OpenShift
---------- --------------- ---- --------------------
httpd      127.0.0.1:8080   =>  xx.xx.xx.xx:8080
postgresql 127.0.0.1:5432   =>  xx.xx.xx.xx:5432

Press CTRL-C to terminate port forwarding

Now you can connect to PostgreSQL instance as it was running on localhost. The following command will import previously exported data (you can manually verify the content of latest.dump before). Replace appusername with the automatically created username that your application uses to connect to the database (the one that was given to you when you created the application and the same as in the OPENSHIFT_POSTGRESQL_DB_USERNAME variable).

pg_restore --verbose --clean --no-acl --no-owner -h localhost -U appusername -d myapp latest.dump

And that should be all. Now visit your application, test it and verify if everything is alright. There’s a high chance that you’ve just successfully migrated your application to OpenShift.