|||

Video Transcript

X

From Project to Productionized with Python

We hope that you and your loved ones are staying safe from the COVID-19 pandemic. As a result of its effect on large gatherings, PyCon 2020 was cancelled changed to an online event. Although not being able to gather in person was disheartening for organizers, speakers, and attendees, the Python community shared virtual high-fives and hugs with PyCon US 2020 Online. We recorded our planned Heroku workshop for the event, on which this blog post is based.

Casey Faist: Hi, I'm Casey Faist the queen Pythonista at Heroku and this is From Project to Productionized on Heroku. Now, I wish we could be together today. I wish we could be swapping stories and coding together. I wish we could hear the groans from my deployable jokes. Now this is intended to be a hands-on workshop, so while video format presents some challenges for that, the good thing is that if at any point you need me to slow down or you want to go off on a rabbit hole, just pause the video. I'll be waiting for you right here. Now, this is intended as a hands-on workshop, so if you're going to follow along at home, first of all, high five, yeah. Second of all, we assume a couple things about your experience and there are a couple of prerequisites necessary for a good experience with this workshop. The first thing is that this is not a Django tutorial.

Casey Faist: If you're looking for intro to Django information, go to the official Django docs where they have some excellent tutorial information. Next, we assume a little bit of Git familiarity, you need Git installed on your machine that you're working on. We provide a starter repo for you to work along with that has instructions but it's available on GitHub, so you'll need to be able to Git clone that repo. To complete this workshop, you'll need four things. You will need to sign up for a Heroku account. This is free, you don't need to add a credit card. You will need to download the Heroku CLI. You will need to clone the workshop repo or use a project of your own, and you will need to be able to open that repo in a text editor or IDE of your choice. All right, have all that ready to go? Awesome. Let's get to it.

Casey Faist: So let me set the scene. You have an application, you've poured your blood, sweat and tears into this application. It's wonderful and it's finally ready. It's ready to go out there and be on the Internet. How do you do that? Luckily with Heroku, you've already done the hard part. See, Heroku is a platform-as a-service and unlike infrastructure-as-a-service models, there's a bunch of stuff that you just don't have to do. Stuff like infrastructure maintenance or building deploy pipelines, setting up tools to manage user access or system level security updates. You don't have to do it. It's not your problem. That is what Heroku is there to do. We're managing those security updates, providing tooling, all of those, plus metrics and more to make your life as a developer easier. It's a huge time save but there's a catch. A 12 factored catch.

Speaker 2: Boo.

Casey Faist: See Heroku works best with 12 factor applications. This is something that Heroku championed more than 10 years ago. It's the idea that you build an application with robust redeploys in mind. Things like config variables, using processes and more. If you'd like to learn more, you can check out the link here. But most of this workshop is actually not specific to Heroku. A lot of what we're going to do today is about taking a Django application and making it 12 factored. There's a lot of benefits to using 12 factor applications whether or not you're deploying to Heroku but it's especially important on Heroku because of something called ephemeral deploys. Ephemeral deploys or ephemeral environments are designed to spin up or down at a moment's notice with very little downtime but this means that you can't persist anything to the local file system.

Casey Faist: It either needs to be checked into Git, stored in a database or as an environment variable or in an attached resource like an S3 bucket that does persist. Now if you've never worked with 12 factor applications before, don't worry. We're going to do 10 steps together to take your application From Project to Productionized on Heroku. Once you've opened the project in the text editor of your choice, take a second to look around. Your editor and your terminal should be in the root directory of the Pycon 2020 project. So I type ls, you should see manage.py, requirements.txt, and workshop.md. You'll also see two Django projects. The first is hello, we won't do anything to this one, so don't worry about it but the other one is called getting started. We'll make a couple of changes to this one including updates to settings.py and the WSGI file. But for now, bring up the workshop.md file. Our first step is to update the gitignore file, copy the text here in the code block, then navigate to the gitignore file in the root of your project. Highlight and copy and paste.

Casey Faist: If you're working off of the raw markdown file, you want to make sure you don't transfer any quotes or back ticks as they'll mess with how this file works. I created a virtual env already, up here it's called venv, so I don't need to add this line, I can remove it. I don't want any pycache files stored. I also don't want to add any SQLite databases or sequel SQLite artifacts to source control. Any local data that I'm using for testing or to ensure my application works that should stay locally on my machine and not get pushed to Git. However important note, you absolutely do want to add your Django migrations to source control, so make sure that you do not add any of your Django migrations to this gitignore file. Okay. And now the last step, this app is not called your app, it's called, we're actually looking for getting started here. This is where our static files will generate. Heroku will automatically generate our static files for us, but we don't want to check our local ones into Git, we want Heroku to rebuild those on deploy.

Casey Faist: This looks good. So I am going to save the file. Then I'm going to come down to my terminal and I'm going to check this change into Git, so I can check the status real quick just by the git status command. Cool, I only have the one change I'm going to git add the gitignore file and then I'm going gitcommit -m for message. Step one add gitignore. Now to deploy to Heroku do we need a gitignore file? Technically no. You can deploy successfully without a gitignore file present but it's highly recommended and not just for Heroku. Never store passwords or credentials in a Git repo. Don't push them up, don't put them in to begin with. You want to keep those things private and safe. Adding those files to your gitignore is a great way to do that.

Casey Faist: Similarly, anything you don't want pushed up to Heroku is a great candidate for the gitignore file. Things like your local venv, which you don't want to deploy it to Heroku because Heroku sets you up and environment for Python automatically. Other things like files you don't want included or general cruft to keep your deploy clean goes in your gitignore file. Next up we want to modularize our Django settings. To do that we need to navigate to our getting started Django directory, it's right here and now that we're here we're going to add a new folder. You're going to call this folder settings and this does matter, so make sure that you do use the name settings and now we're going to grab our settings.py file and we're going to move it to our directory. Now, this naming will get confusing, so I'm going to rename this to base.py. I chose this name because this is going to serve as the base that all of our other settings configurations are going to pull from but if something like dev or local.py makes more sense to you, feel free to use that.

Casey Faist: Just change out base for what you name this file later on. By moving and renaming the settings file our Django application now has two broken references, let's fix them before we move on. The first is in the wsgi.py in your getting started folder. Open it up and on line 12 you'll see that it has a Django settings module default being set and what we're setting it to is gettingstarted.settings. This is what it used to be, but we just moved our settings file, so to fix this, all we have to do is add a dot and then since I named my file base, I'm just going to type out base. Perfect. Save that and then navigate one more directory up to the root level directory, find manage.py. On line six you'll see here the same default is being set for the Django settings module. Same thing here, we want to add .base to the end of this line. Then as always, save these changes and add them to Git.

Casey Faist: Local projects only have one environment to keep track of, your local machine but once you want to deploy to different places or stages, its important to keep track of what settings go where. Nesting our settings files this way it makes it easy for us to keep track of where those settings are as well as take advantage of Heroku's continuous delivery tool pipelines. So you have four stages you build in development stage. This is your local machine, Docker container, however you like to develop locally. Next in the review stage, you want to check if your test pass along with the full test suite of your code base. If that goes well, you merge to staging, which is where you probably have more realistic settings, maybe some test data available in order to more accurately predict how the change will impact your production settings. Lastly, if all that goes well, you push to production where the change is now live for your customers.

Casey Faist: Continuous delivery or CD workflows are designed to test your change in conditions progressively closer and closer to production and in more and more detail. If something breaks, this means it's easier to debug since you know exactly what settings it broke with and when you deploy, you can deploy it with confidence knowing that you've tested your change thoroughly and in conditions as close to your production settings as possible. Continuous delivery is a powerful workflow and can make all of the difference in your experience as a developer once you've productionized your application. Heroku can save you a lot of time here, we've already built the tools for you to have a continuous delivery workflow. From your dashboard on Heroku you can with the click of a button, set up a pipeline, add applications to staging and production as well as deploy. You can also, if needed, roll back.

Casey Faist: If you connect your GitHub repository, pipelines can also automatically deploy and test new PRs opened on your repo. By providing the tooling and automating these processes, Heroku's continuous delivery workflow is powerful enough to help you keep up with your development cycle. Modularizing your Django settings is a great way to take advantage of this continuous delivery workflow by splitting up your settings, whether you're deploying to Heroku or elsewhere but there's one more thing we have to do to base.py. Django static assets work best when you also use whitenoise to manage your static assets. It's really easy to add to your project. Start by copying this middleware line here, all of it, this time you want the quotes and the comma. Copy and now head back to base.py.

Casey Faist: We're looking for your middleware list, Django loads your middleware in the order that it's listed here, so you always want your security first but it's important to add whitenoise as the second step in this base file. In this project I have a handy reminder where it goes. Boop. Next, jump back to the workshop and grab this next code block, the static file storage setting. Copy it and in base.py we're going to scroll all the way down to the bottom. See we've loaded whitenoise as middleware but to actually use whitenoise compression, we need to set this variable and with that we're done with base.py, congratulations. Save your work and commit it to Git. Our base settings are complete but now we need our Heroku settings. If you come to the workshop.md, you can see I've already provided a template file that should work with most projects. You can see here at the top we are importing our base settings file. If you're using your own project, make sure that you update this line to match what your directory structure looks like. For now, grab this whole text block and copy it.

Casey Faist: Navigate to base.py and instead of changing that file, we're going to add a new file to the settings folder. I'm going to call it heroku.py and inside this file we're going to paste our template. You can see in this file the values that we're listing are the ones that we're overriding from our base settings, so these are the settings that will be different on Heroku. Now we're using one of my favorite packages to do this. It's Django-environ imported just as environ. This allows us to quickly and easily interface with the operating system environment without knowing much about it. It's got built-in type conversions such as this, it knows and will cast this environment variable to a list for us and in particular it's automatic database parsing. This is all we need in order to parse our Heroku Postgres database URL that we will be given. It's just really convenient, so I'm a big fan of this package. I recommend you check it out. But with that, save and our settings are complete.

Casey Faist: Awesome. That's all the work we need to do to get our application into 12 factored shape but there's three more files we need in order to deploy to Heroku. Requirements.txt is a standard Python dependency file. In addition to the packages your project already uses, there's a few you need to deploy to Heroku. If we take a look at the provided requirements.txt file, you can see these required packages here. Obviously we have Django but there are four other ones. We've already talked about Django-environ as well as whitenoise and we've already configured those but the other two are also important and needed for deploy. The first is Gunicorn or Gunacorn. This is the recommended WSGI server for Heroku. We'll take a look at configuring this in just a bit. The last one is psychopg2. This is a Postgres database adapter. You need it in your requirements.txt file to deploy but you don't need any code changes in order to activate it, we just need it installed in the environment.

Casey Faist: A quick note, I chose to keep things simple for the purpose of this demo but when you're ready to deploy your project to Heroku, consider freezing your dependencies. You can do this with pip freeze. This will make your build a little bit more predictable by locking your exact dependency versions into your Git repo. This is important because Heroku recycles your resources once a day. This is important for application health but if your dependencies aren't locked, you might find yourself deploying one version of Django one day and a new one the next. When Heroku cycles your application resources which are organized into containers called dynos, we're not just reinstalling your application. On every dyno cycle or dyno deploy, Heroku rebuilds your environment onto AWS resources, this doesn't just mean your Django app. This means all of the system packages that Heroku provides as well as your Python installation.

Casey Faist: Heroku will install a default Python version if you don't specify one, but if you want to pick your Python version, you'll need a runtime.txt file. You want to put it in the root level directory next your requirements, manage.py, gitignore and the rest. This file only needs one line formatted like this, a lowercase python, a dash, and then the major, minor and patch version that you want your application to run on. The last file we need to add is a file specific to Heroku. The Procfile. This is what we use to specify the processes our application should run. The processes specified in this file will automatically boot on deploy to Heroku. The Procfile also goes in the root level directory right next to your requirements and runtime file. Make sure to capitalize the P of Procfile otherwise Heroku might not recognize it.

Casey Faist: Now the first thing we're going to do is specify a release phase process. The release phase of deploy is the best place to run tasks like migrations or updates and that's what we're going to add here. So pythonmanage.py migrate. The other process we're going to add to this file is the web process, the most important process for any web application. The other process we're going to add today is a web process. This is a very important process for web application. This is where our Gunicorn config goes. You want to pass Gunicorn the same things you would if you were to run it locally. So we want to pass it our WSGI file, which is located in the getting started directory and then we're going to give it a bit of configuration. So we're going to pass it preload, this will ensure it can receive requests just a little bit faster and then we're going to specify that the log file should get routed to Heroku.

Casey Faist: Congratulations, you've updated your app and you are ready for deploy. Woo hoo! Take a second before moving on and just double check that you've saved and committed all of your changes to Git. Remember, we need those changes in the Git repo in order for them to successfully deploy. After that, let's get ready to make an app. The first step is to create our Heroku app. You can do this from the command line since we already installed the Heroku CLI at the beginning of this workshop. All you need to do is type in Heroku create. All right, time to make some magic. It is Heroku create time. Enigmatic mountain. Excellent. This has done a couple of things for us. First, you can see it's generated not only an app but a unique name, that's that "enigmatic mountain" there. Next, it's provided a unique URL, this is where our app will live on the web and lastly, it's added a Git remote to our Git repository.

Casey Faist: You can confirm by saying git remote and now you can see that Heroku is there, that's how we're going to deploy. Now, before we go anywhere, I'm just going to grab this URL. I'm going to copy it because we'll need it in a second. Remember when we created our heroku.py settings file? We used Django-environ to load environment variables into our settings config. Those settings config need to be present in our Heroku environment, so let's set those now. We'll be using the Heroku CLI for this but you can add and edit config variables from your dashboard directly. The Heroku CLI command we'll be using though is heroku config:set. This will take in your key-value pairs as argument and set in your Heroku runtime environment. First, let's configure our allowed hosts, so we want to type in a heroku config:set and allowedhosts= and then we're going to paste that URL we just got. Make sure to remove the slash on the end if you haven't already. Perfect.

Casey Faist: Next, let's set our Django settings module, so up arrow and Django settings module. This is what determines what settings configuration we use on this platform. So you will remember that we want to use the gettingstarted.settings and instead of base this time, we want the Heroku settings. Now we need to add the secret key as a config var to our application. We can use the same CLI command, heroku config:set to do this and make sure that you add a different secret key than you used locally. It can be anything. You may even want to check out the Python secrets module if you have specific secret key requirements. But the important thing is do not add your production secret key to Git ever and only share it with a password manager. Like Gandalf said, "Keep it secret, keep it safe." Now locally, Django is configured to use a SQLite database but we're productionizing. We need something a little bit more powerful, so it's time to provision our Postgres database.

Casey Faist: First, let's check if we have a database already. Heroku addons will tell us. Okay, so we do need one for our application. Makes sense, it's brand new. Now you can do this with the click of a button from your dashboard but I'm going to show you how to do it from your command line. heroku addon just like before and we're going to create a Heroku Postgres goal, Postgres sequel and we're going to use the hobby dev tier of database. We offer several tiers of Postgres database. This is the free tier, so you can play around with this without a credit card. Boom, Postgres database. And if you remember from the settings file, our application is expecting this database URL. Luckily, this command automatically adds this to our Heroku environment so we don't have to do anything more to connect our application to our Postgres database. It is time. Your code is ready, your Heroku app is configured, you are ready to deploy. This is the easy part.

Speaker 2: Yay.

Casey Faist: First, make sure your terminal is in the root level directory of your project. If you type ls on Mac or Linux, you should see that requirements.txt file and the runtime.txt file right there. Then just type out, git push heroku master and we take care of the rest. You'll see your build logs scrolling through your terminal. This will show you what we're installing on your behalf and where you are in the build process. You'll see the release phase as well that we specified earlier. Now, if you're not on the master branch, you made your own, no worries. You can still deploy your code to Heroku but you'll need to type out, git push heroku, your branch:master. The last step is to scale up our web process. This creates new dynos, however many you specify or copies of your code into our containers to handle more web traffic. You can do this from your dashboard or you can use the Heroku ps:scale command and we want to scale up our web process to one.

Casey Faist: Now to go freak out about your brand new website just type in heroku open and there you have it. Okay. If you hit some snags, don't worry, we have some tips that might help. Number one, are all of your changes saved and checked into Git? Number two, are your changes on master or are they on a different branch or a combination? Make sure that whatever you're deploying, all of your changes are in that Git branch. Number three, did you deploy from the root directory of your project? Did you also Heroku create from the root directory of your project? If not, this could absolutely cause a trip up. Number four, did you remove anything from the code in the provided demo that we didn't discuss? Number five, use your tools. What tools you ask? In addition to your build logs which will tell you whether your application successfully deployed or not, you have access to all logs produced by Heroku and by your application. You can get to them through a couple of ways but the quickest way is just to say, heroku logs tail.

Casey Faist: And this, let me make this screen a little bit bigger, is the live feedback from your application. If you switch over and click on something that'll get reflected here. You can see that we have our Heroku web one process, we have our app processes and then we have these router methods. You can see this one's a GET. All of this is here to help you debug any connection issues you may have. Another tool you have is the Heroku run bash command. This spins up a one time or one-off dyno container that you have direct access to from your console. So if I ls, you can see that this is my deployed application, it can be useful to check that what is up here matches what is locally on your machine. If not, you might see some issues. You can also check whether things work by spinning up a Python terminal and this is a full Python REPL, so anything you could do locally you can do here.

Casey Faist: Heroku has a wealth of technical documentation as well. Dev Center is where you'll find most of our technical how-to and supported technologies information and if you're having a technical issue, chances are someone else has asked the same question and been answered on our help docs. Use both of these resources to solve your problems as well as to learn about best practices when deploying to Heroku. And with that you have successfully productionized and deployed a Heroku app. Congratulations. I hope you had as much fun in this tutorial as I had putting it together. I have been Casey Faist, you have been amazing. Stay well and happy coding from your friends at Heroku.

Imagine that you've just spent the last two weeks pouring all your energy into an application. It's magnificent, and you're finally ready to share it on the Internet. How do you do it? In this post, we're going to walk through the hands-on process aimed at Python developers deploying their local application to Heroku.

An application running on Heroku works best as a 12-factor application. This is actually a concept that Heroku championed over 10 years ago. It's the idea that you build an application with robust redeployments in mind. Most of this workshop is actually not specific to Heroku, but rather, about taking a regular Django application and making it meet the 12 factor app methodology, which has become a standard that most cloud deployment providers not only support but recommend.

Prerequisites

Before completing this workshop, we're going to make a few assumptions about you, dear reader. First, this is not going to be a Django tutorial. If you're looking for an introduction to Django, their documentation has some excellent tutorials to follow. You will also need a little bit of Git familiarity, and have it installed on your machine.

In order to complete this workshop, you'll need a few things:

  1. An account on Heroku. This is completely free and doesn't require any payment information.
  2. The Heroku CLI. Once your application is on Heroku, this will make managing it much easier.
  3. You'll need to clone the repository for this workship, and be able to open it in a text editor.

With all that sorted, it's time to begin!

Look around you

With the project cloned and available on your computer, take a moment to explore its structure. We'll be modifying the manage.py and requirements.txt files, as well as settings.py and wsgi.py in the gettingstarted folder.

Updating .gitignore

To begin with, we'll be updating the gitignore file. A gitignore file excludes files which you don't want to check into your repository. In order to deploy to Heroku, you don't technically need a gitignore file. You can deploy successfully without one, but it's highly recommended to always have one (and not just for Heroku). A gitignore can be essential for keeping out passwords and credentials keys, large binary files, local configurations, or anything else that you don't want to expose to the public.

Copy the following block of code and paste it into the gitignore file in the root of your project:

/venv
__pycache__
db.sqlite3          # not needed if you're using Postgres locally
gettingstarted/static/

The venv directory contains a virtual environment with the packages necessary for your local Python version. Similarly, the __pycache__ directory contains precompiled modules unique to your system. We don't want to check in our database (db.sqlite3), as we don't want to expose any local data. Last, the static files will be automatically generated for us during the build and deploy process to Heroku, so we'll exclude the gettingstarted/static/ directory.

Go ahead and run git status on your terminal to make sure that gitignore is the only file that's been modified. After that, call git add, then git commit -m "step 1 add git ignore".

Modularize your settings

Next up, we want to modularize our Django settings. To do that, add a new folder within gettingstarted called settings. Then, move the settings.py file into that directory. Since this naming scheme is a bit confusing, let's go ahead and rename that file to base.py. We'll call it that because it will serve as the base (or default) configuration that all the other configurations are going to pull from. If something like dev.py or local.py makes more sense to you, feel free to use that instead!

Local projects only have one environment to keep track of: your local machine. But once you want to deploy to different places, it's important to keep track of what settings go where. Nesting our settings files this way makes it easy for us to keep track of where those settings are, as well as take advantage of Heroku's continuous delivery tool pipelines.

By moving and renaming the settings file, our Django application now has two broken references. Let's fix them before we move on.

The first is in the wsgi.py in your gettingstarted folder. Open it up, and on line 12 you'll see that a default Django settings module is being set to gettingstarted.settings, a file which no longer exists:

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gettingstarted.settings")

To fix this, append the name of the file you just created in the settings subfolder. For example, since we called ours base.py, the line should now look like this:

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gettingstarted.settings.base")

After saving that, navigate up one directory to manage.py. On line 6, you'll see the same default being set for the Django settings module. Once again, append .base to the end of this line, then commit both of them to Git.

Continuous delivery pipelines

In an application's deployment lifecycle, there are typically four stages:

  1. You build your app in the development stage on your local machine to make sure it works.
  2. Next comes the review stage, where you check to see if your changes pass with the full test suite of your code base.
  3. If that goes well, you merge your changes to staging. This is where you have conditions as close to public as possible, perhaps with some dummy data available, in order to more accurately predict how the change will impact your users.
  4. Lastly, if all that goes well, you push to production, where the change is now live for your customers.

Continuous delivery (CD) workflows are designed to test your change in conditions progressively closer and closer to production and with more and more detail. Continuous delivery is a powerful workflow that can make all of the difference in your experience as a developer once you've productionized your application. Heroku can save you a lot of time here, as we've already built the tools for you to have a continuous delivery workflow. From your dashboard on Heroku, you can—with the mere click of a button!–set up a pipeline, add applications to staging and production, and deploy them.

If you connect your GitHub repository, pipelines can also automatically deploy and test new PRs opened on your repo. By providing the tooling and automating these processes, Heroku's continuous delivery workflow is powerful enough to help you keep up with your development cycle.

Adding new middleware to base.py

Modularizing your Django settings is a great way to take advantage of this continuous delivery workflow by splitting up your settings, whether you're deploying to Heroku or elsewhere, but there's one more change we have to make to base.py.

Django static assets work best when you also use the whitenoise package to manage your static assets. It's really easy to add to your project.

In your base.py file, scroll down to about line 43, and you should see an array of package names like this:

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    # Whitenoise goes here
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

This is your list of Django middleware, which are sort of like plugins for your server. Django loads your middleware in the order that it's listed, so you always want your security middleware first, but it's important to add whitenoise as the second step in this base file.

Copy the following line of code and replace the line that says Whitenoise goes here with this:

"whitenoise.middleware.WhiteNoiseMiddleware",

We've loaded whitenoise as middleware, but to actually use the whitenoise compression, we need to set one more variable. Copy the following code and paste it right at the end of your base.py file:

STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

With that, we're done with base.py. Congratulations! Save your work and commit it to Git.

Setting up heroku.py

Our base settings are complete, but now we need our Heroku-specific settings. Create a new file under gettingstarted/settings called heroku.py and paste the following block of code:

"""
Production Settings for Heroku
"""

import environ

# If using in your own project, update the project namespace below
from gettingstarted.settings.base import *

env = environ.Env(
    # set casting, default value
    DEBUG=(bool, False)
)

# False if not in os.environ
DEBUG = env('DEBUG')

# Raises django's ImproperlyConfigured exception if SECRET_KEY not in os.environ
SECRET_KEY = env('SECRET_KEY')

ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')

# Parse database connection url strings like psql://user:pass@127.0.0.1:8458/db
DATABASES = {
    # read os.environ['DATABASE_URL'] and raises ImproperlyConfigured exception if not found
    'default': env.db(),
}

You can see in this file the values that we're listing here are the ones that we're overriding from our base settings, so these are the settings that will be different and unique for Heroku.

To do this, we're using one of my favorite packages, Django-environ. This allows us to quickly and easily interface with the operating system environment without knowing much about it. It has built-in type conversions, and in particular it has automatic database parsing. This is all we need in order to parse our Heroku Postgres database URL that we will be given. It's just really convenient.

Heroku-specific files

That's all the work we need to do to get our application into 12 factored shape, but there are three more files we need in order to deploy to Heroku.

requirements.txt

In addition to the packages your project already uses, there are a few more you need to deploy to Heroku. If we take a look at the provided requirements.txt file, you can see these required packages here. We've already talked about Django, Django-environ, and whitenoise, and we've already configured those for use. But the other two are also important and needed for deployment.

The first one is called Gunicorn. This is the recommended WSGI server for Heroku. We'll take a look at configuring this in just a bit. The next one is psychopg2. This is a Postgres database adapter. You need it in your requirements.txt file to deploy, but you don't need any code changes in order to activate it.

A quick side note: we're keeping our discussion on packages simple for the purpose of this demo, but when you're ready to deploy a real project to Heroku, consider freezing your dependencies. You can do this with the pip freeze command. This will make your build a little bit more predictable by locking your exact dependency versions into your Git repo. If your dependencies aren't locked, you might find yourself deploying one version of Django one day and a new one the next.

runtime.txt

Heroku will install a default Python version if you don't specify one, but if you want to pick your Python version, you'll need a runtime.txt file. Create one in the root directory, next to your requirements.txt, manage.py, .gitignore and the rest. Specify your Python version with the prefix python-, followed by the major, minor, and patch version that you want your application to run on:

python-3.8.2

Procfile

The last file we need to add is a file specific to Heroku: the Procfile. This is what we use to specify the processes our application should run. The processes specified in this file will automatically boot on deploy to Heroku. Create a file named Procfile in the root level directory, right next to your requirements.txt and runtime.txt files. (Make sure to capitalize the P of Procfile otherwise Heroku might not recognize it!) Copy-paste the following lines into it:

release: python3 manage.py migrate
web: gunicorn gettingstarted.wsgi --preload --log-file -

The release phase of a Heroku deployment is the best place to run tasks, like migrations or updates. The command we will run during this phase is to simply run the migrate task defined in manage.py.

The other process is the web process, which is very important, if not outright essential, for any web application. This is where we pass our Gunicorn config, the same things we need when running the server locally. We pass it our WSGI file, which is located in the gettingstarted directory, and then we pass a few more flags to add it a bit more configuration. The --preload flag ensures that the app can receive requests just a little bit faster; the --logfile just specifies that the log file should get routed to Heroku.

Readying for deployment

Take a second before moving on and just double check that you've saved and committed all of your changes to Git. Remember, we need those changes in the Git repo in order for them to successfully deploy. After that, let's get ready to make an app!

Creating an app with heroku create

Since we have the Heroku CLI installed, we can call heroku create on the command line to have an app generated for us:

$ heroku create
Creating app... done, ⬢ mystic-wind-83
Created http://mystic-wind-83.herokuapp.com/ | git@heroku.com:mystic-wind-83.git

Your app will be assigned a random name—in this example, it's mystic-wind-83—as well as a publicly accessible URL.

Setting environment variables on Heroku

When we created our heroku.py settings file, we used Django-environ to load environment variables into our settings config. Those environment variables also need to be present in our Heroku environment, so let's set those now.

The Heroku CLI command we'll be using for this is heroku config:set. This will take in key-value pairs as arguments and set them in your Heroku runtime environment. First, let's configure our allowed hosts. Type the following line, and replace YOUR_UNIQUE_URL with the URL generated by heroku create:

$ heroku config:set ALLOWED_HOSTS=<YOUR_UNIQUE_URL>

Next, let's set our Django settings module. This is what determines what settings configuration we use on this platform. Instead of using the default of base, we want the Heroku-specific settings:

$ heroku config:set DJANGO_SETTINGS_MODULE=gettingstarted.settings.heroku

Lastly, we'll need to create a SECRET_KEY. For this demo, it doesn't matter what its value is. You can use a secure hash generator like md5, or a password manager's generator. Just be sure to keep this value secure, don't reuse it, and NEVER check it into source code! You can set it using the same CLI command:

$ heroku config:set SECRET_KEY=<gobbledygook>

Provisioning our database

Locally, Django is configured to use a SQLite database but we're productionizing. We need something a little bit more robust. Let's provision a Postgres database for production.

First, let's check if we have a database already. The heroku addons command will tell us if one exists:

$ heroku addons
No add-ons for app mystic-wind-83.

No add-ons exist for our app, which makes sense—we just created it! To add a Postgres database, we can use the addons:create command like this:

$ heroku addons:create heroku-postgresql:hobby-dev

Heroku offers several tiers of Postgres databases. hobby-dev is the free tier, so you can play around with this without paying a dime.

Going live

It is time. Your code is ready, your Heroku app is configured, you are ready to deploy. This is the easy part!

Just type out

$ git push heroku master

And we'll take care of the rest! You'll see your build logs scrolling through your terminal. This will show you what we're installing on your behalf and where you are in the build process. You'll also see the release phase as well that we specified earlier.

Scaling up

The last step is to scale up our web process. This creates new dynos, or, in other words, copies of your code on Heroku servers to handle more web traffic. You can do this using the following command:

$ heroku ps:scale web=1

To see your app online, enter heroku open on the terminal. This should pop open a web browser with the site you just built.

Debugging

If you hit some snags, don't worry, we have some tips that might help:

  • Are all of your changes saved and checked into Git?
  • Are your changes on the master branch or are they on a different branch? Make sure that whatever you're deploying, all of your changes are in that Git branch.
  • Did you deploy from the root directory of your project? Did you also call heroku create from the root directory of your project? If not, this could absolutely cause a trip up.
  • Did you remove anything from the code in the provided demo that we didn't discuss?

Logging

If you've run through this list and still have issues, take a look at your log files. In addition to your build logs—which will tell you whether your application successfully deployed or not—you have access to all logs produced by Heroku and by your application. You can get to these through a couple of different ways, but the quickest way is just to run the following command:

$ heroku logs --tail

Remote console

Another tool you have is the heroku run bash command. This provides you with direct access from your terminal to a Heroku dyno with your code deployed to it. If you type ls, you can see that this is your deployed application. It can be useful to check that what is up here matches what is locally on your machine. If not, you might see some issues.

Wrapping up

Congratulations on successfully deploying your productionized app onto Heroku!

To help you learn about Heroku, we also have a wealth of technical documentation. Our Dev Center is where you'll find most of our technical how-to and supported technologies information. If you're having a technical issue, chances are someone else has asked the same question and it's been answered on our help docs. Use these resources to solve your problems as well as to learn about best practices when deploying to Heroku.

Originally published: June 22, 2020

Browse the archives for engineering or all blogs Subscribe to the RSS feed for engineering or all blogs.