Getting Started on Heroku with Python

Introduction

This tutorial will have y'all deploying a Python app (a simple Django app) in minutes.

Hang on for a few more minutes to learn how it all works, then you tin can make the most out of Heroku.

The tutorial assumes that you have:

  • a free Heroku account.
  • Python version 3.x installed locally - see the installation guides for OS X, Windows, and Linux.
  • Postgres installed locally, if running the app locally.

Set upwardly

The Heroku CLI requires Git, the popular version control system. If you don't already accept Git installed, complete the following before proceeding:

  • Git installation
  • First-fourth dimension Git setup

In this step you'll install the Heroku Command Line Interface (CLI). You use the CLI to manage and scale your applications, provision add-ons, view your awarding logs, and run your application locally.

Download and run the installer for your platform:

Once installed, you can use the heroku command from your command beat out.

On Windows, start the Command Prompt (cmd.exe) or Powershell to access the command shell.

Use the heroku login command to log in to the Heroku CLI:

          $ heroku login heroku: Press whatsoever key to open the browser to login or q to exit  ›   Alarm: If browser does non open up, visit  ›   https://cli-auth.heroku.com/auth/browser/*** heroku: Waiting for login... Logging in... done Logged in as me@instance.com                  

This control opens your spider web browser to the Heroku login page. If your browser is already logged in to Heroku, simply click the Log in push button displayed on the page.

This hallmark is required for both the heroku and git commands to piece of work correctly.

Prepare the app

In this step, yous volition prepare a simple application that can be deployed.

Before continuing, make sure Git is installed (see Prepare).

To clone the sample application and then that you take a local version of the code that you tin 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 performance git repository that contains a simple application, a runtime.txt specifying which Python version will be used, and a requirements.txt, which is used by Python's dependency managing director, Pip.

Deploy the app

In this stride you will deploy the app to Heroku.

Before continuing, make sure both Git and the Heroku CLI are installed (meet Set up).

Create an app on Heroku, which prepares Heroku to receive your source lawmaking:

          $ heroku create Creating app... done, ⬢ serene-caverns-82714 https://serene-caverns-82714.herokuapp.com/ | https://git.heroku.com/serene-caverns-82714.git                  

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 serene-caverns-82714) for your app, or you can pass a parameter to specify your ain app name.

Now deploy your code:

          $ git push button heroku principal Enumerating objects: 509, washed. Counting objects: 100% (509/509), done. Delta compression using upwards to 12 threads Compressing objects: 100% (238/238), washed. Writing objects: 100% (509/509), 94.fourscore KiB | 94.80 MiB/southward, done. Total 509 (delta 234), reused 509 (delta 234), pack-reused 0 remote: Compressing source files... washed. remote: Building source: remote: remote: -----> Building on the Heroku-twenty stack remote: -----> Determining which buildpack to utilise for this app remote: -----> Python app detected remote: -----> Using Python version specified in runtime.txt remote: -----> Installing python-three.10.3 remote: -----> Installing pip 21.3.1, setuptools 57.5.0 and bicycle 0.37.0 remote: -----> Installing SQLite3 remote: -----> Installing requirements with pip remote:        Collecting django remote:          Downloading Django-4.0.1-py3-none-whatever.whl (8.0 MB) remote:        Collecting gunicorn remote:          Downloading gunicorn-20.1.0-py3-none-any.whl (79 kB) remote:        Collecting django-heroku remote:          Downloading django_heroku-0.three.1-py2.py3-none-any.whl (half-dozen.2 kB) remote:        Collecting asgiref<iv,>=3.four.1 remote:          Downloading asgiref-iii.iv.1-py3-none-any.whl (25 kB) remote:        Collecting sqlparse>=0.2.2 remote:          Downloading sqlparse-0.4.ii-py3-none-whatever.whl (42 kB) remote:        Collecting psycopg2 remote:          Downloading psycopg2-two.9.3.tar.gz (380 kB) remote:          Preparing metadata (setup.py): started remote:          Preparing metadata (setup.py): finished with condition 'done' remote:        Collecting dj-database-url>=0.five.0 remote:          Downloading dj_database_url-0.5.0-py2.py3-none-any.whl (5.v kB) remote:        Collecting whitenoise remote:          Downloading whitenoise-five.3.0-py2.py3-none-any.whl (19 kB) remote:        Building wheels for collected packages: psycopg2 remote:          Building wheel for psycopg2 (setup.py): started remote:          Building wheel for psycopg2 (setup.py): finished with condition 'done' remote:          Created bicycle for psycopg2: filename=psycopg2-ii.9.three-cp310-cp310-linux_x86_64.whl size=586593 sha256=25ea6b40570d00fb7329c9691e557245a1050857323893e93068f0f07ecef238 remote:          Stored in directory: /tmp/pip-ephem-wheel-enshroud-d5wzowyy/wheels/81/b6/3d/091aad3e8919ea76c84c2674b02ce3ab52de882e091c39249e remote:        Successfully congenital psycopg2 remote:        Installing collected packages: sqlparse, asgiref, whitenoise, psycopg2, django, dj-database-url, gunicorn, django-heroku remote:        Successfully installed asgiref-3.four.1 dj-database-url-0.5.0 django-4.0.1 django-heroku-0.iii.i gunicorn-20.1.0 psycopg2-2.9.three sqlparse-0.4.2 whitenoise-v.3.0 remote: -----> $ python manage.py collectstatic --noinput remote:        129 static files copied to '/tmp/build_a5ccf75d/staticfiles', 379 mail service-candy. remote: remote: -----> Discovering process types remote:        Procfile declares types -> web remote: remote: -----> Compressing... remote:        Done: 68.1M remote: -----> Launching... remote:        Released v5 remote:        https://serene-caverns-82714.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy... done. To https://git.heroku.com/serene-caverns-82714.git  * [new branch]      revert-to-requirements -> chief                  

The application is now deployed. Ensure that at least ane case of the app is running:

          $ heroku ps:calibration web=i                  

If when running the ps:scale command above you lot see the mistake "Couldn't find that process type (web)", it ways the kickoff deploy of the awarding is taking a bit longer than normal, and the command should be repeated afterwards waiting a few minutes.

Now visit the app at the URL generated by its app proper noun. Equally a handy shortcut, you lot can open the website as follows:

          $ heroku open                  

View logs

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 almost your running app using one of the logging commands, heroku logs --tail:

          $ heroku logs --tail 2022-01-19T16:49:40.323576+00:00 heroku[web.ane]: Starting process with command `gunicorn gettingstarted.wsgi` 2022-01-19T16:49:41.952853+00:00 app[web.1]: [2022-01-19 16:49:41 +0000] [4] [INFO] Starting gunicorn xx.1.0 2022-01-19T16:49:41.953307+00:00 app[web.1]: [2022-01-19 16:49:41 +0000] [4] [INFO] Listening at: http://0.0.0.0:5021 (4) 2022-01-19T16:49:41.954552+00:00 app[spider web.1]: [2022-01-19 16:49:41 +0000] [iv] [INFO] Using worker: sync 2022-01-19T16:49:41.962918+00:00 app[web.1]: [2022-01-19 xvi:49:41 +0000] [9] [INFO] Booting worker with pid: nine 2022-01-19T16:49:41.992997+00:00 app[web.1]: [2022-01-19 16:49:41 +0000] [10] [INFO] Booting worker with pid: 10 2022-01-19T16:49:42.380291+00:00 heroku[web.ane]: State changed from starting to upwards 2022-01-19T16:49:44.000000+00:00 app[api]: Build succeeded 2022-01-19T16:56:43.488176+00:00 heroku[router]: at=info method=Become path="/" host=serene-caverns-82714.herokuapp.com request_id=df14c065-0d94-45b7-b21e-6e69a0ad93e2 fwd="123.45.67.89" dyno=spider web.1 connect=0ms service=31ms status=200 bytes=7719 protocol=https                  

Visit your awarding in the browser once more, and you'll come across another log message generated.

Printing Command+C to stop streaming the logs.

Define a Procfile

Use a Procfile, a text file in the root directory of your application, to explicitly declare what command should be executed to beginning your app.

The Procfile in the example app you deployed looks like this:

          web: gunicorn gettingstarted.wsgi                  

This declares a single process type, web, and the control needed to run it. The name spider web is of import 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 comprise additional procedure types. For example, you might declare i for a background worker process that processes items off of a queue.

Microsoft Windows

The sample app has an additional Procfile for local development on Microsoft Windows, located in the file Procfile.windows. Later tutorial steps will apply this instead: it starts a different web server, i that is uniform with Windows.

          spider web: python manage.py runserver 0.0.0.0:5000                  

Scale the app

Right at present, 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.

Yous tin check how many dynos are running using the ps command:

          $ heroku ps Free dyno hours quota remaining this month: 999h 50m (99%) Free dyno usage for this app: 0h 0m (0%) For more than information on dyno sleeping and how to upgrade, meet: https://devcenter.heroku.com/manufactures/dyno-sleeping  === web (Gratis): gunicorn gettingstarted.wsgi (1) web.1: up 2022/01/xix 16:49:42 +0000 (~ 3m ago)                  

By default, your app is deployed on a costless dyno. Free dynos will sleep after a half hr of inactivity (if they don't receive any traffic). This causes a delay of a few seconds for the outset asking upon waking. Subsequent requests volition perform unremarkably. Free dynos as well swallow from a monthly, business relationship-level quota of gratis dyno hours - every bit long every bit the quota is not exhausted, all gratis apps tin can continue to run.

To avoid dyno sleeping, y'all can upgrade to a hobby or professional person dyno type as described in the Dyno Types commodity. For example, if you migrate your app to a professional dyno, y'all can easily calibration information technology 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 spider web dynos to cypher:

          $ heroku ps:calibration web=0 Scaling dynos... washed, now running web at 0:Gratuitous                  

Access the app once again by hitting refresh on the web tab, or heroku open to open information technology in a web tab. You will get an error message because yous no longer take whatever web dynos available to serve requests.

Scale it up again:

          $ heroku ps:scale web=1 Scaling dynos... washed, at present running spider web at 1:Free                  

For abuse prevention, scaling a not-free application to more than one dyno requires account verification.

Install app dependencies locally

Heroku recognizes an app as a Python app past looking for key files. Including a requirements.txt in the root directory is 1 fashion for Heroku to recognize your Python app.

The demo app you deployed already has a requirements.txt, and information technology looks something like this:

          django gunicorn django-heroku                  

The requirements.txt file lists the app dependencies together. When an app is deployed, Heroku reads this file and installs the appropriate Python dependencies using the pip install -r command.

To install the dependencies locally, we offset want to create a "Virtual Environment" (also known as a "venv") into which we can install the packages without affecting your system Python installation. To do this, run the following control:

          $ python3 -m venv venv                  

Next, the virtual environment needs to be activated.

If y'all're on a Microsoft Windows arrangement, activate the venv using:

          .\venv\Scripts\activate                  

Or if you're on a macOS/Linux system, actuate the venv using:

          source venv/bin/activate                  

For assistance with setting up a virtual surround, run into the Python documentation.

Finally, the dependencies can now exist installed into the newly created surround:

          $ pip install -r requirements.txt                  

Annotation: Postgres must be properly installed in social club for this step to work properly. If you're running Linux, the libpq-dev organisation parcel (or equivalent for your distribution) must as well be installed.

Installing the dependencies also caused several other dependencies to be installed. You can see them by using pip'due south feature listing:

          $ pip list Package         Version --------------- ------- asgiref         3.4.ane dj-database-url 0.5.0 Django          4.0.1 django-heroku   0.3.1 gunicorn        xx.1.0 pip             21.3.1 psycopg2        2.9.3 setuptools      59.5.0 sqlparse        0.iv.2 cycle           0.37.0 whitenoise      5.iii.0                  

Once dependencies are installed, you volition be ready to run your app locally.

Run the app locally

Before standing, make sure the app'due south dependencies have been installed locally.

The app is almost fix to kickoff locally. Django uses local avails, and then first, y'all'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 Heroku CLI.

If you lot're on a Microsoft Windows arrangement, run this:

          $ heroku local -f Procfile.windows                  

Or if you're on a macOS/Linux organisation, employ the default Procfile past running:

          $ heroku local                  

Your local spider web server will then start upwards:

          [OKAY] Loaded ENV .env File as KEY=VALUE Format 17:39:12 web.1   |  [2022-01-nineteen 17:39:12 +0000] [18471] [INFO] Starting gunicorn 20.1.0 17:39:12 web.ane   |  [2022-01-nineteen 17:39:12 +0000] [18471] [INFO] Listening at: http://0.0.0.0:5000 (18471) 17:39:12 spider web.1   |  [2022-01-19 17:39:12 +0000] [18471] [INFO] Using worker: sync 17:39:12 web.i   |  [2022-01-nineteen 17:39:12 +0000] [18472] [INFO] Booting worker with pid: 18472                  

If yous see "Connexion in use" errors, cheque that no other running programs are using port 5000. On macOS Monterey, Airplay Receiver runs on port 5000 and will demand to exist disabled past going to System Preferences -> Sharing and so unticking "Airplay Receiver".

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.

If you run across "Not Found" errors in your console, check that the collectstatic step in a higher place was run before starting the spider web server.

To stop the app from running locally, go back to your terminal window and press Ctrl+C to go out.

Push local changes

In this step you'll learn how to propagate a local change to the awarding through to Heroku. Equally an example, you'll modify the awarding to add an additional dependency and the lawmaking to employ it.

Add the requests package to your requirements.txt file:

          django gunicorn django-heroku requests                  

And so use pip to install the requests package via the updated requirements.txt file:

          $ pip install -r requirements.txt                  

Change hello/views.py so that information technology imports the requests module at the showtime:

          import requests                  

At present alter the index method to brand utilize of the module. Try replacing the current index method with the post-obit code:

          def index(request):     r = requests.go('https://httpbin.org/status/418')     print(r.text)     return HttpResponse('<pre>' + r.text + '</pre>')                  

Now exam once more locally.

If you're on a Microsoft Windows system, run this:

          $ heroku local -f Procfile.windows                  

Or if you lot're on a macOS/Linux organization, apply the default Procfile by running:

          $ heroku local                  

Visit your application at http://localhost:5000. Yous should now see the output of fetching http://httpbin.org/status/418, which is a lovely teapot:

                      -=[ teapot ]=-         _...._      .'  _ _ `.     | ."` ^ `". _,     \_;`"---"`|//       |       ;/       \_     _/         `"""`                  

If instead you see the fault ModuleNotFoundError: No module named 'requests', check that the steps to install the requests package above were completed successfully.

At present 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"                  

At present deploy, just as you did previously:

          $ git push heroku main                  

Finally, check that everything is working:

          $ heroku open up                  

Provision add-ons

Add together-ons are 3rd-party cloud services that provide out-of-the-box additional services for your application, from persistence through logging to monitoring and more than.

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 take written logging services that provide things such as log persistence, search, and email and SMS alerts.

In this footstep you lot will provision one of these logging add together-ons, Papertrail.

To help with corruption prevention, provisioning an add-on requires account verification. If your account has not been verified, you volition be directed to visit the verification site, to add a credit-card to your account. This credit menu will merely be charged if y'all use paid Heroku features - the default Papertrail plan is gratuitous.

Provision the papertrail logging add-on:

          $ heroku addons:create papertrail Creating papertrail on ⬢ serene-caverns-82714... free Welcome to Papertrail. Questions and ideas are welcome (technicalsupport@solarwinds.com). Happy logging! Created papertrail-convex-88929 as PAPERTRAIL_API_TOKEN Employ heroku addons:docs papertrail to view documentation                  

The add-on is now deployed and configured for your application. You can list add together-ons for your app similar so:

          $ heroku addons                  

To see this particular add-on in action, visit your awarding's Heroku URL a few times. Each visit volition generate more log messages, which should now become 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 panel, showing the latest log events. The interface lets you search and set alerts:

Screenshot of the console

Beginning a panel

You lot can run a command, typically scripts and applications that are part of your app, in a 1-off dyno using the heroku run control. It tin can likewise be used to launch a REPL process attached to your local terminal for experimenting in your app'southward environs:

          $ heroku run python manage.py shell Running python manage.py shell on ⬢ serene-caverns-82714... upwardly, run.9594 (Gratis) Python 3.10.3 (chief, Jan fourteen 2022, 23:41:42) [GCC 9.3.0] on linux Blazon "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>>                  

If you receive an fault, Error connecting to process, and so 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 case, yous will exist be able to run the following:

          >>> import requests >>> print(requests.go('https://httpbin.org/status/418').text)      -=[ teapot ]=-         _...._      .'  _ _ `.     | ."` ^ `". _,     \_;`"---"`|//       |       ;/       \_     _/         `"""`                  

To exit the Python shell, run the command 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 beat on that dyno. You can so execute commands there. Each dyno has its own imperceptible filespace, populated with your app and its dependencies - once the command completes (in this case, bash), the dyno is removed.

          $ heroku run fustigate Running bash on ⬢ serene-caverns-82714... up, run.3789 (Free) ~ $ ls app.json  gettingstarted  hi  manage.py  Procfile  Procfile.windows  README.dr.  requirements.txt  runtime.txt  staticfiles ~ $ go out exit                  

Don't forget to type exit to exit the trounce and terminate the dyno.

Ascertain config vars

Heroku lets you externalise configuration - storing data such as encryption keys or external resources addresses in config vars.

At runtime, config vars are exposed as surround variables to the awarding.

Edit hello/views.py. At the offset, add a line to import the bone module:

          import os                  

Now modify the alphabetize method and so that information technology repeats an activeness depending on the value of the TIMES environment variable:

          def index(request):     times = int(os.environ.get('TIMES', 3))     return HttpResponse('How-do-you-do! ' * times)                  

heroku local volition 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=two                  

If y'all run the app with heroku local and so visit http://localhost:5000, you lot'll run across two "Hello!"'s.

To set the config var on Heroku, execute the post-obit:

          $ heroku config:gear up TIMES=2                  

View the config vars that are gear up using heroku config:

          $ heroku config === serene-caverns-82714 Config Vars DATABASE_URL:         postgres://aadoybxpeabguh:<PASSWORD>@ec2-three-216-113-109.compute-i.amazonaws.com:5432/d8pcm30gp3rago PAPERTRAIL_API_TOKEN: <SECRET_TOKEN> TIMES:                2                  

Deploy your changed awarding to Heroku to see this in activity.

Provision a database

The add-on market has a large number of data stores, from Redis and MongoDB providers, to Postgres and MySQL. In this step you will larn nigh the free Heroku Postgres add-on that was automatically provisioned when your app was deployed.

A database is an add-on, and then y'all can find out a fiddling more virtually the database provisioned for your app using the addons command in the CLI:

          $ heroku addons Add-on                                      Plan       Price  Country ──────────────────────────────────────────  ─────────  ─────  ─────── heroku-postgresql (postgresql-cubed-97628)  hobby-dev  gratuitous   created  └─ as DATABASE ...                  

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 === serene-caverns-82714 Config Vars DATABASE_URL:         postgres://aadoybxpeabguh:<Countersign>@ec2-3-216-113-109.compute-1.amazonaws.com:5432/d8pcm30gp3rago ...                  

Heroku too provides a pg command that shows a lot more than:

          $ heroku pg === DATABASE_URL Plan:                  Hobby-dev Status:                Bachelor Connections:           0/20 PG Version:            13.5 Created:               2022-01-nineteen xvi:49 UTC Data Size:             7.ix MB/1.00 GB (In compliance) Tables:                0 Rows:                  0/10000 (In compliance) - refreshing Fork/Follow:           Unsupported Rollback:              Unsupported Continuous Protection: Off Add-on:                postgresql-cubed-97628                  

This indicates I have a hobby database (free), running Postgres 13.v, with no data.

The case app you deployed already has database functionality, which you should be able to accomplish 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.

However, accessing it now volition yield a relation "hello_greeting" does non exist mistake, considering while the database is configured, the tables take not yet been created.

To create the hello_greeting table, run the standard Django manage.py migrate command:

          $ heroku run python manage.py migrate Running python manage.py migrate on ⬢ serene-caverns-82714... upwardly, run.4231 (Costless) Arrangement bank check identified some issues:  WARNINGS: how-do-you-do.Greeting: (models.W042) Auto-created chief key used when not defining a primary key type, by default 'django.db.models.AutoField'.     HINT: Configure the DEFAULT_AUTO_FIELD setting or the AppConfig.default_auto_field attribute to point to a subclass of AutoField, eastward.g. 'django.db.models.BigAutoField'. Operations to perform:   Apply all migrations: admin, auth, contenttypes, hello, sessions Running migrations:   Applying contenttypes.0001_initial... OK ...                  

Now access the /db route again and you'll see a simple page update every time you access it:

          Folio View Report  Jan. 19, 2022, 6:12 p.m. Jan. 19, 2022, 6:17 p.m.                  

The code to access the database is straightforward, and makes use of a uncomplicated Django model chosen Greetings that yous can find in hello/models.py.

Whenever you visit the /db route of your app, the post-obit method in the hullo/views.py file is invoked which creates a new Greeting and and then renders all the existing Greetings:

          def db(request):      greeting = Greeting()     greeting.relieve()      greetings = Greeting.objects.all()      return render(request, 'db.html', {'greetings': greetings})                  

Assuming that you lot take Postgres installed locally, use the heroku pg:psql command to connect to the remote database and see all the rows:

          $ heroku pg:psql --> Connecting to postgresql-cubed-97628 psql (fourteen.1, server 13.5 (Ubuntu xiii.v-two.pgdg20.04+1)) SSL connectedness (protocol: TLSv1.3, naught: TLS_AES_256_GCM_SHA384, bits: 256, compression: off) Type "assist" for help.  serene-caverns-82714::DATABASE=>                  

Read more nearly Heroku PostgreSQL.

A like technique tin can exist used to install MongoDB or Redis add together-ons.

Next steps

You now know how to deploy an app, modify its configuration, view logs, scale, and adhere add together-ons.

Here'due south some recommended reading. The showtime, an article, will requite you a firmer understanding of the basics. The second is a pointer to the main Python category here on Dev Heart:

  • Read How Heroku Works for a technical overview of the concepts you'll encounter while writing, configuring, deploying and running applications.
  • Read Deploying Python and Django Apps on Heroku to empathize how to accept an existing Python or Django app and deploy it to Heroku.
  • Visit the Python category to learn more well-nigh developing and deploying Python applications.
  • Acquire more than about the Heroku developer experience and CI/CD features in the Heroku Enterprise Programmer Learning Journey.