Run a Python App

Getting an application running on Fly.io is essentially working out how to package it as a deployable image. Once packaged, it can be deployed to the Fly.io global application platform.

In this guide we’ll learn how to deploy a Python application on Fly.io.

Initial Local Setup

Make sure that Python is already installed on your computer along with a way to create virtual environments.

This allows you to run your project locally, and test that it works, before deploying it to Fly.io.

We recommend the latest supported versions of Python.

Virtual Environment

For this guide, we use venv but any of the other popular choices such as Poetry, Pipenv, or pyenv work too.

# Unix/macOS
$ mkdir hello-flask
$ cd hello-flask
$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $
# Windows
> mkdir hello-flask
> cd hello-flask
> python -m venv .venv
> .venv\Scripts\activate
(.venv) $

From this point on, the commands won’t be displayed with (.venv) $ but we assume you have your Python virtual environment activated.

Run a Flask App

In this guide we recreate and deploy this minimal Flask application to demonstrate how quickly Flask apps can be deployed to Fly.io!

You can follow the guide to recreate the app or just git clone https://github.com/fly-apps/hello-flask to get a local copy.

We assume you already have Python installed and your virtual environment is activated.

Install Flask

With your virtual environment activated, install the latest version of Flask using pip:

python -m pip install Flask

This will load Flask and all its dependency packages. You can check all of the packages with pip freeze command:

pip freeze
blinker==1.6.2
click==8.1.3
Flask==2.3.2
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.2
Werkzeug==2.3.3

If you have a local copy of hello-flask, you can install all the dependencies using python -m pip install -r requirements.txt

Create the Flask App

The hello-flask application is, as you’d expect for an example, very minimal.

Inside the hello-flask folder, create an app.py file and add the code:

