Matt Laux

Deploy a Django web application on Heroku

When I initially began my web development journey, I started with Django. I had some previous experience with Python and some quick googling revealed that many major companies such as Instagram and Spotify were using Django to some extent.

This seemed like a great framework to begin with. Django is a "batteries-included" full-stack application that has been around for quite a while. The primary goal of Django is to speed up development by handling many of the commonly encountered issues in web development.

A couple of issues that Django has built-in handling for are:

  1. CSRF protection
  2. User management
  3. Secure authentication and authorization
  4. Database integration
  5. And much more...

If you have minimal experience, need to rapidly develop a complete web application, or have need of Python then Django could be a great choice for you.

However, Django does have a few disadvantages:

  1. Statically-generated pages
  2. Django-specific syntax and flow that must be learned
  3. Strongly-opinionated

Over the past decade Javascript has come to dominate the front-end of web development. Every company wants fast and dynamic pages that provide interactivity for their user. This is the primary issue with Django's statically-generated pages. The solution to this is the Django REST framework.

With the Django REST framework, you get many of the advantages of Django while still being able to use a JS front-end framework such as React.

Overview

In this guide I will discuss how to upload a Django web application to Heroku so that you can begin serving your creation to potential customers.

For the backend you will use a postgreSQL database hosted on the Heroku Postgres add-on.

All images will be hosted on AWS S3.

Heroku is one of the most popular platforms for hosting web applications in the cloud. Many novice programmers underestimate how complex deploying a website can be. Even once you have a web application built in your local development environment, there are still many steps before it is ready for production.

The application must undergo the build process, it must be optimized, servers need to be configured, etc. Heroku automates all of this so that you can focus on developing a great web application. Some other platforms similar to Heroku that are worth checking out are:

  • AWS
  • Netlify
  • Dokku
  • Vercel

Table of Contents

Prepare Django for production

First you need to create an account with Heroku and then click Create new app. This will set up a Heroku project with a Heroku provided URL.

Before you can upload Django to Heroku, you must prepare it for the production environment. Django includes a very handy command shown below:

manage.py check --deploy

Depending on your setup you may need to add python or python3 in front of the above command.

This command will automatically evaluate your Django application and discover many of the common issues.

Some of the most common settings and issues are listed:

  • SECRET_KEY setting
  • Set DEBUG to False
  • Add Heroku's provided URL to ALLOWED_HOSTS
  • Define a STATIC_ROOT setting
  • Set CSRF_COOKIE_SECURE and SESSION_COOKIE_SECURE to true

All of these settings should be adjusted in your settings.py folder. If the setting is not already in settings.py you should add it.

One setting that Django will most likely recommend turning on is the SECURE_SSL_REDIRECT setting. This setting redirects all non-HTTPS requests to HTTPS to increase security. However, this causes issues with Heroku as all traffic within the Heroku network is HTTP.

As a result, you need to tell Django to check the X-FORWARDED_PROTO header to determine if the connection that originally hit the Heroku proxy was HTTPS:

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

The formal Django production checklist can be found here.

If you encounter any other issues or settings, Django's documentation is the best place to start.

Set up Procfile

A Procfile is Heroku's method for defining instructions on how to run the deployed application. Heroku "should" automatically run the necessary commands to start up your web application after the build process.

However, in actuality this does not always work. It is best practice to create a Procfile to avoid any potential issues.

Before creating your Procfile, you want to install and set up Gunicorn. Gunicorn allows your Django application to process incoming HTTP requests concurrently instead of using Django's default blocking servers.

pip install gunicorn

In the root directory of your application (same location as .gitignore and README) create a file called Procfile with the following code:

web: gunicorn application_name.wsgi

Note that application_name should be whatever you have named your Django application. This will be the name of the directory that holds the wsgi.py file.

Ensure that this file is not included in .gitignore.

Create requirements.txt

In order to build your web application, Heroku needs to know what Python packages to install. This is where the requirements.txt file is used.

Every Python package and version used by your application needs to be listed in this file. It should look similar to the below:

appdirs==1.4.4
asgiref==3.4.1
backports.zoneinfo==0.2.1
Bottleneck==1.3.5
certifi==2020.6.20
distlib==0.3.2
Django==3.2.5
...

