The Simplest Way to Deploy a Flask App

Deploy Your Flask App: Simply, Hassle-Free, and Affordably

For context, this post is being made specifically for my 4Geeks web dev students. The full-stack web course culminates with the completion of a final project that involves creating a RESTful client-server based web app. The specific technologies are Python Flask + PostgreSQL for the back-end and React for the front-end. The official 4Geeks course doesn’t get into how to actually deploy the applications so that’s what this post is for.

The three things I look for in deployment strategies are simplicity, ease of use, and affordability. Often times, the choice when deciding between deployment strategies comes down to optimizing for 2/3 of the qualities mentioned above. Which two you should optimize for depends on your use case. The use case we’ll be looking at here is the following…

  1. We need to host a Flask API
  2. We need to be able to create/access a SQL database from the Flask API
  3. We need the deployment process is to be fairly user friendly, target audience is new to software.
  4. We need the hosting to be free to almost free (This is for small portfolio projects not commercial products)

As a side note, this architecture also scales pretty well. Its not just for small projects. The distinction is that managing more traffic/compute leads to higher spending, potentially making this not as good a solution on the affordability scale.

Points 3 and 4 above direct us to optimize for ease of use, and affordability. This effectively means that we’re conciously sacrificing some simplicity in our deployment strategy. To be clear, by simplicity I mean, how difficult the system is to understand conceptually. The deployment model can be easy to use (high ease of use) but more difficult to understand (often the case with distributed cloud environments).

We’ll be using a service called Google Cloud Run(manages the Flask API) and Supabase(manages the PostgreSQL server) for hosting/managing our backends. These are both managed solutions; managed means that we’re not provisioning nor maintaining the underlying infrastructure (VMs, updates, backups, etc).

Back-end Architecture Cloud Run will handle spinning up instances of our Flask app when it receives requests. Supabase will manage our postgres database so we dont have to worry about maintenance.

Pre-requisites

Before we get started you’ll have to create two accounts…

  1. Google Clound Platform (GCP) Account
    • You will have to input a billing method but we’ll be safely within the free tier so we shouldn’t have to pay for anything. This is assuming no sudden and prolonged traffic spikes (like from DDoS attack).
    • You should also create a GCP Project. Google Cloud Platform Project
  2. Supabase Account
    • Might also have to input billing for this platform but again, we should be well inside the free tier.
    • Create new project Google Cloud Platform Project

Also you will need to have a Python 3 version installed as well as pipenv for that python version.

You need one final piece of software to complete the deployment (Google Cloud Plaform CLI). Follow the directions here to install the gcloud cli tool that we’ll use to deploy our app. Once you have the gcloud cli installed run the following command…

Minimal Flask App

In order to deploy a Flask app, well… you actually need a Flask app to deploy so lets write up a minimal example app. Go to a folder/directory where you are comfortable making a new Flask app. I usually do this from ~/projects. You can see the final result of the next few steps in this github repo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# go to ~/projects directory
cd ~/projects
# create and go to ~/projects/test-flask-deployment directory
mkdir test-flask-deployment && cd test-flask-deployment
# install python dependencies
python3 -m pipenv install Flask Flask-Migrate Flask-SQLAlchemy gunicorn psycopg2-binary
# create main.py file
touch main.py
mkdir templates
# we'll render this template as our home page
touch template/index.html

# load DATABASE_URI into our shell environment as environment variable
export "DATABASE_URI=postgresql+psycopg2://<rest-of-the-uri>"
# this URI can be found on your Supabase project by clicking the
# green connect button that is circled in red in the image above.
# notice the postgresql+psycopg2:// part here needs to be added
# manually to the URI gotten from Supabase (see image below)
echo $DATABASE_URI > .env

Supabase connection string

Copy paste this code into ~/projects/test-flask-deployment/main.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import os
from flask import Flask, render_template, request, redirect
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Integer, String
from sqlalchemy.orm import Mapped, mapped_column

# this app variable needs to be defined as is in the main.py for Google Cloud Run
app = Flask(__name__)
# define database instance
db = SQLAlchemy()
# this requires a DATABASE_URI environment variable which we'll define in a sec
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DATABASE_URI")
# library that handles migrations https://flask-migrate.readthedocs.io/en/latest/
migrate = Migrate(app, db, compare_type=True)
db.init_app(app)

# super basic user model
class User(db.Model):
    id: Mapped[int] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column(unique=True)
    name: Mapped[str] = mapped_column(String(60))

@app.route("/")
def render_home_page():
    users = User.query.all()
    # return index.html template (we'll fill this in next)
    return render_template("index.html", users=users)

# Serves as a basic handler for a create user form
@app.route("/users", methods=['POST'])
def create_user():
    name = request.form.get("name")
    email = request.form.get("email")

    user = User(name=name, email=email)
    db.session.add(user)
    db.session.commit()

    return redirect("/")

# Google Cloud Run also needs this bit of code to bootstrap the application
# https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-python-service
if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))

Now copy paste this into ~/projects/test-flask-deployment/templates/index.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Flask Test App</title>
  </head>
  <body>
    <h1>Flask Test App</h1>

    <form action="/users" method="POST">
      <label for="email-input">Email:</label>
      <input id="email-input" name="email" type="text" required />

      <label for="name-input">Name:</label>
      <input id="name-input" name="name" type="text" required />

      <button>Submit</button>
    </form>

    <h2>Users</h2>

    <!-- This is jinja2 syntax https://jinja.palletsprojects.com/en/3.1.x/ -->
    {% for user in users %}
    <p>{{ user.email }} - {{ user.name }}</p>
    {% endfor %}
  </body>
</html>

You can now start your Flask app by runnung the following commands

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# enter virtual environment (still in ~/projects/test-flask-deployment directory)
python3 -m pipenv shell
# intialize your migrations/ directory
flask --app main db init
# create the initial migration
flask --app main db migrate
# run migration on database
flask --app main db upgrade
# start the app
python main.py

You can now visit your browser http://localhost:8080 and view the app and create some test users. Now we have an app we can deploy.

Test Flask App

Deploying

At this point, if you view your postgres database on Supabase you’ll see two new tables

Test Flask postgres database

1
2
3
4
# deploy flask app to Google Cloud Run
# this command must be run in the same directory where the main.py is
# ~/projects/test-flask-deployment/main.py
gcloud run deploy
  1. When prompted for Source code location just press enter (i.e. accept default)
  2. When prompted for Service name just press enter (i.e. accept default)
  3. When prompted for region - enter 33
  4. When prompted to allow unauthenticated invocations enter y

If the deployment is successful, the cli should give you a URL similar to https://test-flask-deployment-jbegnff72a-ue.a.run.app. If you click on that link right away you’ll probably see a Service Unavailable message. This is because our production Flask app is expecting a DATABASE_URI environment variable to be defined but currently thats only defined locally.

Go to your Google Cloud Platform account and go to your Cloud Run service

Cloud Run Services

Click on your newly created service and edit it

Newly create Cloud Run Service

Finally, add your DATABASE_URI environment variable into the VARIABLES & SECRETS section as a new variable and then deploy your app again.

Cloud Run Database Environment Variable

You might have two wait a couple minutes but now your Flask app is officially deployed and in production! Try creating some test users.

Production Flask App

Hey there, welcome to my blog. My name is Elvis and I’m a web developer. This is where I like to write about software things I find interesting or think will be valuable to my web development students.