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. Procfile
s 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 (ourapp.py
file) - second
app
is the Flask object created inside ofapp.py
(ourapp = 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, whenauto_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.