Hopefully you have been using a virtual environment with Pip. If you have a .venv folder in the root folder of your Django application then you have been.

Creating a requirments.txt file is as simple as entering the below command into your terminal:

pip freeze > requirements.txt

Or if that does not work try the below:

pip3 freeze > requirements.txt

This command will automatically create the requirements.txt file with whatever packages are in your virtual environment.

Ensure this file is not included in .gitignore.

If you have been using Anaconda to install packages you can try running the below command:

pip list --format=freeze > requirements.txt

More information can be found here. I use Pip and cannot speak to how effective these techniques are.

runtime.txt

This file should be added to your root folder. It specifies exactly which version of Python Heroku should use for your web application.

Run the below code to discover which version you are using:

python --version

or this command if the previous does not work:

python3 --version

Simply type the outputted version into runtime.txt using the following format:

python-3.9.7

Ensure this file is not included in .gitignore.

Set up AWS S3 (Optional)

Amazon's Simple Storage Service (S3) is ideal for hosting static files such as images. This service is invaluable with Heroku for two reasons:

  1. Heroku has a hard limit on your final build size (500 MB)
  2. Heroku uses an ephemeral filesystem

If your application has no images feel free to continue to the next section. In addition, if you believe your build size will be below 500 MB even with the images included skip to the next section. If your build size ends up being too large you can always come back and move your images to S3.

If you do not want to create an AWS account, Heroku's documentation recommends the Bucketeer add-on. I have not used this add-on and cannot speak to its effectiveness.

In order to use S3 you will need to create an AWS account. The process is straight-forward and well-documented. Check out this guide to get started.

After creating your primary AWS account, you will want to create an IAM user. IAM user's have restricted access and unique credentials which decreases potential security concerns.

The IAM user creation process is detailed below:

  1. From the search bar at the top of the AWS console search for "IAM"
  2. Select "Users" on the left-hand side
  3. Click "Add users"
  4. Enter a username (name of your site is a decent option)
  5. Select "Access key - Programmatic access" and then click "Next"
  6. Click "Attach existing policies directly" and enter "S3" into the search bar
  7. Select "AmazonS3FullAccess" and then click "Next"
  8. Skip the "Tags" section and then click "Create user"
  9. Copy the Access key ID and the Secret access key to your Config Vars under Heroku's settings as shown below
AWS_ACCESS_KEY_ID => Access key ID
AWS_SECRET_ACCESS_KEY => Secret access key

If you need additional help creating a user this guide provides more information.

Now you will need to create your S3 bucket. This bucket will host your images and any other static files you would like to store in it.

The S3 bucket creation process is detailed below:

  1. From the search bar at the top of the AWS console search for S3
  2. Click Create bucket at the top
  3. Give it a name that follows the rules for bucket naming
  4. Select the AWS region that closest matches your Heroku region
  5. Leave the remaining settings on their defaults and click Create bucket
  6. Add the bucket name to Heroku's Config Vars
S3_BUCKET_NAME => your-bucket-name

Now you can upload your image files to S3 and remove them from your web application's files. If you click on your image in S3 a detailed view will pop up. One of the values will be Object URL which is what you should use for your images' src values in your code.

If you need additional help setting up a S3 bucket and integrating it with Heroku this guide provides more information.

Set up Postgres add-on

The final step before deploying is setting up your Postgres database. This will be done through the Heroku Postgres add-on. From the Heroku dashboard install the Heroku Postgres add-on.

After installation, check your Config Vars for a new DATABASE_URL variable. This is the variable that will be used to connect to your database in Django settings. You should not hard code the database credentials into Config Var as these can sometimes change.

DATABASE_URL will automatically stay up-to-date and keep you connected even if the database credentials change.

Before your Django web application can connect to your Heroku Postgres database there are some settings you need to adjust.

First you will need to install the following packages:

pip install dj-database-url
pip install whitenoise

Here is the Whitenoise documentation to ensure you properly set it up.

Then you should ensure that your settings.py file reflects this example from Heroku.

Connect Heroku to Github

From your app's homepage on Heroku click Deploy at the top. This will open all deployment settings for your application. We recommend that you use Heroku Git or GitHub.

If your code is already stored on Github, then the Github integration option will be easiest. This guide details the process.

If you would like to use the Heroku CLI, this guide can walk you through the setup process.

Regardless of which option you select, once you push your code to Heroku the build process should begin. You can follow the progress by navigating to the the Overview tab and clicking View build progress on the right under Latest activity.

Allow this process to continue until you see an error or a Done: Launching... message. Then navigate back to the Overview tab. Underneath Latest activity there should now be a message Deployed.

Continue to wait until the Build in progress message changes to Build succeeded. This may take a few minutes. You should now be able to click Open app and see your web application.

If you receive an Application error when attempting to view your web application check your logs. Heroku's logs are typically very informative and there are many resources online to sort through the various issues.

In addition, check out my Common issues section.

While Heroku is a very advanced tool, there are still a multitude of issues that can be encountered while trying to deploy your application. Be patient and do your research. The answer is out there somewhere and if not try posting a question on StackOverflow.

Common issues

This section investigates a couple of issues that frequently occur during the build process. If you encounter any issues the best place to start is the logs. These can be found in two places:

  1. Click More and View logs on the Heroku dashboard
  2. Type heroku logs --tail in Heroku CLI

requirements.txt or runtime.txt not accurate

If your requirements.txt or runtime.txt are not accurate, Heroku will not be able to build your web application. Review these files to ensure that all of the necessary packages are included and that the versions are correct.

Sometimes specific versions of packages are not supported by Heroku which can be difficult to discover. StackOverflow is a great resource to check for potential solutions.

Slug size too large

Heroku includes a 500 MB hard limit on their "slug size". The slug is a compressed and optimized version of your web application that is sent to Heroku's dyno servers. If your final slug size is larger than 500 MB you will not be able to deploy to Heroku.

There are a couple of techniques you can use to decrease your slug size:

  1. Move static files such as images and PDFs to a separate asset storage system such as S3
  2. Review requirements.txt and remove any unused packages
  3. Files that do not need to be included in your slug should be added to .slugignore (ex. test files)
  4. Purge the build cache

The below commands can be used to purge the build cache:

heroku plugins:install heroku-builds
heroku builds:cache:purge

To check the size of all of you files in your slug you can run the following commands:

heroku run bash -a appname
du -ha --max-depth 1 /app | sort -hr

If after all of this your slug size is still too large then you may need to look into other platforms such as AWS.

at=error code=H14 desc=”No web process running”

Heroku "should" automatically run the necessary commands to start up your web application after the build process. However, in actuality this does not always work. It is best practice to create a Procfile that explicitly tells Heroku how to start up your web application.

Ensure that your Procfile is in the root folder and includes the following code:

web: gunicorn application_name.wsgi

Note that application_name should be whatever you have named your Django application. This will be the name of the directory that holds the wsgi.py file.

Double check that your Procfile is not included in .gitignore or .slugignore. If you already have the Procfile included you will need to run the following command using the Heroku CLI:

heroku ps:scale web=1 --app YOUR_APP_NAME

This command ensures that a dyno is started up to serve your web application. After running this command, you should see the contents of your Procfile underneath Dyno formation on your Heroku app dashboard. In addition, there should be a green ON symbol next to it.

at=error code=H10 desc="App crashed"

This is a generic app crash message that by itself is not that useful. However, if you encounter this issue the next best step is to type the below code into the Heroku CLI:

heroku restart --app YOUR_APP_NAME

Ensure that you are watching the logs while the app restarts. It will display some messages such as State change from crashed to starting. Then it will most likely display a more informative error message indicating why the app has crashed.

Next steps

Now that your application is built and hosted on Heroku you probably want to focus on improving the performance of your website. One of the best methods you can use to improve the performance is a content delivery network (CDN).

A CDN allows fast delivery of assets required for loading your website. Images, JS files, stylesheets, etc can all take advantage of a CDN. Nearly all professional-quality websites and web applications use CDNs.

If you are interested in taking this next step, check out AWS CloudFront.

;