authors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.
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 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:
Now that we understand how Heroku works and its basic terminology, we will show how straightforward it is to deploy our sample application.
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.
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
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:
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.
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.
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.
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.
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.
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.
Now we ask Heroku to associate our app with our GitHub repository with the following instructions:
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.
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.
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.
Heroku is a cloud-based platform that hosts apps written in popular languages, including Python.
Heroku makes hosting and deployment simple for developers, even going so far as to link to source code repositories for no-effort redeployments.
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.
Plymouth, MI, United States
Member since June 4, 2018
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
World-class articles, delivered weekly.
World-class articles, delivered weekly.
Join the Toptal® community.