Deploying Django to Heroku: Procfile, STATIC_ROOT, & Other Pitfalls
Heroku’s help wasn’t helpful enough, so I wrote this guide to get you (and me) though your first deployment.
Heroku is a great option for deploying your apps. It’s especially attractive if you want to use a relational database, because PostgreSQL is free on Heroku if you’re a hobby developer. On other cloud providers like Google Cloud or AWS, you have to pay for your databases.
That said, it took me a little while to get my barebones Django application running on Heroku. It was especially difficult because Heroku’s docs aren’t comprehensive. I had to stitch this workflow together with help from Stack Overflow.
Here’s what you need to do to go from zero to deployed on Heroku with a Django application from the command line.
Are you an experienced dev just looking for some source code? I’m happy to oblige: https://github.com/bennett39/django-heroku-example/tree/barebones
I’ll assume you’re starting with an empty directory and nothing built yet. I’ll also assume you’re on Linux.
Mac people, you’ll have to do some translating of these commands, but the workflow is the same. Windows people, I’m sorry.
Here’s what you need to have installed:
Pyenv (not required but makes this whole thing easier)
curl https://pyenv.run | bash
Pyenv virtualenv (plugin for Pyenv)
git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv
echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bash_profile
Python 3.6.8 (Heroku’s default Python runtime)
pyenv install 3.6.8
sudo apt-get pip
pip install --upgrade pip
We’ll install other stuff as well, but these will allow us to create a virtual environment that’s clean and compartmentalized. That makes managing the dependencies Heroku sees easier.
Create Your Virtual Environment
Find a good place to save your project locally. Feel free to use whatever name you want, but for this example I’ll use the name
We’re going to make this
myproject/ directory a special virtual environment with its own Python source and clean dependency tree.
pyenv virtualenv 3.6.8 myproject
The command above creates a new virtual environment, using Python 3.6.8.
The name of that new virtualenv is “myproject” — the same as the name of the directory.
Now, we need to set the
myproject/ directory to use the myproject virtualenv:
pyenv local myproject
To confirm your setup, run the following in the
$ python --version
$ pyenv version
myproject (set by /home/user/path/to/file/.python-version)
Now that we have a clean virtual environment, we can install the modules we’ll need to build our app.
In this example, we’ll launch a Django application, so let’s get Django:
pip install django
Verify the version of Django you have is 2.1 or later:
$ python -m django --version
If so, we’re ready to create our Django project locally!
Using Django’s typical naming scheme, I’m going to call our project
mysite . You can use whatever name you want.
django-admin startproject mysite
After running that command, you’ll see a new folder named
mysite/ in your current directory.
Navigate into it and check out its contents:
$ cd mysite/ && ls -a
./ ../ db.sqlite3 manage.py* mysite/
If that’s what you see, then you’ve successfully created your first Django project. To test it out, try:
$ python manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
January 24, 2019 - 20:15:54
Django version 2.1.5, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Don’t worry about the “15 unapplied migrations” for right now. You can fix those later on once you have the database running on Heroku.
You’ll be greeted by the friendly Django startup screen:
This isn’t a tutorial about how to build an application with Django, so we won’t do anything more with Django. That startup screen is enough.
Now, our goal is to get that same startup screen to appear at ProjectName.heroku.com
Setting Up Heroku
There are a few steps before you can start moving the Django app to Heroku.
1. Sign Up on Heroku
You’ll need a Heroku account to create a Heroku app, so visit:
Sign up for a free Heroku developer account and get started building your apps on Heroku.signup.heroku.com
2. Get Git, if you don’t have it
sudo apt-get git
First time using Git? Google some getting started guides and follow these instructions to get set up.
Learning Git is outside the scope of this guide.
3. Get the Heroku Command Line Interface
curl https://cli-assets.heroku.com/install.sh | sh
The Heroku CLI integrates with Git to allow you to track and push your local changes to your application on Heroku.
4. Initialize a Heroku Application from the Command Line
myproject/mysite/ . Verify you’re in the right place:
db.sqlite3 manage.py* mysite/
You should be in the same directory as
Now, create a new Git repository for this project:
And then create a Heroku application linked to the Git repository:
$ heroku create
Creating app... done, ⬢ nameless-tundra-24362
https://nameless-tundra-24362.herokuapp.com/ | https://git.heroku.com/nameless-tundra-24362.git
Heroku will give your application a creative name like
nameless-tundra-24632. If you want to specify a name, you can with
heroku create ProjectNameHere , but it needs to be a unique name.
Getting Ready to Deploy
Before we can push our Django app to Heroku, Heroku needs a little more information on how to run the app.
Specifically, Heroku needs 6 changes to our out-of-the-box Django app:
Gunicorn is an open-source web server for Python. It allows Heroku to deploy our application across various “workers.”
How Gunicorn works is a topic for another post, but needless to say Heroku needs it so we’ll give it to them.
In your project’s directory, run:
pip install gunicorn
This adds Gunicorn to your virtual environment so your Django project can use it.
A Procfile is something unique to Heroku. It’s a file in your project’s root directory that tells Heroku how your app should start and run.
In our case, we’ll use our Procfile to tell Heroku to run a Gunicorn server.
The good news is Django comes with out-of-the-box support for Gunicorn servers, because they both follow the conventions of WSGI.
In the Procfile, we’ll tell Heroku to start a Gunicorn server and then point that server to our Django project’s default WSGI interface.
myproject/mysite/ , run the following command to create the Procfile:
echo 'web: gunicorn mysite.wsgi --log-file -' > Procfile
If you decided to name your Django project something other than
mysite , then you’ll need to replace
Django is a pretty popular framework, so Heroku has created a module called django-heroku that helps with settings, testing, and logging automatically.
To install it, make sure you’re in
pip install django-heroku
With the module successfully installed, we can now add it to our Django project’s
settings.py . Open
At the top of
settings.py import the module. Then, at the very bottom, call it:
# All of your settings here
settings.py , but don’t close it. We have more changes to make.
4. STATIC_ROOT & PROJECT_ROOT
settings.py for an environment variable called
Next to that setting, we’ll also need to give Heroku more context about where static files (images, scripts, etc) are stored.
We’ll add two new variables,
PROJECT_ROOT , in addition to
STATIC_URL. That section of
settings.py should now look like this:
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static')
We’re almost ready to deploy our Django project!
Before we can, we need to tell Heroku about all the packages we’ve installed:
In addition, those packages have installed their own dependencies. You may not have realized it, but our little application has now installed 7 different modules under the hood that allow it to run.
How do we keep track of all the modules we’ve installed?
Easy! We created a clean virtual environment when we started the project, and we’ve used pip to install everything we need. So, pip knows what we’ve installed and can create a handy list for us.
Make sure you’re in the
myproject/mysite directory, then:
pip freeze > requirements.txt
requirements.txt to make sure it looks right. Depending when you read this tutorial, this list could change:
As you build your Django project, chances are you’ll find some other module you need. If so, don’t forget to
pip freeze > requirements.txt whenever you deploy those changes.
The final thing we need to do before deploying is tell Heroku what version of Python to use.
Heroku defaults to Python 3.6.8 right now, so if you didn’t specify you’d probably be okay.
That said, it’s best practice to be specific with the versions you’re using in your stack.
Run the following in your project’s root directory to create your
echo 'python-3.6.8' > runtime.txt
Heroku (and many other cloud service providers) uses Git to deploy versions of websites and rollback any problems.
We could deploy our application right now, but let’s clean up one thing before we do.
myproject/mysite/ and type
If you set up Git correctly earlier in this guide, you should see a file listed called
If not, find where you typed
git init earlier and in that folder remove the
.git file with
rm .git. Then go back to
myproject/mysite/ and run
git init there.
Once you’re in the right directory and alongside your
.git file, create a new file:
.gitignore with your favorite text editor and paste in the following information about files Git should ignore:
### Django ###
Now, you won’t bog down the deployment process with your log and cache files. We’ll keep Heroku nice and clean.
In the future, in Part 2 of this guide, we’ll add
db.sqlite to this list because we’ll be using a different database on Heroku.
Let’s Launch!!! Django on Heroku FTW.
Cross your fingers.
Hopefully we covered everything and this will go flawlessly.
Here’s what your
myproject/mysite/ should look like: source code.
1. Add your project’s files to Git
git add .
As long as you’re in the
myproject/mysite/ directory, this will add everything we’ve worked on — the entire Django project.
2. Commit the files to Git
git commit -am "Initialize Django project"
This will commit all the files you just added.
3. Push the files to the Heroku repository
The Heroku CLI added a remote repository to your git. Let’s push the files there:
git push heroku master
You should see the Heroku CLI respond with messages about your project’s build:
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 459 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote: -----> Python app detected
remote: -----> Installing requirements with pip
remote: -----> $ python manage.py collectstatic --noinput
remote: 119 static files copied to '/tmp/build_54c6d4edbd5faba2e2e1bb1dbd158e89/staticfiles', 375 post-processed.
remote: -----> Discovering process types
remote: Procfile declares types -> web
remote: -----> Compressing...
remote: Done: 54.4M
remote: -----> Launching...
remote: Released v6
remote: https://nameless-tundra-24362.herokuapp.com/ deployed to Heroku
remote: Verifying deploy... done.
eb3dc42..c29842c master -> master
If the build worked, open the link provided: https://nameless-tundra-24362.herokuapp.com/
IT WORKS!!! WOOHOO!!!
It’s likely you’ll find some helpful resources in the Heroku CLI’s error messages.
If the CLI builds the site, but clicking the link results in an error, then checkout
heroku logs --tail for more information about the errors you’re running into.
If you’re really stuck, comment below and I and other Djangoers will try to help out.
Ok, so now what?
We have the Django startup page working on Heroku, but there’s still a major issue.
By default, Django uses SQLite as a database. It’s a lightweight, open-source, awesome option for small to medium sized websites.
But SQLite doesn’t work great on distributed cloud computing infrastructure like Heroku.
So, we’ll need to set up a different database to use with Django on Heroku. Creating a Heroku PostgreSQL instance and connecting it to Django is the subject of a whole different post.
Thanks for reading!
Did I miss anything or mess anything up? Feel free to let me know in the comments below.