# app.py
from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
@app.route('/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

Create the Template

Flask is set up to route request to a hello function which in turn passes a name variable (taken from the requests path) to a function to render the hello.html template. Create a directory named templates/ and inside, create the hello.html:

<!-- templates/hello.html -->
<!DOCTYPE html>
<html>
<title>Hello from Fly.io</title>
<link rel="stylesheet" href="{{ url_for('static', filename='main.css') }}">
<body>
    {% if name %}
        <h1>Hello, {{ name | capitalize }}!</h1>
    {% else %}
        <h1>Hello, World!</h1>
    {% endif %}
</body>
</html>

We’re using a template as it makes it easier to show what you should do with assets that aren’t the actual application.

Start the Development Server

Flask apps are run with the flask run command:

flask run
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit

or

flask --app app run
 * Serving Flask app 'app'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit

Note that flask run command works since our file is named app.py. It also works if your file is named wsgi.py, so you don’t have to use --app to tell Flask where your app is. More details here.

If your file is saved as hello.py instead of app.py, you would need to use the --app option to point to Flask where your app is:

flask --app hello run
 * Serving Flask app 'hello'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit

If you open http://127.0.0.1:5000/ in your web browser and it displays Hello, World!.

If you open, for example, http://127.0.0.1:5000/fly , it displays Hello, Fly!.

Before Deployment

A few more steps are necessary before deploying the app.

For production, the web application will be served by gunicorn. To install it, run:

python -m pip install gunicorn

And now, save all the dependencies to a requirements.txt file using:

pip freeze > requirements.txt

Now, let’s move on to deploying this app to Fly.io.

Launch your Fly App

Fly.io has its own command-line utility for managing apps, flyctl.

If not already installed, follow the instructions on the installation guide and log in to Fly.io.

Both flyctl and fly commands will work the same way.

To configure and launch the app, run fly launch and follow the wizard as follow:

fly launch
Creating app in ../flyio/hello-flask
Scanning source code
Detected a Python app
Using the following build configuration:
        Builder: paketobuildpacks/builder:base
? Choose an app name (leave blank to generate one): hello-fly-flask
? Select Organization: Fly.io (fly-io)
Some regions require a paid plan (bom, fra, maa).
See https://fly.io/plans to set up a plan.

? Choose a region for deployment: Amsterdam, Netherlands (ams)
App will use 'ams' region as primary

Created app 'hello-fly-flask' in organization 'fly-io'
Admin URL: https://fly.io/apps/hello-fly-flask
Hostname: hello-fly-flask.fly.dev
? Would you like to set up a Postgresql database now? No
? Would you like to set up an Upstash Redis database now? No
Wrote config file fly.toml
Validating ../flyio/hello-flask/fly.toml
Platform: machines
✓ Configuration is valid
We have generated a simple Procfile for you. Modify it to fit your needs and run "fly deploy" to deploy your application.

You can set a unique name for the app and choose your primary region. You can also choose to launch and attach a PostgreSQL database and/or an Upstash Redis database though we are not using either in this example.

Organizations are a way of sharing applications between Fly users. When you are asked to select an organization, there should be one with your account name, this is your personal organization. Select that.

One thing to know about the built-in Python builder (paketobuildpacks/builder:base) is that it will automatically copy over the contents of the directory to the deployable image. This is how you can move static assets such as templates and other files to your application.

The other thing to know is that it uses a Procfile to run the application. Procfiles are used on other platforms to deploy Python applications so we keep it simple. The Procfile contains instructions for starting the application. The minimal generated Procfile starts the Gunicorn server with our WSGI application:

# Modify this Procfile to fit your needs
web: gunicorn app:app

gunicorn says that the web component of the application is served by gunicorn.

{module_import}:{app_variable} as in app:app is equivalent to ‘from app import app’:

  • first app is the Python module (our app.py file)
  • second app is the Flask object created inside of app.py (our app = Flask(__name__))

Important: If your Flask object is created, for example, in hello.py instead of app.py, you should update your Procfile like this:

web: gunicorn hello:app

Inside fly.toml

The fly.toml file now contains a default configuration for deploying your app. In the process of creating that file, flyctl has also created a Fly-side application slot by the name, hello-fly-flask. If we check the fly.toml file we can verify the name in there:

# fly.toml app configuration file generated for hello-fly-flask on 2023-06-12T17:22:20+02:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#

app = "hello-fly-flask"
primary_region = "ams"

[build]
  builder = "paketobuildpacks/builder:base"

[env]
  PORT = "8080"

[http_service]
  internal_port = 8080
  force_https = true
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 0

The fly command will always refer to this file in the current directory if it exists, specifically for the app name/value at the start. That name will be used to identify the application to the Fly service. The rest of the file contains settings to be applied to the application when it deploys:

  • primary_region: it configures where the primary region is, used to create new Machines;
  • internal_port: it configures which port the application will use to communicate with clients;
  • force_https = true: it enforces HTTP to HTTPS redirects;
  • auto_start_machines = true: it enables starting Machines automatically based on requests and capacity;
  • auto_stop_machines = true: it enables stopping Machines automatically when the app is idle for several minutes;
  • min_machines_running = 0: it sets the number of Machines to keep running, in the primary region only, when auto_stop_machines = true.

Deploy to Fly.io

We are now ready to deploy our app to Fly.io. At the command line, run:

fly deploy

This will lookup our fly.toml file, and get the app name hello-fly-flask from there. Then flyctl will start the process of deploying our application to Fly.io. flyctl will return you to the command line when it’s done.

Once complete, visit your app with the following command:

fly apps open

View the Deployed App

Now the application has been deployed, let’s find out more about its deployment. The command fly status will give you all the essential details.

fly status
App
  Name     = hello-fly-flask
  Owner    = fly-io
  Hostname = hello-fly-flask.fly.dev
  Image    = hello-fly-flask:deployment-01H2R4E4ABT72E5FVVMP0C0588
  Platform = machines

Machines
PROCESS ID              VERSION REGION  STATE   CHECKS  LAST UPDATED
app     1781903a919e98  1       ams     started         2023-08-31T03:50:27Z
app     e28673dfd42186  1       ams     started         2023-08-29T14:14:34Z

The application has been assigned with a DNS hostname of hello-fly-flask.fly.dev, and two instances are running in Amsterdam. Your deployment’s name will, of course, be different.

Connecting to the App

The quickest way to connect to your deployed app is with the fly apps open command. This will open a browser on the HTTP version of the site. That will automatically be upgraded to an HTTPS secured connection (for the fly.dev domain).

To specify a path, add /name to fly apps open and it’ll be appended to the URL as the path and you’ll get an extra greeting from the hello-fly-flask app:

fly apps open /fly

Congrats! You have successfully built, deployed, and connected to your first Flask application on Fly.io.