This article is the third in a series exploring how to effectively use pydantic in Django projects. As a quick recap, the first article demonstrated how pydantic’s use of Python type hints can simplify Django settings management. In the second tutorial, we built upon this concept by using Docker to create a web application, ensuring consistency between our development and production environments.
Deploying and updating source code can be a frustrating experience, often leading to disappointment. Having experienced my fair share of unsuccessful attempts with other platforms, I’m incredibly pleased to have found a reliable and efficient solution with Django and Heroku. In this article, I’ll share the secrets to my success through a detailed example.
Our objective is to deploy our Django application, prioritizing ease of use and security. Heroku excels in both areas, providing a smooth and secure platform for our application.
In part 2 of this series, we built a sample hello-visitor application and demonstrated how to mirror production settings in our development environment using pydantic, significantly reducing project risk.
The final step is to deploy our application using Heroku. Note: You’ll need a Basic plan account on Heroku to follow along with this tutorial.
Understanding Heroku
Heroku is a Platform-as-a-Service (PaaS) designed for deploying and managing applications. These applications, referred to as “apps,” combine our system requirements and source code. Deploying an app on Heroku involves creating a Heroku slug, which is essentially an application image that packages our configuration, add-ons, and other necessary components for deployment. Heroku slugs are comparable to Docker images.
Heroku follows a structured pipeline that consists of the following steps:
- Build step:
- Heroku analyzes the application source code to determine the required technologies.
- It then builds the necessary base system image using a buildpack, in this case, heroku/python.
- The resulting image is referred to as a slug in Heroku.
- Release step:
- This step allows for pre-deployment tasks and various checks on the system, settings, or data.
- Database migrations are often performed during this stage.
- Runtime step:
- Heroku launches our images into lightweight containers called dynos and connects them to our add-on services, such as a database.
- These dynos, working together, form our system infrastructure, including the necessary routers for communication between them.
- The router, responsible for handling incoming HTTP requests, directs traffic to the appropriate web server dyno(s).
- Scaling is made easy with Heroku’s dynamic dyno provisioning based on load.
With a basic understanding of Heroku’s workings and terminology, let’s explore the straightforward process of deploying our sample application.
Installing the Heroku CLI
To interact with Heroku, we need its command-line interface installed installed locally. We’ll use the standard snap installation method on an Ubuntu development machine. For other platforms, the Heroku documentation provides detailed installation instructions.
| |
Next, we need to configure our local Heroku tools with our credentials using the authentication step:
| |
This command will securely store our email address and an API token in the ~/.netrc file for future use.
Creating a Heroku App
With Heroku installed, the next step is to creating our app. This app serves as a reference to our source code repository and lists the required add-ons.
It’s crucial to note that Heroku requires each application to have a unique name across all users. Therefore, we cannot use a common example name for this tutorial. Choose a name that resonates with you and use it consistently throughout the instructions. While our screenshots will display hello-visitor as the app name, remember to replace it with your unique name.
We’ll utilize the basic Heroku scaffolding command to create our app:
| |
Adding the PostgreSQL Add-on
Our Django project relies on a relational database, as outlined in part 2 of this series. Configuring the necessary add-ons is done through the Heroku browser interface. Follow these steps:
- Go to the Resources tab in your Heroku dashboard to manage add-ons.
- Select Postgres and specifically choose heroku-postgresql for installation.
- Opt for the Mini add-on plan.
- Link this add-on to your uniquely named app.
- Submit the order form.
Once PostgreSQL is provisioned and linked to our app, the database connection string will be accessible in our app’s configuration variables. To view it, navigate to Settings and click on Reveal Config Vars. You’ll find a variable named DATABASE_URL:
| |
As highlighted in parts 1 and 2, our application leverages the power of pydantic and environment variables. Heroku automatically exposes its Config Vars to the application environment, eliminating the need for code modifications when deploying to production. We won’t delve into the specifics of each setting, leaving that as an exercise for you.
Configuring the Application Pipeline
Earlier, we discussed the key steps involved in Heroku’s pipeline for building, configuring, and deploying an app. Each step is associated with files that contain the corresponding settings and commands.
Build Step Configuration
We need to specify the technology stack for Heroku to use. Since our app relies on Python and a set of dependencies outlined in its requirements.txt file, and Heroku defaults to a recent Python version (currently 3.10.4), we can skip explicit build configuration for now.
Release Step Configuration
Heroku’s release step, which occurs before deployment, is defined by a command specified in our app’s hello-visitor/Procfile. Following best practices, we’ll create a separate shell script to list the commands or scripts to be executed. Heroku will always run the contents of hello-visitor/Procfile.
Since we don’t have a script yet, let’s create our release script, hello-visitor/heroku-release.sh, instructing Heroku to enhance deployment security and automatically run database migrations. Add the following code:
| |
As with any shell script, we need to make it executable. Use the following command on Unix distributions:
| |
Now that our release script is ready, add it to your app’s hello-visitor/Procfile to ensure it runs during the release phase. Create the the Procfile and add the following:
| |
With the release step configured, let’s move on to defining the deployment step.
Deployment Step Configuration
Our goal is to start a web server with two worker nodes.
Again, following best practices, we’ll create a separate deployment script named heroku-web.sh in our project root directory. Add the following content:
| |
Make the script executable:
| |
Update your app’s Procfile to include the deployment script, ensuring it runs at the appropriate stage:
| |
With our Heroku app pipeline defined, the next step is to set up environment variables for our source code. This step follows logically after defining the Heroku app. Our deployment will fail without these variables, as our source code relies on them.
Setting Up Environment Variables
Django requires a secret key, SECRET_KEY, for proper operation. This key, along with other variables, will be stored in our app’s environment variable collection. Before configuring them, let’s generate our secret key, ensuring it doesn’t contain special characters by using base64 encoding (not UTF-8). Base64 avoids non-alphanumeric characters like + or @, which can cause issues when provisioning secrets as environment variables. Generate the SECRET_KEY using the following Unix command:
| |
Now we can configure our environment variables as Heroku Config Vars.
Previously, we examined the database connection string in the Config Vars admin panel. Navigate back to this panel to add new variables and their values:
Key | Value |
|---|---|
|
|
| (Use the generated key value) |
|
|
|
|
At this stage, our Heroku app has a fully configured deployment pipeline and environment variables. The last step is to connect Heroku to our source code repository.
Linking to GitHub Repository
Instruct Heroku to associate our app with our GitHub repository by following these steps:
- Go to the Deploy tab in your Heroku dashboard.
- Authenticate your Heroku account with GitHub (a one-time process).
- Navigate to the Admin panel for your Heroku app.
- Choose GitHub from the Deployment method dropdown. This will display a list of available repositories from your GitHub account.
- Select the desired GitHub repository.
- Heroku will establish a connection with your chosen repository.
Your dashboard should now resemble the following:
To deploy our app manually, navigate to the manual deploy section, select the main branch of your repository, and click the Deploy Branch button.
If everything goes smoothly, our deployment will succeed, utilizing our defined build and release scripts, and the website will be deployed.
Testing the Deployed Application
To test the deployed application, click the Open App button located at the top of the Heroku App dashboard.
The webpage will show a visitor count that increases with each refresh.
Achieving Smoother Django App Deployments
This deployment process is incredibly straightforward. The configuration is simple, and the core Heroku buildpack, coupled with the platform itself, handles most of the heavy lifting. Moreover, the core Heroku Python buildpack is open source, making this approach applicable to other application platforms.
Combining this ease of deployment with the power of a mirrored environment and pydantic settings management results in a robust, environment-independent deployment that works seamlessly both locally and on the web.
By adopting this Django settings management strategy, you’ll have a single settings.py file that configures itself using environment variables.
The Toptal Engineering Blog would like to thank Stephen Davidson for his contributions in reviewing and beta testing the code samples presented in this article.


