Deploying to Heroku

  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:

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

       gem "sqlite3"
      

      If you have it, it should be modified to:

       gem "sqlite3", :group => :development
      

      or

       group :development do
         gem "sqlite3"
       end
      

      and, in addition, add the line

       gem "pg", "~> 0.18", :group => :production
      

      or

       group :production do
         gem "pg", "~> 0.18"
       end
      

      This step may already be done for you, depending on which project you’re working on. We’re now ready to use the powerful database that Heroku provides1.

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

       gem "rails_12factor", :group => :production
      

      If you don’t have it, add it. Alternatively, you could have something like this2:

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

  4. Run the command:

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

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

  5. Run the command:

     git push production master
    

  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 servers4, rather than one of GitHub’s5.

    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]-production.herokuapp.com/6:

  7. If you visit the URL that Heroku gave you, you will likely see an error:

    The first thing to notice is that error messages look different in production mode. 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 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 create7 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, there are a couple of common issues at this point:

    • If you see the error message “Directly inheriting from ActiveRecord::Migration is not supported. Please specify the Rails release the migration was written for”:

    Some of your migrations are using an old Rails 4 style. These all need to be updated. Basically, just go through each file in db/migrate and ensure that the first line has a [5.0] on the end, like this:

      class CreateVenues < ActiveRecord::Migration[5.0]
    

    Then run these commands in sequence:

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

    and, after it re-deploys the new codebase,

      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
    
  8. 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 create your-app-name-production --remote=production
     git push production master
    

    As you continue to build the application, you simply commit your changes as usual and git push production master whenever you are ready to deploy a new version.

    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:

  9. 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 staging master
    

    Done! You now have two industrial grade servers running: one is live at https://[YOUR APP NAME]-production.herokuapp.com, and the other at https://[YOUR APP NAME]-staging.herokuapp.com. 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.

  10. 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”.

  1. 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:

  2. 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 dev:prime 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, don’t forget to git pull to get the changes that Heroku made on our behalf:

  3. 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!

  1. Heroku gives us a very powerful open-source database called Postgres. By default, a brand new Rails application uses a lightweight database called SQLite since it is already installed on basically every device that exists. We have to ensure that we make our app compatible with Postgres. Fortunately, since ActiveRecord handles translating our Ruby into SQL for us anyway, this requires no change to our application code. We simply need to switch to a different adapter when Heroku starts up the rails server in production mode (as opposed to development mode, which is what we do on our own machine). 

  2. Any gems that are listed within the :production group will only be used when the rails server is started up in production mode. Usually, we start our server in the default mode, which is development. If you really wanted to, you could also restrict other gems to the :development group if they aren’t used while serving actual requests; for example, starter_generators. This will result in a (small) performance boost. 

  3. If the name you chose is taken, don’t worry about it. Just choose something else that is unique. Ultimately you will hide these identifiers behind your own custom domain name anyway. 

  4. Technically, it’s one of Amazon’s AWS servers, which Heroku rents and manages on our behalf. 

  5. Our first remote is named origin, by default. This is the remote that rules all the others: the one that we push our code to in order to collaborate with our team and hammer out the final version of the codebase. Nowadays, almost every team uses GitHub for this. 

  6. This idea of deploying an app via a simple git push was incredibly revolutionary. Heroku autodetects that it’s a Rails codebase and does a ton of work on our behalf to deploy it to an Amazon AWS server in production mode. Magical! 

  7. You might be wondering why the database doesn’t go to Heroku and GitHub along with all of the other code when we git push. There are lots of reasons, but for one, it would be a terrible idea to be using the same database while we’re messing around developing as the one that our actual users are storing their precious data on. Other reasons include size and data security.