Skip to main content

Flask Heroku Deployment

Introduction

Deploying your Flask application to a production environment is a crucial step in the web development process. Heroku is one of the most popular cloud platforms for hosting web applications due to its simplicity and developer-friendly workflow. In this tutorial, we'll walk through the process of deploying a Flask application to Heroku, from setting up your environment to launching your app live on the internet.

Heroku uses a container-based system called "dynos" to run your applications, and it supports multiple programming languages including Python. It follows a "platform as a service" (PaaS) model, which means you don't need to worry about infrastructure management - you can focus solely on your code.

Prerequisites

Before we begin, make sure you have:

  • A working Flask application
  • Python installed on your computer
  • Git installed and basic knowledge of Git commands
  • A Heroku account (sign up at heroku.com if you don't have one)
  • Heroku CLI installed on your computer

Step 1: Preparing Your Flask Application for Heroku

First, we need to add a few files that Heroku requires to properly deploy your Flask application.

Create a Procfile

Heroku needs a special file called Procfile (no file extension) that tells Heroku how to run your application. Create this file in your project's root directory:

text
web: gunicorn app:app

This tells Heroku to use Gunicorn (a production-ready WSGI server) to run your Flask application. The format is web: gunicorn filename:flask_instance_name. If your main file is named app.py and your Flask instance is named app, then you would use app:app.

Create requirements.txt

Heroku needs to know what Python packages your application depends on. You can create a requirements.txt file using pip:

bash
pip freeze > requirements.txt

Make sure your requirements.txt includes at least the following:

text
Flask==2.0.1
gunicorn==20.1.0

Add gunicorn to your dependencies

Gunicorn is a production-ready WSGI server that Heroku recommends for Python applications:

bash
pip install gunicorn

Update your application to use environment variables

Heroku sets the port for your application through an environment variable. Update your Flask application code to use this:

python
import os
from flask import Flask

app = Flask(__name__)

# Example route
@app.route('/')
def home():
return 'Hello, Heroku!'

if __name__ == '__main__':
# Get port from environment variable or choose 5000 for local development
port = int(os.environ.get("PORT", 5000))
app.run(host='0.0.0.0', port=port)

Step 2: Set Up Version Control with Git

Heroku uses Git for deploying your application. If your project isn't already under version control, initialize a Git repository:

bash
git init
git add .
git commit -m "Initial commit for Heroku deployment"

Step 3: Create a Heroku Application

Now, let's create a new Heroku application using the Heroku CLI:

bash
heroku login
heroku create your-app-name

Replace your-app-name with a unique name for your application. If you don't specify a name, Heroku will generate a random one for you.

Step 4: Deploy Your Application to Heroku

Deploy your application to Heroku by pushing your code to the Heroku Git remote:

bash
git push heroku main

If you're using an older version of Git or have a different branch name, you might need to use:

bash
git push heroku master

Step 5: Ensure at Least One Instance is Running

To make sure your app is running, execute:

bash
heroku ps:scale web=1

Step 6: Open Your Application

You can open your deployed application in a web browser with:

bash
heroku open

Or visit https://your-app-name.herokuapp.com in your browser.

Troubleshooting Common Issues

View Logs

If your application fails to start or you encounter other issues, you can view the logs:

bash
heroku logs --tail

Application Error or Crashed

If you see an "Application Error" page, check your logs for details. Common causes include:

  1. Missing dependencies: Make sure all required packages are in your requirements.txt
  2. Incorrect Procfile: Verify that your Procfile correctly points to your application
  3. Runtime errors: Your application might be crashing; check the logs for error messages

Database Configuration

If your Flask application uses a database, you'll need to configure it for Heroku. For PostgreSQL:

bash
heroku addons:create heroku-postgresql:hobby-dev

Then update your application to use the DATABASE_URL environment variable:

python
import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///local.db')
# Fix for Heroku PostgreSQL URI format change
if app.config['SQLALCHEMY_DATABASE_URI'].startswith("postgres://"):
app.config['SQLALCHEMY_DATABASE_URI'] = app.config['SQLALCHEMY_DATABASE_URI'].replace("postgres://", "postgresql://", 1)

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

Real-world Example: Deploying a Flask Todo Application

Let's see a complete example of deploying a simple Flask Todo application to Heroku.

1. Sample Todo Application (app.py)

python
import os
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# Configure database
database_url = os.environ.get('DATABASE_URL', 'sqlite:///todos.db')
# Fix for Heroku PostgreSQL URI format change
if database_url.startswith("postgres://"):
database_url = database_url.replace("postgres://", "postgresql://", 1)

app.config['SQLALCHEMY_DATABASE_URI'] = database_url
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)

# Todo model
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(200), nullable=False)
completed = db.Column(db.Boolean, default=False)

