Skip to content

Instantly share code, notes, and snippets.

@timshadel
Last active December 14, 2015 20:39
Show Gist options
  • Save timshadel/5145297 to your computer and use it in GitHub Desktop.
Save timshadel/5145297 to your computer and use it in GitHub Desktop.

Managing Multiple Environments on Heroku

Using Heroku to work with multiple environments can be pretty easy. The heroku tool can look at your git remotes to decide which app should receive the API commands.

Default Setup

The origin remote

You probably have just a single remote for your local repo: origin. It's the Github repo where the code lives. We won't be touching it. All the git push and git pull commands you use will continue to work against this repo.

$ cd myapp
$ git remote -v  # Look at your existing remotes
origin  https://github.com/ghuser/myapp.git (fetch)
origin  https://github.com/ghuser/myapp.git (push)

Typical deployments

It's pretty typical to have separate environments for production and staging or test, but over time you'll probably expand to more.

Using heroku --app

With this default setup, you have to specify which app you're working on with the -a, --app APP option:

$ heroku ps --app myapp-prod  # Type full app name: myapp-prod
=== web: `./node_modules/.bin/startup start --cluster`
web.1: up 2013/03/12 10:53:43 (~ 11m ago)
web.2: up 2013/03/12 10:53:43 (~ 11m ago)

$ heroku ps -a myapp-test  # Type full app name: myapp-test
=== web: `./node_modules/.bin/startup start --cluster`
web.1: up 2013/03/12 11:04:35 (~ 42s ago)

It doesn't take long for it to get tedious to type the app name in over and over. It's also confusing when you have many apps on Heroku and you tend to have a similar setup for each of them (which app am I in again?).

It'd be easier if you could always type prod when you're in the app's directory and have it just know which Herkou app you're talking about.

Connect to Heroku Apps

You can connect the codebase of your app to each of its deployments on Heroku. Remember, each environment has its own config settings, even if they run the same code.

Add prod and test remotes

To start with, let's add some remote references to prod and test as shortcuts. The Heroku app name is also it's git repo name.

$ git remote add prod [email protected]:myapp-prod.git  # Add standard deployments
$ git remote add test [email protected]:myapp-test.git

So now I have a remote prod connected to my app myapp-prod and a remote test connected to myapp-test.

Add personal deploy

It can be helpful to have a deployment for your own code. You can pick any name for it. Here I use mine.

$ git remote add mine [email protected]:myapp-timshadel.git  # Add personal deployment

Review remotes

Here's how you list all the remotes you have for a given codebase. Notice that both your source code management remotes (Github) and your app deployment remotes (Heroku) are mingled together in this list.

$ git remote -v  # Checkout the git setup
mine  [email protected]:myapp-timshadel.git (push)
mine  [email protected]:myapp-timshadel.git (fetch)
origin  https://github.com/ghuser/myapp.git (fetch)
origin  https://github.com/ghuser/myapp.git (push)
prod  [email protected]:myapp-prod.git (push)
prod  [email protected]:myapp-prod.git (fetch)
test  [email protected]:myapp-test.git (fetch)
test  [email protected]:myapp-test.git (push)

Using Heroku with remotes

Now that we've got setup the git remotes, let's use them with the heroku command via the -r, --remote REMOTE option. It's like using the -a, --app APP option, but instead it figures out the app name from your git remote.

From myapp

Here's what it's like to check on all the processes for each of your app deployments:

$ heroku ps -r prod  # Uses myapp-prod
=== web: `./node_modules/.bin/startup start --cluster`
web.1: up 2013/03/12 10:53:43 (~ 11m ago)
web.2: up 2013/03/12 10:53:43 (~ 11m ago)

$ heroku ps -r test  # Uses myapp-test
=== web: `./node_modules/.bin/startup start --cluster`
web.1: up 2013/03/12 11:04:35 (~ 42s ago)

$ heroku ps -r mine  # Uses myapp-timshadel
=== web: `node runner.js`
web.1: idle 2012/07/11 18:36:31

From other apps

Once you start using this pattern, the commands you type don't change when you work with another app. Here's anotherapp:

$ cd anotherapp

$ heroku ps -r prod  # Uses anotherapp-prod
=== web: `./node_modules/.bin/startup start --cluster`
web.1: up 2013/03/12 09:53:43 (~ 1h ago)
web.2: up 2013/03/12 09:53:43 (~ 1h ago)

$ heroku ps -r test  # Uses anotherapp-test
=== web: `./node_modules/.bin/startup start --cluster`
web.1: up 2013/02/12 11:04:35 (~ 1 month ago)

$ heroku ps -r mine  # Uses anotherapp-timshadel
=== web: `node runner.js`
web.1: idle 2012/09/11 18:36:31

Wrap up

The big benefit from this pattern is how easily you can work with a large number of apps while letting your muscle memory keep the same keystrokes no matter which app you use.

The -r, --remote REMOTE option is global, so every heroku subcommand, including those installed by plugins, will work with it.

Happy DevOps-ing!

Bonus Round

Making a new Heroku app

This is great if you or someone on your team has already created your apps in Heroku, but how do you create an app and have it set the remote right from the get-go? Easy. Just use the -r, --remote REMOTE option with heroku create.

$ heroku create myapp-stage -r stage  # Add a staging deployment
Creating myapp-stage... done, stack is cedar
http://myapp-stage.herokuapp.com/ | [email protected]:myapp-stage.git
Git remote stage added

