This tutorial will have you deploying a Python app (a simple Django app) in minutes.
Hang on for a few more minutes to learn how it all works, so you can make the most out of Heroku.
The tutorial assumes that you have:
pip install virtualenv.In this step you will install the Heroku Toolbelt. This provides you access to the Heroku Command Line Interface (CLI), which can be used for managing and scaling your applications and add-ons. A key part of the toolbelt is the heroku local command, which can help in running your applications locally.
Once installed, you can use the heroku command from your command shell.
Log in using the email address and password you used when creating your Heroku account:
$ heroku login Enter your Heroku credentials. Email: [email protected] Password: ...
Authenticating is required to allow both the heroku and git commands to operate.
Note that if you’re behind a firewall that requires use of a proxy to connect with external HTTP/HTTPS services, you can set the HTTP_PROXY or HTTPS_PROXY environment variables in your local development environment before running the heroku command.
In this step, you will prepare a simple application that can be deployed.
To clone the sample application so that you have a local version of the code that you can then deploy to Heroku, execute the following commands in your local command shell or terminal:
$ git clone https://github.com/heroku/python-getting-started.git $ cd python-getting-started
You now have a functioning git repository that contains a simple application as well as a requirements.txt file, which is used by Python’s dependency manager, Pip.
In this step you will deploy the app to Heroku.
Create an app on Heroku, which prepares Heroku to receive your source code:
$ heroku create Creating lit-bastion-5032 in organization heroku... done, stack is cedar-14 http://lit-bastion-5032.herokuapp.com/ | https://git.heroku.com/lit-bastion-5032.git Git remote heroku added
When you create an app, a git remote (called heroku) is also created and associated with your local git repository.
Heroku generates a random name (in this case lit-bastion-5032) for your app, or you can pass a parameter to specify your own app name.
Now deploy your code:
$ git push heroku master Counting objects: 232, done. Delta compression using up to 4 threads. Compressing objects: 100% (217/217), done. Writing objects: 100% (232/232), 29.64 KiB | 0 bytes/s, done. Total 232 (delta 118), reused 0 (delta 0) remote: Compressing source files... done. remote: Building source: remote: remote: -----> Python app detected remote: -----> Installing python-2.7.11 remote: $ pip install -r requirements.txt remote: Collecting dj-database-url==0.4.0 (from -r requirements.txt (line 1)) remote: Downloading dj-database-url-0.4.0.tar.gz remote: Collecting Django==1.9.2 (from -r requirements.txt (line 2)) remote: Downloading Django-1.9.2-py2.py3-none-any.whl (6.6MB) remote: Collecting gunicorn==19.4.5 (from -r requirements.txt (line 3)) remote: Downloading gunicorn-19.4.5-py2.py3-none-any.whl (112kB) remote: Collecting psycopg2==2.6.1 (from -r requirements.txt (line 4)) remote: Downloading psycopg2-2.6.1.tar.gz (371kB) remote: Collecting whitenoise==2.0.6 (from -r requirements.txt (line 5)) remote: Downloading whitenoise-2.0.6-py2.py3-none-any.whl remote: Installing collected packages: dj-database-url, Django, gunicorn, psycopg2, whitenoise remote: Running setup.py install for dj-database-url: started remote: Running setup.py install for dj-database-url: finished with status 'done' remote: Running setup.py install for psycopg2: started remote: Running setup.py install for psycopg2: finished with status 'done' remote: Successfully installed Django-1.9.2 dj-database-url-0.4.0 gunicorn-19.4.5 psycopg2-2.6.1 whitenoise-2.0.6 remote: remote: $ python manage.py collectstatic --noinput remote: 58 static files copied to '/app/gettingstarted/staticfiles', 58 post-processed. remote: remote: -----> Discovering process types remote: Procfile declares types -> web remote: remote: -----> Compressing... remote: Done: 39.3M remote: -----> Launching... remote: Released v4 remote: http://lit-bastion-5032.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy... done. To [email protected]:lit-bastion-5032.git * [new branch] master -> master
While the deployment is happening, you may see a syntax error during the install for gunicorn about invalid syntax for the line yield from self.wsgi.close(). That error can be ignored.
The application is now deployed. Ensure that at least one instance of the app is running:
$ heroku ps:scale web=1
Now visit the app at the URL generated by its app name. As a handy shortcut, you can open the website as follows:
$ heroku open
Heroku treats logs as streams of time-ordered events aggregated from the output streams of all your app and Heroku components, providing a single channel for all of the events.
View information about your running app using one of the logging commands, heroku logs --tail:
$ heroku logs --tail 2014-08-15T15:17:55.780361+00:00 app[web.1]: 2014-08-15 15:17:55 [2] [INFO] Listening at: http://0.0.0.0:19585 (2) 2014-08-15T15:17:55.780488+00:00 app[web.1]: 2014-08-15 15:17:55 [2] [INFO] Using worker: sync 2014-08-15T15:17:55.830489+00:00 app[web.1]: 2014-08-15 15:17:55 [7] [INFO] Booting worker with pid: 7 2014-08-15T15:17:55.779494+00:00 app[web.1]: 2014-08-15 15:17:55 [2] [INFO] Starting gunicorn 19.0.0 2014-08-15T15:17:56.321151+00:00 heroku[web.1]: State changed from starting to up 2014-08-15T15:17:57.847806+00:00 heroku[router]: at=info method=GET path="/" host=lit-bastion-5032.herokuapp.com request_id=7fd99883-20ec-43d5-8b2d-5204351cdf2d fwd="94.174.204.242" dyno=web.1 connect=1ms service=240ms status=200 bytes=679
Visit your application in the browser again, and you’ll see another log message generated.
Press Control+C to stop streaming the logs.
Use a Procfile, a text file in the root directory of your application, to explicitly declare what command should be executed to start your app.
The Procfile in the example app you deployed looks like this:
web: gunicorn gettingstarted.wsgi --log-file -
This declares a single process type, web, and the command needed to run it. The name web is important here. It declares that this process type will be attached to the HTTP routing stack of Heroku, and receive web traffic when deployed.
Procfiles can contain additional process types. For example, you might declare one for a background worker process that processes items off of a queue.
The sample app has an additional Procfile for local development on Microsoft Windows, located in the file Procfile.windows. Later tutorial steps will use this instead: it starts a different web server, one that is compatible with Windows.
web: python manage.py runserver 0.0.0.0:5000
Right now, your app is running on a single web dyno. Think of a dyno as a lightweight container that runs the command specified in the Procfile.
You can check how many dynos are running using the ps command:
$ heroku ps === web (Free): `gunicorn gettingstarted.wsgi --log-file -` web.1: starting 2014/12/11 11:59:02 (~ 1s ago)
By default, your app is deployed on a free dyno. Free dynos will sleep after a half hour of inactivity and they can be active (receiving traffic) for no more than 18 hours a day before going to sleep. If a free dyno is sleeping, and it hasn’t exceeded the 18 hours, any web request will wake it. This causes a delay of a few seconds for the first request upon waking. Subsequent requests will perform normally.
To avoid dyno sleeping, you can upgrade to a hobby or professional dyno type as described in the Dyno Types article. For example, if you migrate your app to a professional dyno, you can easily scale it by running a command telling Heroku to execute a specific number of dynos, each running your web process type.
Scaling an application on Heroku is equivalent to changing the number of dynos that are running. Scale the number of web dynos to zero:
$ heroku ps:scale web=0
Access the app again by hitting refresh on the web tab, or heroku open to open it in a web tab. You will get an error message because you no longer have any web dynos available to serve requests.
Scale it up again:
$ heroku ps:scale web=1
For abuse prevention, scaling a non-free application to more than one dyno requires account verification.
Heroku recognizes an app as a Python app by the existence of a requirements.txt file in the root directory. For your own apps, you can create one by running pip freeze.
The demo app you deployed already has a requirements.txt, and it looks something like this:
dj-database-url==0.4.0 Django==1.9.2 gunicorn==19.4.5 psycopg2==2.6.1 whitenoise==2.0.6
The requirements.txt file lists the app dependencies together with their versions. When an app is deployed, Heroku reads this file and installs the appropriate Python dependencies using the pip install -r requirements.txt command.
To do this locally, create a virtualenv for the app:
$ virtualenv venv
Then, activate the virtualenv.
If you are using Windows, run this command:
> venv\Scripts\activate.bat
If you are not using Windows, run this command:
$ source venv/bin/activate
Now run this command in your local directory to install the dependencies, preparing your system for running the app locally:
$ pip install -r requirements.txt Downloading/unpacking Django==1.9.2 (from -r requirements.txt (line 1)) Downloading Django-1.9.2.tar.gz (6.6MB): 6.6MB downloaded Running setup.py egg_info for package Django ... Successfully installed dj-database-url Django gunicorn psycopg2 whitenoise Cleaning up...
Note: Postgres must be properly installed in order for this step to work properly.
If you’re running Linux, the libpq-dev system package (or equivalent for your distribution) must also be installed.
Once dependencies are installed, you will be ready to run your app locally.
The app is almost ready to start locally. Django uses local assets, so first, you’ll need to run collectstatic:
$ python manage.py collectstatic
Respond with “yes”.
Now start your application locally using heroku local, which was installed as part of the Toolbelt.
If you’re on Microsoft Windows system, run this:
$ heroku local web -f Procfile.windows
If you’re on a Unix system, just use the default Procfile by running:
$ heroku local web
Your local web server will then start up:
11:48:19 web.1 | started with pid 36084 11:48:19 web.1 | 2014-07-17 11:48:19 [36084] [INFO] Starting gunicorn 19.0.0 11:48:19 web.1 | 2014-07-17 11:48:19 [36084] [INFO] Listening at: http://0.0.0.0:5000 (36084) 11:48:19 web.1 | 2014-07-17 11:48:19 [36084] [INFO] Using worker: sync 11:48:19 web.1 | 2014-07-17 11:48:19 [36087] [INFO] Booting worker with pid: 36087
Just like Heroku, heroku local examines the Procfile to determine what to run.
Open http://localhost:5000 with your web browser. You should see your app running locally.
To stop the app from running locally, go back to your terminal window and press Ctrl+C to exit.
In this step you’ll learn how to propagate a local change to the application through to Heroku. As an example, you’ll modify the application to add an additional dependency and the code to use it.
Modify requirements.txt to include a dependency for requests:
requests==2.9.1
Your final requirements.txt should look something like this:
dj-database-url==0.3.0 Django==1.9.1 gunicorn==19.4.5 psycopg2==2.6.1 whitenoise==2.0.6 requests==2.9.1
Modify hello/views.py so that it imports the requests module at the start:
import requests
Now modify the index method to make use of the module. Try replacing the current index method with the following code:
def index(request): r = requests.get('http://httpbin.org/status/418') print r.text return HttpResponse('<pre>' + r.text + '</pre>')
Now test locally:
$ pip install -r requirements.txt $ heroku local
Visit your application at http://localhost:5000. You should now see the output of fetching http://httpbin.org/status/418, which is a lovely teapot:
-=[ teapot ]=-
_...._
.' _ _ `.
| ."` ^ `". _,
\_;`"---"`|//
| ;/
\_ _/
`"""`
Now deploy. Almost every deploy to Heroku follows this same pattern. First, add the modified files to the local git repository:
$ git add .
Now commit the changes to the repository:
$ git commit -m "Demo"
Now deploy, just as you did previously:
$ git push heroku master
Finally, check that everything is working:
$ heroku open
Add-ons are third-party cloud services that provide out-of-the-box additional services for your application, from persistence through logging to monitoring and more.
By default, Heroku stores 1500 lines of logs from your application. However, it makes the full log stream available as a service - and several add-on providers have written logging services that provide things such as log persistence, search, and email and SMS alerts.
In this step you will provision one of these logging add-ons, Papertrail.
Provision the papertrail logging add-on:
$ heroku addons:create papertrail Adding papertrail on sharp-rain-871... done, v4 (free) Welcome to Papertrail. Questions and ideas are welcome ([email protected]). Happy logging! Use `heroku addons:docs papertrail` to view documentation.
To help with abuse prevention, provisioning an add-on requires account verification. If your account has not been verified, you will be directed to visit the verification site.
The add-on is now deployed and configured for your application. You can list add-ons for your app like so:
$ heroku addons
To see this particular add-on in action, visit your application’s Heroku URL a few times. Each visit will generate more log messages, which should now get routed to the papertrail add-on. Visit the papertrail console to see the log messages:
$ heroku addons:open papertrail
Your browser will open up a Papertrail web console, showing the latest log events. The interface lets you search and set up alerts:

