Deploying to Heroku
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 -Agit commit -m "Write a commit message here"
- 4.Deploy!git push production HEAD:master -f
- 1.
- 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 => :developmentor it should be within the:development
group:group :development dogem "sqlite3"endIn addition, make sure you have the line:gem "pg", :group => :productionor it could be within the:production
group, like this:group :production dogem "pg"end - In your
Gemfile
, make sure therails_12factor
gem is included in the production group:gem "rails_12factor", :group => :productionor this:group :production dogem "pg"gem "rails_12factor"end - If you made any changes to your
Gemfile
, thenbundle install
commit the changes tomaster
.
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=productionreplacing
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 intomaster
or, more simply,git push production HEAD:master -fThis command will overwrite themaster
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 itproduction
. 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 therails 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/
: - 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 loginheroku create your-app-name-production --remote=productiongit 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 --tailThis 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 existPG::UndefinedTable
... 🤔 The issue is that, just like when we clone a new codebase to our own machine, we have torails 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 withheroku run
:heroku run rails db:migrateNow if you visit your URL again, you'll see your live app!Note: When you tryheroku 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 indb/migrate
that ends with..._create_active_admin_comments.rb
and comment out lines 11-13:Then run these commands in sequence:rails db:migrategit add -Agit commit -m "Fix ActiveAdmin indexes"git push production masterand, 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
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=staginggit push HEAD:staging master -fDone! You now have two industrial grade servers running: one is live at
https://[YOUR APP NAME]-production.herokuapp.com
, and the other athttps://[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 willgit push
new commits tostaging master
first, and only once it has passed code review, QA, and been accepted by the Product Owner do yougit 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 ourorigin
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 addbin/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 commandgit 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: