labs

Deploying a Rails App to CloudFoundry

CloudFoundry has launched a private beta, and if you’re lucky enough to have access to it, you can get free hosting and services for the next 2 months.

I recently gained access to it and deployed a Rails application to it. The CloudFoundry documentation is still a work in progress, so I thought a blog post on deploying a Rails application to CloudFoundry might prove helpful to anyone attempting this.

Assuming you’ve got a private beta account, head to your nearest terminal and `cd` to your Rails app.

First, install the “cf” gem.

$ gem install cf

Unlike Heroku, CloudFoundry makes no assumptions about version control. You can push whatever you’ve got locally to CloudFoundry with `cf push`. This is great if you find yourself in the position where you’d like to push build artifacts to CloudFoundry that you’d rather not check into version control.

Since you’re deploying a Rails app, you’ll want to use an as-yet undocumented feature of CloudFoundry, the .cfignore file. Think of it like .gitignore for CloudFoundry. You’ll want to add tmp and log to it. At the moment, CloudFoundry doesn’t recognize that you’re attempting to push a rails app and automatically ignore tmp and log, and if you’ve got an application you’ve been developing for more than a few days, you may have some sizable logs that you’d prefer to avoid uploading.

$ cat > .cfignore
tmp/
log/

You’re now ready to push with the “cf push” command. It will ask you all kinds of questions, like what you want your domain name to be, how many instances you want to run, how big you want those instances to be, etc. You’ll have the chance to select services like postgres (via elephantsql).

When you run the `cf push` command, you can also specify the command you want to use to run your application:

$ cf push --command 'bundle exec rails s -p $PORT'

At the moment, there’s no way to run one-off commands like rake tasks other than through the startup command. For example, if you wanted to migrate and seed your database on deploy, you’d need to set the start command as follows:

$ cf push --command 'bundle exec rake db:migrate db:seed && bundle exec rails s -p $PORT'

When it’s done, it will ask you if you want to save your answers to a manifest.yml file. The cli defaults to “no” for this answer, but you’ll want to say “yes” and check in the manifest.yml to your version control. This way, it will remember all of your answers and automatically use your manifest.yml file next time you run “cf push”.

Note that if something goes wrong with your app, you can run the “cf logs” command to view your application logs.

If you want to run a worker like resque or sidekiq, you’ll need to create a new app instance on CloudFoundry, bind it to the same services as your current app, and tell it to start your workers on app boot. There’s a nice tutorial on setting up workers in the CloudFoundry docs here.

But basically, this is as simple as adding another application to your manifest.yml:

---
applications:
- name: cfdemo
  memory: 256M
  instances: 1
  url: cfdemo.cfapps.io
  command: bundle exec rake db:create db:migrate db:seed && bundle exec rails s -p $PORT
  path: .
  services:
    elephantsql-cfdemo-development:
      label: elephantsql
      provider: elephantsql
      version: n/a
      plan: turtle
- name: cfdemo-worker
  memory: 256M
  instances: 1
  url:
  path: .
  command: bundle exec someworkercommand
  services:
    elephantsql-cfdemo-development:
      label: elephantsql
      provider: elephantsql
      version: n/a
      plan: turtle

Now you have two applications in a single manifest, each bound to the same services. We’ve set the start command on our worker to “bundle exec someworkercommand”. Replace “someworkercommand” with sidekiq or resque or whatever worker system you’re using. Notice that we left the url for the worker blank (do this if there’s no endpoint in that instance that you want anyone accessing).

Good luck!