You can run a command, typically scripts and applications that are part of your app, in a one-off dyno using the heroku run command. It can also be used to launch a REPL process attached to your local terminal for experimenting in your app’s environment:
$ heroku run python manage.py shell Python 2.7.5 (default, Sep 4 2013, 10:55:05) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.1.63)] on darwin Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>>
If you receive an error, Error connecting to process, then you may need to configure your firewall.
The Python shell is running in the context of your app and all its dependencies. From here you can import some of your application files. For example, you will be be able to run the following:
>>> import requests
>>> print requests.get('http://httpbin.org/status/418').text
-=[ teapot ]=-
_...._
.' _ _ `.
| ."` ^ `". _,
\_;`"---"`|//
| ;/
\_ _/
`"""`
>>> exit()
To get a real feel for how dynos work, you can create another one-off dyno and run the bash command, which opens up a shell on that dyno. You can then execute commands there. Each dyno has its own ephemeral filespace, populated with your app and its dependencies - once the command completes (in this case, bash), the dyno is removed.
$ heroku run bash Running `bash` attached to terminal... up, run.3052 ~ $ ls gettingstarted hello manage.py Procfile README.md requirements.txt runtime.txt staticfiles ~ $ exit exit
Don’t forget to type exit to exit the shell and terminate the dyno.
Heroku lets you externalise configuration - storing data such as encryption keys or external resource addresses in config vars.
At runtime, config vars are exposed as environment variables to the application.
Edit hello/views.py. At the beginning, add a line to import the os module:
import os
Now modify the index method so that it repeats an action depending on the value of the TIMES environment variable:
def index(request): times = int(os.environ.get('TIMES',3)) return HttpResponse('Hello! ' * times)
heroku local will automatically set up the environment based on the contents of the .env file in your local directory. In the top-level directory of your project there is already a .env file that has the following contents:
TIMES=2
If you run the app with heroku local, you’ll see two “Hello!”’s.
To set the config var on Heroku, execute the following:
$ heroku config:set TIMES=2
View the config vars that are set using heroku config:
$ heroku config == sharp-rain-871 Config Vars PAPERTRAIL_API_TOKEN: erdKhPeeeehIcdfY7ne TIMES: 2
Deploy your changed application to Heroku to see this in action.
The add-on marketplace has a large number of data stores, from Redis and MongoDB providers, to Postgres and MySQL. In this step you will learn about the free Heroku Postgres add-on that was automatically provisioned when your app was deployed.
A database is an add-on, and so you can find out a little more about the database provisioned for your app using the addons command in the CLI:
$ heroku addons === lit-bastion-5032 Configured Add-ons heroku-postgresql:hobby-dev HEROKU_POSTGRESQL_BLUE papertrail:choklad ...
Listing the config vars for your app will display the URL that your app is using to connect to the database, DATABASE_URL:
$ heroku config === lit-bastion-5032 Config Vars DATABASE_URL: postgres://qplhaskhqyxp:YXDPus9MrU4HglCPzjhOevee@ec2-54-204-47-58.compute-1.amazonaws.com:5432/dc9qsdhia6v1 ...
Heroku also provides a pg command that shows a lot more:
$ heroku pg == HEROKU_POSTGRESQL_BLUE_URL (DATABASE_URL) Plan: Hobby-dev Status: Available Connections: 0 PG Version: 9.3.3 Created: 2014-08-08 13:54 UTC Data Size: 6.5 MB Tables: 0 Rows: 0/10000 (In compliance) Fork/Follow: Unsupported Rollback: Unsupported
This indicates I have a hobby database (free), running Postgres 9.3.3, with a single row of data.
The example app you deployed already has database functionality, which you should be able to reach by visiting your app’s URL and appending /db. For example, if your app was deployed to https://wonderful-app-287.herokuapp.com/ then visit https://wonderful-app-287.herokuapp.com/db.
Accessing it will yield an error though, because while the database is configured, the tables have not been created. Run the standard Django manage.py migrate to create the tables.
$ heroku run python manage.py migrate Running `python manage.py migrate` attached to terminal... up, run.1059 Synchronizing apps without migrations: Creating tables... Creating table hello_greeting Running deferred SQL... Installing custom SQL... Running migrations: Rendering model states... DONE Applying contenttypes.0001_initial... OK ...
If you see a message that says, “You just installed Django’s auth system, which means you don’t have any superusers defined. Would you like to create one now?”, type no.
Now access the /db route again and you’ll see a simple page update every time you access it:
Page View Report Aug. 15, 2014, 1:14 p.m. Aug. 15, 2014, 1:18 p.m.
The code to access the database is straightforward, and makes use of a simple Django model called Greetings that you can find in hello/models.py.
The only configuration that’s necessary can be found in gettingstarted/settings.py, which sets up the database based on the environment variable DATABASE_URL:
# Parse database configuration from $DATABASE_URL import dj_database_url ... DATABASES['default'] = dj_database_url.config()
Whenever you visit the /db route of your app, the following method in the hello/views.py file is invoked which creates a new Greeting and then renders all the existing Greetings:
def db(request): greeting = Greeting() greeting.save() greetings = Greeting.objects.all() return render(request, 'db.html', {'greetings': greetings})
Assuming that you have Postgres installed locally, use the heroku pg:psql command to connect to the remote database and see all the rows:
$ heroku pg:psql heroku pg:psql psql (9.3.2, server 9.3.3) SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256) Type "help" for help. => select * from hello_greeting; id | when ----+------------------------------- 1 | 2014-08-15 13:14:30.585583+00 2 | 2014-08-15 13:15:31.731376+00 3 | 2014-08-15 13:14:38.05101+00 (3 rows) => \q
Read more about Heroku PostgreSQL.
A similar technique can be used to install MongoDB or Redis add-ons.
You now know how to deploy an app, change its configuration, view logs, scale, and attach add-ons.
Here’s some recommended reading. The first, an article, will give you a firmer understanding of the basics. The second is a pointer to the main Python category here on Dev Center: