Want to deploy a Rails app to ‘the cloud?’ Here are 5 steps to get your app on Cloud Foundry. Although here I’ll deploy a JRuby on Rails app, deploying a Ruby on Rails app works just the same.
Step 0: Initialise Rails App
In this example, I’ll use postgres but Cloud Foundry supports MySQL and other popular databases too.
$ jruby -S rails new myApp -d postgresql
Step 1: Creating Orgs and Spaces
Login to your Cloud Foundry account. If this is your first time logging in, it asks you to create a new Org. Orgs and Spaces are Cloud Foundry jargon which seem confusing and superfluous initially. However, they are a neat way of managing environments. Having deployed to Cloud Foundry on a couple of projects now, here is what Orgs and Spaces mean to me:
- Org – Use this to name a cluster of applications that are logically grouped together.
- Space – Use each space as a separate environment for your application. An Org has multiple spaces, which serves as a convenient way to manage different environments for your applications.
My Cloud Foundry account has an Org called all-apps with a space called staging.
Step 2: Logging into Cloud Foundry from the console
$ cf login
When prompted for an API endpoint enter api.run.pivotal.io. If all goes well, you should see a success message like:
API endpoint: https://api.run.pivotal.io (API version: 2.0.0) User: <your_cloud_foundry_username> Org: all-apps Space: staging
Step 3: Prepping for deploy
Cloud Foundry is not tightly coupled with your version control system. While this can be a little extra work and doesn’t give you the benefit of automatically deploying every time you push to git, I’ve generally found it to be quite useful:
- I don’t have to worry about taking staging/demo instances down or the PM/customer stumbling upon an unfinished feature every time I push my changes to git. In general, since everything that gets pushed to git doesn’t get immediately deployed I find it encourages developers to push more often.
- Another advantage is having a .cfignore separate from .gitignore. This was helpful so I could upload API keys/credentials/anything-I-don’t-want-to-be-version-controlled only to Cloud Foundry. Similarly, by ignoring logs/, tests/, dev-tools/ only in .cfignore, uploading the app to CF is a much faster process.
- Finally, I often found myself making commits to git to tweak configuration or credentials to make my app work on Heroku. Deploying from my working directory allows me to directly applying these changes without necessarily checking them in to git.
- Here’s a sample .cfignore for myApp:
tmp/ log/ test/ .DS_Store
Step 4: Creating and binding to services
$ cf create-service elephantsql turtle elephantsql-myApp-staging
This creates an instance of the ElephantSQL service that may be used by any app in the staging space. If you are unsure of what services you want to use
cf marketplacelists all available services and plans. To bind to the service instance(s) that you have created, use the manifest.yml. An app manifest is a way to specify details about your application. While its not required to deploy, its an easy way to configure your app. A simple manifest.yml looks like:
applications: - name: myApp-staging memory: 1024M instances: 1 host: myApp-staging domain: cfapps.io path: ./tmp/deploy timeout: 120 command: bundle exec rake db:migrate && bundle exec rails s -p $PORT services: - elephantsql-myApp-staging
While most of it is standard, a few interesting things are the path, command and timeout. path represents the path from which the manifest deploys. Copying the contents of your working directory over to a ./tmp/deploy and pointing your manifest to deploy from there allows you to continue working while the app deploys. command is what cloud foundry runs to start your app. timeout is a way to tell cloud foundry about your app’s startup time. If you don’t specify a timeout, Cloud Foundry assumes a default of 1 minute. This is reasonable for most RoR apps, but JRuby needs a longer startup time.
Step 5: Deploying
The last thing you need to do is to allow your Rails app to serve static assets in production. In production.rb make sure you have an entry
config.serve_static_assets = true
myApp is now ready for deploying!
$ cf push myApp-staging
Cloud Foundry uses Heroku’s Ruby buildpack, which precompiles assets for your Rails apps and if you have a single app it is faster to precompile assets on Cloud Foundry rather than uploading precompiled assets. However, if you are pushing multiple apps, it is faster for you to precompile assets locally once and upload them.
To do so,
$ RAILS_ENV=production rake assets:precompilebefore pushing your app.
Do you see your robots.txt when you go to myapp-staging.cfapps.io? Success! :)
PS: At any point if you get unexpected results, some useful debugging commands are:
cf logs myApp,
cfto list all cf commands.