def __repr__(self):
return f'<Todo {self.id}: {self.content}>'

# Routes
@app.route('/')
def index():
todos = Todo.query.all()
return render_template('index.html', todos=todos)

@app.route('/add', methods=['POST'])
def add():
content = request.form.get('content')
if content:
new_todo = Todo(content=content)
db.session.add(new_todo)
db.session.commit()
return redirect(url_for('index'))

@app.route('/delete/<int:id>')
def delete(id):
todo = Todo.query.get_or_404(id)
db.session.delete(todo)
db.session.commit()
return redirect(url_for('index'))

@app.route('/complete/<int:id>')
def complete(id):
todo = Todo.query.get_or_404(id)
todo.completed = not todo.completed
db.session.commit()
return redirect(url_for('index'))

# Create tables
with app.app_context():
db.create_all()

if __name__ == '__main__':
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port, debug=True)

2. Templates (templates/index.html)

html
<!DOCTYPE html>
<html>
<head>
<title>Todo App</title>
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
.todo { padding: 10px; margin-bottom: 10px; background-color: #f9f9f9; border-radius: 5px; }
.completed { text-decoration: line-through; color: #888; }
form { margin-bottom: 20px; }
input[type="text"] { width: 70%; padding: 8px; }
button { padding: 8px 12px; background-color: #4CAF50; color: white; border: none; }
a { margin-left: 10px; text-decoration: none; color: #2196F3; }
</style>
</head>
<body>
<h1>Todo App</h1>

<form action="/add" method="post">
<input type="text" name="content" placeholder="Add a new todo..." required>
<button type="submit">Add</button>
</form>

<h2>Todo List</h2>
{% for todo in todos %}
<div class="todo {% if todo.completed %}completed{% endif %}">
{{ todo.content }}
<a href="/complete/{{ todo.id }}">{% if todo.completed %}Undo{% else %}Complete{% endif %}</a>
<a href="/delete/{{ todo.id }}">Delete</a>
</div>
{% else %}
<p>No todos yet. Add one above!</p>
{% endfor %}
</body>
</html>

3. Project Files

Procfile:

text
web: gunicorn app:app

requirements.txt:

text
click==8.0.1
Flask==2.0.1
Flask-SQLAlchemy==2.5.1
gunicorn==20.1.0
itsdangerous==2.0.1
Jinja2==3.0.1
MarkupSafe==2.0.1
psycopg2-binary==2.9.1
SQLAlchemy==1.4.23
Werkzeug==2.0.1

4. Deployment Steps

bash
# Initialize Git repository if not already done
git init
git add .
git commit -m "Initial commit of Todo application"

# Create Heroku app
heroku create flask-todo-example-app

# Add PostgreSQL database
heroku addons:create heroku-postgresql:hobby-dev

# Deploy to Heroku
git push heroku main

# Ensure the app is running
heroku ps:scale web=1

# Open the application
heroku open

Advanced Heroku Features

Once your application is deployed, Heroku offers several advanced features to help you manage and scale it:

1. Environment Variables

You can set environment variables for your application:

bash
heroku config:set SECRET_KEY="your-secret-key-here"

Access them in your Flask application:

python
secret_key = os.environ.get('SECRET_KEY', 'default-dev-key')
app.config['SECRET_KEY'] = secret_key

2. Custom Domains

You can add a custom domain to your Heroku application:

bash
heroku domains:add www.yourdomain.com

3. Database Backups

If you're using Heroku Postgres, you can create backups:

bash
heroku pg:backups:capture

4. Scaling

You can scale your application by adding more dynos:

bash
heroku ps:scale web=2

Summary

In this tutorial, you've learned how to deploy a Flask application to Heroku, from preparing your application to troubleshooting common issues. Here's a quick recap of what we covered:

  1. Preparing your Flask application for Heroku deployment
  2. Setting up version control with Git
  3. Creating a new Heroku application
  4. Deploying your application to Heroku
  5. Configuring and connecting to a database
  6. Troubleshooting common deployment issues
  7. Advanced Heroku features for managing your application

Deploying your Flask application to Heroku makes it accessible to users worldwide and gives you valuable experience with cloud platforms and production environments.

Additional Resources

Exercises

  1. Deploy a simple Flask application that serves a "Hello, World!" page to Heroku.
  2. Modify your Flask application to use a PostgreSQL database on Heroku.
  3. Set up environment variables for your application's configuration settings.
  4. Implement a CI/CD pipeline using GitHub Actions to automatically deploy your Flask application to Heroku when you push changes to your main branch.
  5. Add a custom domain to your Heroku application.

By completing these exercises, you'll gain practical experience with Heroku deployment and be well on your way to deploying more complex Flask applications to production environments.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)