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:
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:
pip freeze > requirements.txt
Make sure your requirements.txt includes at least the following:
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:
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:
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:
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:
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:
git push heroku main
If you're using an older version of Git or have a different branch name, you might need to use:
git push heroku master
Step 5: Ensure at Least One Instance is Running
To make sure your app is running, execute:
heroku ps:scale web=1
Step 6: Open Your Application
You can open your deployed application in a web browser with:
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:
heroku logs --tail
Application Error or Crashed
If you see an "Application Error" page, check your logs for details. Common causes include:
- Missing dependencies: Make sure all required packages are in your
requirements.txt
- Incorrect Procfile: Verify that your Procfile correctly points to your application
- 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:
heroku addons:create heroku-postgresql:hobby-dev
Then update your application to use the DATABASE_URL
environment variable:
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)
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)
<!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:
web: gunicorn app:app
requirements.txt:
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
# 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:
heroku config:set SECRET_KEY="your-secret-key-here"
Access them in your Flask application:
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:
heroku domains:add www.yourdomain.com
3. Database Backups
If you're using Heroku Postgres, you can create backups:
heroku pg:backups:capture
4. Scaling
You can scale your application by adding more dynos:
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:
- Preparing your Flask application for Heroku deployment
- Setting up version control with Git
- Creating a new Heroku application
- Deploying your application to Heroku
- Configuring and connecting to a database
- Troubleshooting common deployment issues
- 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
- Deploy a simple Flask application that serves a "Hello, World!" page to Heroku.
- Modify your Flask application to use a PostgreSQL database on Heroku.
- Set up environment variables for your application's configuration settings.
- Implement a CI/CD pipeline using GitHub Actions to automatically deploy your Flask application to Heroku when you push changes to your main branch.
- 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! :)