Table of Contents
The Cedar-14 stack is the default runtime stack on Heroku and is the successor to Bamboo. It includes support for multiple languages, flexible process types and HTTP/1.1 and WebSockets. This article outlines the decisions and steps required to perform a manual migration from Bamboo to Cedar-14.
This article demonstrates how to move an app from Bamboo to Cedar-14. If you’re already on Cedar, but want to upgrade to Cedar-14, check out this article.
Here’s a rough outline of the steps required:
- Decide whether to upgrade Ruby version and virtual machine used by app
- Prep local workstation for Ruby development
- Update app to run on Cedar-14
- Change app stack to Cedar-14
Major changes to an application, including stack migrations, should always be executed in a staging environment first. It’s highly recommended that you try pushing your app to a temporary Heroku app to test the migration process and the new environment, so that you can make any changes necessary to run on the new stack without affecting your production environment. Once your code runs cleanly, you can update the stack and push changes to your production app.
Decide on Ruby version
Two flavors of Ruby were available on the Bamboo stack:
- 1.8.7 REE
- 1.9.2 MRI
Both these Ruby VMs have been EOL’ed by their maintainers (Phusion and Ruby Core, respectively). This means they are no longer receiving security updates and other critical maintenance. That makes Ruby 1.8.7 and 1.9.2 unsuited for use by production apps. When moving apps from Bamboo to Cedar-14, you highly recommend that you update to Ruby 2.0 or later. The guide below has details on how to perform this update.
Using a Ruby version that is EOL is dangerous and should not be done for production apps. EOL Ruby versions may contain known, exploitable security vulnerabilities that can expose app source code, secrets and user data. Production apps should use updated Ruby versions when running on Heroku.
If you choose to use an EOL version of Ruby when you migrate from Bamboo to Cedar-14, you cannot use the default Ruby buildpack because it does not support legacy Ruby versions. Instead, you must configure your app to use the correct legacy branch of the Ruby buildpack for the Ruby version you want to use.
For Ruby 1.8.7 use:
$ heroku buildpack:set https://github.com/heroku/heroku-buildpack-ruby#legacy-ruby-1.8.7
For Ruby 1.9.2 use:
$ heroku buildpack:set https://github.com/heroku/heroku-buildpack-ruby#legacy-ruby-1.9.2
Heroku will not update legacy branches of the Ruby buildpack. If you encounter problems or have suggestions for improvements to legacy buildpack versions, then please direct those to the fork in the Legacy Buildpacks GitHub Organization.
Getting app source code ready for Cedar-14
This section describes the changes you have to make to your app source code to make it ready to run on the Cedar-14 stack.
Get app source code
If, for any reason, you don’t have the source code for your Heroku app handy, you can fetch if from Heroku:
$ heroku git:clone --ssh-git -a app-name
Note that HTTP Git doesn’t work with apps on the Bamboo stack, so you need to use SSH git to clone.
Create git topic branch
Any changes made in preparation for migrating to Cedar should be stored in a separate
cedar topic branch for better code organization.
$ git checkout -b cedar Switched to a new branch 'cedar'
Commits made to this topic branch will stay independent from the production-ready
master branch which contains the Bamboo-compatible codebase.
Configure and update to new Ruby
Depending on what choice of Ruby version you made for running on Cedar, you must now specify your desired version. A number of Ruby versions are supported on Cedar. The default version is 2.0.0, but in order to specify a legacy version, you should add the
ruby declaration in your
Gemfile. For instance, to use Ruby 1.8.7:
source "https://rubygems.org" ruby "1.8.7" # ...
ruby declaration will only apply once the app is pushed to Cedar-14.
If you chose to upgrade from 1.8.7 or 1.9.2 to a newer version, you must now update your app to work on on the new Ruby version. Ruby versions are often not backwards compatible, so major changes may be required. Here are some resources to get you started:
- Upgrading From REE 1.8.7 to Ruby 1.9.3
- Lessons Learned Upgrading Harvest to Ruby 1.9.3
- How we migrated Beanstalk to Rails 4 and Ruby 2
Whatever Ruby version you specify in your Gemfile, you will likely need to have that Ruby version working on your local machine to run
bundle install and to debug your app. You can use ruby-install and chruby to manage multiple Ruby versions on your development machine. Other alternatives for managing multiple Ruby versions are rvm and rbenv.
It’s important to create a Gemfile to specify the dependencies of your app. You can also use this Gemfile to install the necessary gems locally.
If you’re already using Bundler to manage your gems on Bamboo then you won’t need to do anything new here. If you’re not, however, you’ll want to migrate to it and declare any gems that you need. Bamboo had pre-installed gems. On Cedar-14, all gems must be declared in the Gemfile.
The Procfile is new to Cedar-14 and specifies what processes Heroku should run for an application. Apps that don’t use background workers on Bamboo should create a new
Procfile at the application root with a single
web process type.
web: bundle exec puma -C config/puma.rb
The above example uses the Puma web server. This is recommended web server for production Ruby apps.
worker is only a suggested process type. Non-
web process types can have custom labels to more accurately reflect their role.
If the application is using Bamboo’s background worker support then a
worker process type should be specified in the
Procfile as well.
web: bundle exec puma -C config/puma.rb worker: bundle exec rake jobs:work
The Procfile is not a Heroku-specific manifest. It can be used to orchestrate an application’s dyno formation during local development as well with Foreman, installed as part of the Heroku Toolbelt.
Variables saved in the
.env file of a project directory will be added to the environment when run by Foreman. Set the
RACK_ENV to development in your environment. Do not commit the
.env file to source control it should only be used for local configuration.
$ echo "RACK_ENV=development" >>.env $ echo ".env" >> .gitignore
You can run your app locally using the environment variables in your
.env, the settings in your Procfile, and Foreman by running.
$ foreman start 10:01:59 web.1 | started with pid 3484 10:01:59 worker.1 | started with pid 3485
Once the application is running locally with Foreman commit the
Procfile to the topic branch.
$ git add Procfile $ git commit -a -m "Added Procfile to define app's process-formation on Heroku/Cedar"
If the application utilizes a Postgres database, then first ensure that you install Postgres locally, and then ensure that you specify the
pg gem in the
Gemfile, since it is not auto-injected on the Cedar-14 stack.
The database credentials will still be configured automatically: at slug compile time, a
config/database.yml that parses the
DATABASE_URL from the environment will be written into the app.
database.yml file is really an ERB file that is parsed at runtime to create the expected YAML-formatted file. To parse the file yourself run it through ERB before loading into YAML:
Bamboo auto-injected the New Relic plugin and auto-created the
config/newrelic.yml file. In Cedar-14, this is no longer the case.
If the application is using New Relic on Bamboo, manually add the
newrelic_rpm gem to the application
And create the
config/newrelic.yml file from New Relic’s template.
$ curl https://gist.githubusercontent.com/rwdaigle/2253296/raw/newrelic.yml > config/newrelic.yml
Further documentation and support for other frameworks can be found in the Sendgrid documentation.
Sendgrid add-on configuration is also no longer automatically provisioned on Cedar-14. Rails applications relying on Sendgrid to deliver emails will need to add a mail initializer at
$ curl https://gist.githubusercontent.com/rwdaigle/1690653/raw/mail.rb > config/initializers/mail.rb
If you are using memcache in your app you might be using a gem that requires c-bindings. The libraries to bind to memcache are not present by default on the Cedar-14 stack. To get around this you can use the Dalli gem or memcache-client both of which should work on all versions and implementations of Ruby and require no c-bindings. We recommend the Dalli gem, but it is not compatible with Rails 2.x. To change over you will need to add the new gem to your Gemfile.
If you were using the rails cache store you can configure Dalli to be used here by setting this value in your config/production.rb
config.cache_store = :dalli_store
If you are on Rails 2.x and have to use MemcacheClient first add it to your Gemfile.
gem 'memcache-client', :require => 'memcache'
Then you can set it to be your cache store:
config.cache_store = :mem_cache_store
If you were manually calling memcache clients you may need to replace calls from other libraries to call to
Dalli::Client for Dalli, or
MemCache for memcache-client.
At this point there may be several changes made to the application source to ready it for deployment to the Cedar stack, including modified gem dependencies.
Run bundler to install any newly required gems.
$ bundle install Fetching source index for https://rubygems.org/ Using rake (0.8.7) ...
Add any new files to git.
$ git add config/newrelic.yml
$ git commit -a -m "Modified application dependencies for Heroku/Cedar compatibility"
Create Cedar-14 app
Before you push directly to your bamboo application to change it’s stack, it’s recommended that you first test this out on a Cedar-14 app that is not getting production traffic. Naming the remote git repository
heroku-cedar will differentiate between the Bamboo and Cedar versions of the application.
$ heroku create --remote heroku-cedar cedar-app Creating cedar-app... done, stack is cedar-14 http://cedar-app.herokuapp.com/ | firstname.lastname@example.org:cedar-app.git Git remote heroku-cedar added
Deploy to your Cedar Staging App
Deploying the modified codebase to the Cedar application will not affect the currently running app on Bamboo. It will merely stage the application and provide the opportunity to verify functionality.
Push the Cedar-compatible source from the
cedar topic branch to the
master branch of the
heroku-cedar remote repository to deploy.
$ git push heroku-cedar cedar:master Counting objects: 67, done. ... -----> Heroku receiving push -----> Rails app detected ...
Once the app is deployed, you should verify that it is working correctly on the Cedar-14 stack, and if not, make any required changes.
Upgrading the production app to Cedar
Once you have verified that the updated source code works correctly on the Cedar staging app, you are ready to update the production app from Bamboo to Cedar-14.
First, set stack on the production app to Cedar:
$ heroku stack:set cedar-14 stack set, next release on oregon-dev will use cedar-14 Run `git push heroku master` to create a new release on cedar-14
This does not immediately change the runtime stack of the app, but the next release created will be deployed on Cedar. To create a release, merge the
cedar branch you have been working on, and push to your production app:
$ git checkout master $ git merge cedar $ git push heroku master
Your production app is now running on Cedar-14, and you should verify that everything is working as expected.
The migration is now complete.
Reverting to Bamboo
If the production app is not working after you changed the stack from Bamboo to Cedar-14, you can temporarily revert back to the Bamboo stack until you debug and resolve the issue. Set the stack to Bamboo and push the Git commit revision that was successfully running on Bamboo before you started the process of switching to Cedar-14:
$ heroku stack:set bamboo-ree-1.8.7 # Note: bamboo-mri-1.9.2 for 1.9.2 apps ... $ git push heroku <commit-id>:master
It should be noted that the system timezone on Bamboo dynos is
Pacific Time (US & Canada) whereas on Cedar-14 dynos it is
UTC. Any logic relying on non-normalized system time should be updated to account for this disparity.
On Bamboo, responses were automatically compressed using gzip. On Cedar-14 no such compression is done on your behalf. To add response compression to your application use middleware such as Rack::Deflater.
You should check that your Bamboo apps are configured to use the latest routing stack.