Deploying to Heroku

Deploy to your first production server

The short version

All it takes to deploy to industrial-grade infrastructure is, from a Terminal prompt, do the following:

  1. Sign into Heroku (only need to do once per workspace):

    heroku login
  2. Create your Heroku deployment target (replacing your-app-name with a name of your choice; only need to do once per workspace):

    heroku create your-app-name-production --remote=production
  3. Git commit your latest changes if you haven't already done so (do this each time you're ready to deploy a new version of the app):

    git add -A
    git commit -m "Write a commit message here"
  4. Deploy!

    git push production HEAD:master -f

The longer version

  1. Sign up for a Heroku account if you haven't already.

  2. At a Terminal prompt, run the command heroku login:

  3. To get the app ready to deploy to Heroku, ensure the following is true (this step should already be done if you started with our base application):

    • In your Gemfile, look for a line that says:

      gem "sqlite3"

      If you have it, it should either have :group => :development after it:

      gem "sqlite3", :group => :development

      or it should be within the :development group:

      group :development do
      gem "sqlite3"

      In addition, make sure you have the line:

      gem "pg", :group => :production

      or it could be within the :production group, like this:

      group :production do
      gem "pg"
    • In your Gemfile, make sure the rails_12factor gem is included in the production group:

      gem "rails_12factor", :group => :production

      or this:

      group :production do
      gem "pg"
      gem "rails_12factor"
    • If you made any changes to your Gemfile, then bundle install commit the changes to master.

    This step was likely already done for you, depending on which project you're working on.

  4. To create our deployment target on Heroku, run the command:

    heroku create your-app-name-production --remote=production

    replacing your-app-name with a name of your choice.

  5. To deploy, run the command:

    git push production master

    1. If you happen to be on a branch locally that is not master (perhaps because you were experimenting, or working on a feature branch), then you need to instead merge your changes into master or, more simply,

      git push production HEAD:master -f

      This command will overwrite the master branch on production with your current branch (whatever it is called). Be careful!

  6. What just happened? First, we set up another remote location besides GitHub that we can git push our code to, and named it production. That second remote location is on one of Heroku's servers, rather than one of GitHub's.

    GitHub's purpose in life is to safely store your Git repositories, and also give you a bunch of tools to discuss and collaborate on it. It's sort of like a super-powered Dropbox-for-code.

    Heroku's purpose in life is simple — to receive your code and run it! You'll notice that it's doing the same thing that you would have done to start up the rails server after cloning the project — bin/setup, bundle install, etc:

    Eventually, it will complete and tell you that it has deployed the application to a public URL, https://[YOUR APP NAME]

  7. Yay, our app is live on the internet! And not just a Cloud9 Preview. Barring any issues, all we've done so far is:

    heroku login
    heroku create your-app-name-production --remote=production
    git push production master
  8. As you continue to build the application, you simply commit your changes as usual at /git; when you are ready to deploy your changes, from the Terminal,

    git push production HEAD:master -f
  9. Some issues that you might run into:

    1. You will inevitably have errors occur on your production server, but the error messages there will not be as helpful as the ones we see on our development servers:

    They don't give you the gory details of what went wrong, which makes sense because our users shouldn't see all that.

    So then how do we know what went wrong so that we can fix it? We'll see some more advanced tools later, but your first and foremost debugging tool is always the server log. In order to see the log of what's happening on our Heroku server, run the command:

    heroku logs --tail

    This command will show us what's going over there in California:

    You'll notice that the production server log is not as helpful as the development log! What I do is clear the Terminal with Cmd+K, and then refresh the request that was causing the error. I then scroll to the top of the mess, and start to look through carefully for the error message.

    You will eventually see your error message in there if you dig through carefully, maybe something like this:

    ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR: relation "users" does not exist

    PG::UndefinedTable... 🤔 The issue is that, just like when we clone a new codebase to our own machine, we have to rails db:migrate to create the database! Right now, on Heroku, none of our migrations have been run.

    Ctrl+C to quit the Heroku server log and return to a Terminal prompt. To run commands on Heroku (rather than locally), we prefix them with heroku run:

    heroku run rails db:migrate

    Now if you visit your URL again, you'll see your live app!

    Note: When you try heroku run rails db:migrate, if you see the error message "Index name 'index_active_admin_comments_on_author_type_and_author_id' on table 'active_admin_comments' already exists":**

    Find the file in db/migrate that ends with ..._create_active_admin_comments.rb and comment out lines 11-13:

    Then run these commands in sequence:

    rails db:migrate
    git add -A
    git commit -m "Fix ActiveAdmin indexes"
    git push production master

    and, after it re-deploys the new codebase,

    heroku run rails db:migrate
    1. If you want to enter your rails console on your server in California, you can do so with:

      heroku run rails console

(Optional) Going further for a cutting edge workflow

It's amazing that it is that easy to deploy to an industrial grade production server nowadays. This ease of deployment also enables some very powerful modern workflows. Let's see one of them:

  1. Sometimes, you want to allow QA testers or product owners to exercise your new features before deploying them to your entire userbase. For this, it's very handy to have a second real server running that you can push your experimental code to. We usually refer to this as a "staging" server.

    It's going to be a lot of work to get our second server set up. Ready?

    heroku create your-app-name-staging --remote=staging
    git push HEAD:staging master -f

    Done! You now have two industrial grade servers running: one is live at https://[YOUR APP NAME], and the other at https://[YOUR APP NAME] But they don't each have to have the same version of the code at the same time.

    Usually, you will git push new commits to staging master first, and only once it has passed code review, QA, and been accepted by the Product Owner do you git push production master.

  2. This staging/master workflow is already quite robust, but we can now leverage our GitHub collaboration workflow to adopt an even better one: Review Apps.

    In your Heroku Dashboard, click on the button in the top-right corner and Create a New Pipeline:

    Choose a name for the pipeline (usually the same as before but without -production or -staging) and then locate our origin remote in the Connect to GitHub section. (Pay attention to the helper text regarding "Missing a GitHub organization?".)

    Then "Connect" and "Create Pipeline".

  3. In the next screen, you'll be asked to add servers to each "phase" of development — staging and production. We didn't choose our app names before lightly — add each one of your apps to the phase that we intended it for:

  4. Now for the good part: click on "Enable Review Apps...". Unless you've already got one, the first thing you'll likely see is a prompt to add a file called app.json to your repository:

    Fortunately, Heroku will write the file and commit it to our repo for us. Usually I accept all the defaults, except that I add bin/rails dummy:reset to the post-deploy section if I have such a script in my app.

    And then commit the file to our repo:

    Finally, back on your development machine, run the command git pull origin master (or click the button at /git) to get the changes that Heroku made to our GitHub repo on our behalf:

  5. Now, for the benefits: imagine that you are working with a team. You are assigned a task, and, as usual, you first create a branch and then start working away on it:

    Also as usual, when you are ready for feedback, you Push to GitHub and open a Pull Request so that your team can discuss:

    But wait! There's something new...

    In a moment, there will be a "View Deployment" link there that anyone can click on:

    What just happened? Once you've configured Review Apps, Heroku will spy on your GitHub repository. As soon as anyone opens a Pull Request, Heroku will automatically spin up another server and deploy the experimental branch to it.

    It then puts a handy link right there in the Pull Request conversation, so that teammates or product owners or QA can actually play around with the new features without having to know how to clone and start up a Rails server, or waste the time doing so. This makes it dramatically easier to give good feedback during the review process — and it sidesteps the common issue of multiple developers jostling for the lone staging server. Awesome!