$ git remote -v  # Check that the git remote was named properly
origin  https://github.com/ghuser/myapp.git (push)
origin  https://github.com/ghuser/myapp.git (fetch)
prod  [email protected]:myapp-prod.git (fetch)
prod  [email protected]:myapp-prod.git (push)
stage [email protected]:myapp-stage.git (push)
stage [email protected]:myapp-stage.git (fetch)

That's all there is to it.

Cleaning up git remotes

Sometimes your git remotes aren't in perfect shape, and you'd like to clean them up. Let's go through a few common scenarios and how to fix them.

A single heroku remote

If you used heroku create originally, with the default arguments, then your initial remotes may look something like this:

$ git remote -v  # Initial git setup after `heroku create`
origin  https://github.com/ghuser/myapp.git (fetch)
origin  https://github.com/ghuser/myapp.git (push)
heroku  [email protected]:myapp.git (push)
heroku  [email protected]:myapp.git (fetch)

There are two things going on here. First, the remote name is simply heroku, and second, the app name is just myapp. Since the main issue is simply how to easily refer to the prod version consistently no matter which app you're working with, we'll rename it.

$ git remote rename heroku prod  # Rename the remote

$ git remote -v  # Check out our setup now
origin  https://github.com/ghuser/myapp.git (fetch)
origin  https://github.com/ghuser/myapp.git (push)
prod  [email protected]:myapp.git (push)
prod  [email protected]:myapp.git (fetch)

Notice that the only thing that was changed was the remote name. Now you can go ahead with the recipe above and setup your other remotes.

Changing app names

After you have a few Heroku apps, and you start to have a few typical environments, it's common to start a naming convention like we showed earlier. Perhaps you started with a mixture of -staging and -test, and now you simply want everything to be -stage.

$ git remote -v  # Look at non-standardized setup
origin  https://github.com/ghuser/myapp.git (fetch)
origin  https://github.com/ghuser/myapp.git (push)
prod  [email protected]:myapp-prod.git (push)
prod  [email protected]:myapp-prod.git (fetch)
test  [email protected]:myapp-staging.git (fetch)
test  [email protected]:myapp-staging.git (push)

There's two ways to approach this.

Change git remote url

If someone on your team has changed the Heroku app name that you're using for your stage environment, you can fix it up in your local git repo. Here we're changing Heroku app names from myapp-staging to myapp-test, but we're keeping the git remote name of test.

$ git remote set-url test [email protected]:myapp-test.git  # Rename the remote

$ git remote -v  # Check out our setup now
origin  https://github.com/ghuser/myapp.git (fetch)
origin  https://github.com/ghuser/myapp.git (push)
prod  [email protected]:myapp-prod.git (push)
prod  [email protected]:myapp-prod.git (fetch)
test  [email protected]:myapp-test.git (fetch)
test  [email protected]:myapp-test.git (push)

Create a new app, destroy old app

This is great if you're the guy on the team to make this change. You need to make a new Heroku app, with your desired name, and then copy over the config and code, then remove the old app.

Here we'll create a new Heroku app from the command-line with the -r, --remote REMOTE option.

Create the new app

$ heroku create myapp-stage -r stage  # Create the new app
Creating myapp-stage... done, stack is cedar
http://myapp-stage.herokuapp.com/ | [email protected]:myapp-stage.git
Git remote stage added

$ git remote -v  # Check that the git remote was named properly
origin  https://github.com/ghuser/myapp.git (push)
origin  https://github.com/ghuser/myapp.git (fetch)
prod  [email protected]:myapp-prod.git (fetch)
prod  [email protected]:myapp-prod.git (push)
stage [email protected]:myapp-stage.git (push)
stage [email protected]:myapp-stage.git (fetch)
test  [email protected]:myapp-test.git (fetch)
test  [email protected]:myapp-test.git (push)

Copy the config

The easiest way to copy the config is to use the heroku-config plugin.

$ heroku plugins:install git://github.com/ddollar/heroku-config.git  # Install heroku-config plugin
heroku-config installed

$ rm -f .env  # Pull the `test` config down
$ heroku config:pull -r test
Config for myapp-test written to .env

$ heroku config:push -r stage  # Push it up to `stage`
Config in .env written to myapp-stage

Push the code

$ git remote update test  # Make sure you've got the latest `test` code
Fetching test

$ git checkout -b migrate test/master  # Get a branch with `test` code
Switched to a new branch 'migrate'

$ git push stage migrate:master  # Deploy the code to `stage`
...(normal Heroku deploy output)...

Destroy the old app

$ heroku destroy --app myapp-test  # Destroy the old app. This is one place -r doesn't work.

 !    WARNING: Potentially Destructive Action
 !    This command will destroy myapp-test (including all add-ons).
 !    To proceed, type "myapp-test" or re-run this command with --confirm myapp-test

> myapp-test  # Here you manually type in the app name.
Destroying myapp-test (including all add-ons)... done

$ git remote -v  # And check to see that all is well.
origin  https://github.com/ghuser/myapp.git (push)
origin  https://github.com/ghuser/myapp.git (fetch)
prod  [email protected]:myapp-prod.git (fetch)
prod  [email protected]:myapp-prod.git (push)
stage [email protected]:myapp-stage.git (push)
stage [email protected]:myapp-stage.git (fetch)

And you're done!

@lynns
Copy link

lynns commented Mar 14, 2013

I've also found it useful at times to open up a bash prompt to my heroku app so I can see what files are really there and how things got deployed. It's pretty simple as well:

$ heroku run bash

That should give you a bash prompt to your heroku app. Be aware that heroku actually starts-up a new instance of your app and then gives you a prompt to that new instance. You can't actually touch your running web process using this method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment