How to Deploy Django on Heroku: A Pydantic Tutorial, Part 3
Heroku makes deployment—and redeployment—incredibly simple. Explore Heroku’s Django hosting in this tutorial, and see for yourself.
Heroku makes deployment—and redeployment—incredibly simple. Explore Heroku’s Django hosting in this tutorial, and see for yourself.
Arjaan is a senior engineer and data scientist who creates mission-critical, Python-based cloud solutions focused on Rasa for international banks and insurance companies. He architects and teaches large-scale Kubernetes solutions.
PREVIOUSLY AT
This is the third installment in a series on leveraging pydantic for Django-based projects. Before we continue, let’s review: In the series’ first installment, we focused on pydantic’s use of Python type hints to streamline Django settings management. In the second tutorial, we used Docker while building a web application based on this concept, aligning our development and production environments.
Deploying source code—and redeploying after updates—can be a frustrating process that leaves you brokenhearted. After so many bad relationships with other deployment platforms, I feel lucky to have found lasting happiness with Django and Heroku. I want to share the secrets of my success through a carefully curated example.
We want to deploy our Django application and ensure it is easy and secure by default. Heroku provides a no-stress relationship with our application platform by combining efficiency and security.
We have already built a sample hello-visitor
application in part 2 of this Django and pydantic tutorial series and discussed how our development environment should mirror our production settings using pydantic. This mirroring removed considerable risk from our project.
The remaining task is to make our application available on the web using Heroku. Note: In order to complete this tutorial, you must sign up for a Basic plan account at Heroku.
Heroku Overview
Heroku is a Platform-as-a-Service, and it serves applications. Those applications, called apps, couple our system requirements and source code. To put our app on Heroku, we must create a Heroku slug—an application image that combines our configuration, add-ons, and more to create a deployable release. Heroku slugs are comparable to Docker images.
Heroku goes through a well-orchestrated pipeline with these steps:
- Build step:
- Heroku inspects our application source and determines what technologies are required.
- Heroku builds the required base system image for our application using a buildpack, which in this case is heroku/python.
- The resulting image is called a slug in Heroku.
- Release step:
- Heroku allows us to do pre-deployment work or perform various checks on our system, settings, or data.
- Database migrations are common during this step.
- Runtime step:
- Heroku spins up our images into lightweight containers called dynos and connects them to our add-on services, e.g., a database.
- One or more dynos constitute our system infrastructure, including required routers to enable intra-dyno communication.
- Incoming HTTP requests also fall within the router’s responsibilities, where traffic links to the appropriate web server dyno(s).
- Scaling out is simple because Heroku allows for dynamic provisioning of dynos based on load.
Now that we understand how Heroku works and its basic terminology, we will show how straightforward it is to deploy our sample application.
Install Heroku CLI
We need Heroku’s command-line interface installed locally. Using the standard snap installation makes this simple—we will demonstrate this on an Ubuntu development machine. The Heroku documentation provides additional steps to install its toolset on other platforms.
sudo snap install --classic heroku
# check that it works
heroku --version
We must configure our local Heroku tools with our credentials via the authentication step:
heroku login
This will save our email address and an API token into the ~/.netrc
file for future use.
Create Heroku App
With Heroku installed, creating our app is the initial step toward deploying our source code. This app not only points to our source code repository, but also enumerates which add-ons we need.
A critical note about Heroku deployment is that every application must have a unique name for every person using Heroku. Therefore, we cannot use a single example name while going through these steps. Please pick a name that makes you happy and plug that into the instruction block throughout this tutorial. Our screenshots will list the app name as hello-visitor
, but as you follow along, your uniquely chosen name will appear in those locations instead.
We use the basic Heroku scaffolding command to create our app:
heroku apps:create <UNIQUE-APP-NAME-HERE>
The PostgreSQL Add-on
Our app requires a relational database for our Django project, as mentioned in part 2 of our series. We configure required add-ons through the Heroku browser interface with the following steps:
- Navigate to the Resources tab in the Heroku dashboard to configure add-ons.
- Ask Heroku to install Postgres, specifically heroku-postgresql.
- Choose the Mini add-on plan.
- Associate this add-on with our uniquely named app.
- Click Submit Order Form.
Once PostgreSQL has been provisioned and associated with our app, we can see our database connection string in our app’s configuration variables. To demonstrate this, we navigate to Settings and click on Reveal Config Vars, where we see a variable DATABASE_URL
:
DATABASE_URL=postgres://{user}:{password}@{hostname}:{port}/{database-name}
As explained in parts 1 and 2 in our series, the power inherent in our application comes from the elegant use of pydantic and environment variables. Heroku makes its Config Vars available in the application environment automatically, which means our code doesn’t require any changes to host in our production environment. We won’t explore each setting in detail, but will leave this as an exercise for you.
Configuring Our Application Pipeline
When we introduced Heroku above, we detailed the key steps in its pipeline that are needed to create, configure, and deploy an app. Each of these steps has associated files containing the appropriate settings and commands.
Configure the Build Step
We need to tell Heroku which technology stack to use. Our app uses Python and a set of required dependencies, as listed in its requirements.txt
file. If we want our app to use a recent Python version (currently defaulted to version 3.10.4) Heroku doesn’t require us to explicitly identify which Python version to use for the build step. Therefore, we will skip explicit build configuration for now.
Configure the Release Step
Heroku’s release step, done pre-deployment, has an associated command specified in our app’s hello-visitor/Procfile
. We follow best practices by creating a separate shell command listing the commands or dependent scripts we want to run. Heroku will always read the hello-visitor/Procfile
file and execute its contents.
We don’t have a script to refer to in that file yet, so let’s create our release shell script, hello-visitor/heroku-release.sh
, and ask Heroku to secure our deployment and perform database migrations automatically with the following text:
# file: hello-visitor/heroku-release.sh
cd src
python manage.py check --deploy --fail-level WARNING
python manage.py migrate
As with any user-created shell script, we must ensure it is executable. The following command makes our script executable on Unix distributions:
chmod +x heroku-release.sh
Now that we have written our release script, we add it to our app’s hello-visitor/Procfile
file so that it will run during release. We create the Procfile
and add the following content:
# file: hello-visitor/Procfile
release: ./heroku-release.sh
The fully configured release step leaves only the deployment step definition before we can do a test deployment.
Configure the Deployment Step
We will configure our app to start a web server with two worker nodes.
As we did in our release section, we will follow best practices and create a separate shell script containing the deployment operations. We will call this deployment script heroku-web.sh
and create it in our project root directory with the following contents:
# file: hello-visitor/heroku-web.sh
cd src
gunicorn hello_visitor.wsgi --workers 2 --log-file -
We ensure our script is executable by changing its system flags with the following command:
chmod +x heroku-web.sh
Now that we have created our executable deployment script, we update our app’s Procfile
so that the deployment step runs at the appropriate phase:
# file: hello-visitor/Procfile
release: ./heroku-release.sh
web: ./heroku-web.sh
Our Heroku app pipeline is now defined. The next step is to prepare the environment variables used by our source code because this follows the Heroku app definition screen in order. Without these environment variables, our deployment will fail because our source code relies on them.
Environment Variables
Django requires a secret key, SECRET_KEY
, to operate correctly. This key will be stored, along with other variables, in our app’s associated environment variable collection. Before we fully configure our environment variables, let’s generate our secret key. We must ensure there are no special characters in this key by encoding it with base64 (and not UTF-8). base64 does not contain non-alphanumeric characters (e.g., +, @) that may cause unexpected results when secrets are provisioned as environment variables. Generate the SECRET_KEY
with the following Unix command:
openssl rand -base64 70
With this key in hand, we may now configure our environment variables as Heroku’s Config Vars.
Earlier, we looked at the database connection string in the Config Vars administration panel. We must now navigate to this administration panel to add variables and specific values:
Key | Value |
---|---|
|
|
|
(Use the generated key value) |
|
|
|
|
At this point, our Heroku app has all the steps in the deployment pipeline configured and our environment variables set. The final configuration step is pointing Heroku at our source code repository.
GitHub Repository
Now we ask Heroku to associate our app with our GitHub repository with the following instructions:
- Navigate to the Deploy tab in the Heroku dashboard.
- Authenticate our Heroku account with GitHub (only done once).
- Navigate to the Admin panel for our Heroku app.
- In the Deployment method dropdown, select GitHub. Heroku will then show a list of available projects in our GitHub account.
- We select our GitHub repository.
- Heroku connects to the GitHub repository.
After that, our dashboard should look like the following:
We may now manually deploy our app by navigating to the manual deploy section, selecting our repository’s main
branch, and clicking the Deploy Branch button.
If all goes well, our deployment will correctly complete using our defined build and release scripts and deploy the website.
A Test Run
We can try out the deployed application by clicking the Open App button at the top of the Heroku App dashboard.
The webpage will show the number of site visitors, which increases each time you refresh the page.
Smoother Django App Deployments
In my opinion, this deployment couldn’t be any easier. The configuration steps are not cumbersome, and the core Heroku buildpack, lovingly cradled by the Heroku platform, does almost all the heavy lifting. Better yet, the core Heroku Python buildpack is open source, and many other application platforms use it. So the approach you have learned in this tutorial is a highly transferable skill.
When we couple deployment ease with the magic of the mirrored environment and pydantic settings management, we have a stable, environment-independent deployment that works locally and on the web.
By following this Django settings management approach, you end up with a single settings.py
that configures itself using environment variables.
The Toptal Engineering Blog extends its gratitude to Stephen Davidson for reviewing and beta testing the code samples presented in this article.
Further Reading on the Toptal Blog:
Understanding the basics
What is Heroku used for?
Heroku is a cloud-based platform that hosts apps written in popular languages, including Python.
Why is Heroku popular?
Heroku makes hosting and deployment simple for developers, even going so far as to link to source code repositories for no-effort redeployments.
What is the best way to deploy a Django app?
Heroku and its Python buildpack make for effortless deployments. Additionally, that same buildpack allows other deployment platforms to use many of the same features for Django hosting and deployments.
Arjaan Buijk
Plymouth, MI, United States
Member since June 4, 2018
About the author
Arjaan is a senior engineer and data scientist who creates mission-critical, Python-based cloud solutions focused on Rasa for international banks and insurance companies. He architects and teaches large-scale Kubernetes solutions.
PREVIOUSLY AT