Deploying Flask applications in WebFaction

A hands-on tutorial

May 05, 2016



Webfaction is a service that provides fully managed servers and other extras for developers. I have been their client for more than 5 years now and never experienced a problem that wasn't handled swiftly. WebFaction is great for people that do not want to manage their own servers themselves but still need a ssh shell occasionally.

However, if you want to deploy a simple Flask application prepare yourself for some difficulties. Documentation is scarce on their wiki and most information out there in the wild is usually outdated. This tutorial attempts to show a simple way of setting up a Flask application on WebFaction.

It assumes the following:

  • You are already a Webfaction client.
  • Your Flask project is using a virtual environment.
  • You have ssh access to your WebFaction server.

1 - A simple Flask application

Let's start by creating a simple Flask application called myserver. This is the directory structure:

myproject/
    - src/
        - myserver.py

The content of myserver.py is enough to return a simple "Hello World!"

from flask import Flask
app = Flask(__name__)


@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

However, since you shouldn't have Flask installed outside of a virtual environment, this is what you get if you try to run it:

jventura$ cd myproject
jventura$ python src/myserver.py

Traceback (most recent call last):
  File "src/myserver.py", line 1, in <module>
    from flask import Flask
ImportError: No module named flask

Create the virtual environment in Python 3.5 and provision it with Flask:

jventura$ pyvenv-3.5 virtualenv
jventura$ . virtualenv/bin/activate
(virtualenv) jventura$ pip install flask
Collecting flask
...
...
(virtualenv) jventura$ python src/myserver.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Basically, your directory structure now looks like this:

myproject/
    - src/
        - myserver.py
    - virtualenv/
        - bin/
        - include/
        - lib/
            - python3.5/
                - site-packages/
                    - flask
                    ...

Flask will be installed on myproject/virtualenv/lib/python3.5/site-packages/.

2 - Create the site on WebFaction

Now we need to create the site using the WebFaction control panel.

  1. Log into your WebFaction console.
  2. Go to the "Domains/Websites" tab and click on "Websites".
  3. Click on "Add new website".
  4. Create the application:
    1. Select a name (use "myproject" to match the project name above).
    2. Create a domain.
    3. On "Contents" select Create a new application. Use "myproject" as name, mod_wsgi as "App category" and Python 3.5 as "App type".
  5. Click the Save buttons to save everything.

You should have an entry on your list of applications like this:

3 - Upload the project and set up virtualenv

The directory structure on your WebFaction server should look like:

~/webapps/
    - ...
    - myproject/
        - apache2/
        - htdocs/

Using a FTP client, copy the contents of your local myproject/src/ to ~/webapps/myproject/. The remote directory structure should now look like:

~/webapps/
    - ...
    - myproject/
        - apache2/
        - htdocs/
        - src/                <- We copied only the src folder
            - myserver.py        but not the development virtualenv..

Now we need to create the virtual environment on the server and install Flask. Ssh to your WebFaction account and cd into ~/webapps/myproject/.

[me@webXXX ~]$ cd ~/webapps/myproject/
[me@webXXX myproject]$ pyvenv-3.5 virtualenv
[me@webXXX myproject]$ . virtualenv/bin/activate
(virtualenv) [me@webXXX myproject]$ . virtualenv/bin/activate
(virtualenv) [me@webXXX myproject]$ pip install flask
Collecting flask
...
...
(virtualenv) [me@webXXX myproject]$ deactivate

Your project is almost ready, we just need to configure the Apache server to run it.

4 - Configure the Apache server

Using your favorite command line editor, open the myproject/apache2/conf/httpd.conf configuration file to make the following changes:

Load alias module

In the section where the Apache modules are loaded, add mod_alias:

LoadModule rewrite_module    modules/mod_rewrite.so
LoadModule setenvif_module   modules/mod_setenvif.so
LoadModule wsgi_module       modules/mod_wsgi.so
LoadModule unixd_module      modules/mod_unixd.so
LoadModule alias_module      modules/mod_alias.so           # <- Added this one

The alias module is necessary to make use of aliases below.

Modify the Directory section

Add the following to your Directory section:

<Directory /home/myusername/webapps/myproject/htdocs>
    Options +ExecCGI
    AddHandler wsgi-script .py
    RewriteEngine On                    # <- Added this
    RewriteBase /                       # <- this
    WSGIScriptReloading On              # <- and this..
</Directory>

This seems to allow hot reloading, not sure what else..

Add Aliases

At the end of the file, add the following lines:

# This will ensure that <my-domain>/static points to the flask static directory
Alias /static/ /home/myusername/webapps/myproject/src/static/

# This points to the file that launches the site
Alias / /home/myusername/webapps/myproject/htdocs/index.py/

The first one makes sure that a static/ folder inside src/ will be served by directly the Apache server. The second one makes sure that any urls for this project do not look like http://example.com/index.py/ as the index.py file will be used to bootstrap our Flask application.

As alternative to serving static content with Apache, you can create a static-only symlink app pointing at /home/myusername/webapps/myproject/src/static/ directory, and then add that app to your site using /static as the URL path. It will make the application much faster since the Apache webserver won't be doing that extra work. Check the documentation here.

5 - Configure htdocs/index.py

The index.py is the file that Apache will use to bootstrap the flask application. The httpd.conf file has two places where it references the htdocs directory, but I'm almost sure that it is the Directory section that defines the index.py file as a wsgi application.

Using a command line editor open the htdocs/index.py file. It should look something like:

import sys

def application(environ, start_response):
    output = 'Welcome to your mod_wsgi website! It uses:\n\nPython %s' % sys.version
    output += '\nWSGI version: %s' % str(environ['mod_wsgi.version'])
    output += '\nPath: ' + str(sys.path)    # Add this if you experience problems with
                                            # your project and want to test some things!

    response_headers = [
        ('Content-Length', str(len(output))),
        ('Content-Type', 'text/plain'),
    ]

    start_response('200 OK', response_headers)

    return [bytes(output, 'utf-8')]

If you point your browser to the project domain, it is the output of this file that you will see.

Before we go on check the comment above regarding sys.path. If you experience any problems you can use something like that to troubleshoot some problems. Although we added the option to auto reload the index.py file, you may want to force the restart of the Apache server (using ./apache2/bin/restart). In alternative, you can check the logs at ~/logs/user/.

Now, delete the contents of the file and write these:

import os
import sys


CURRENT_FILE = os.path.abspath(__file__)
CURRENT_DIR = os.path.dirname(CURRENT_FILE)
PROJECT_DIR = os.path.dirname(CURRENT_DIR)

# Add project top-dir to path (since it has no __init__.py)
sys.path.append(PROJECT_DIR + '/src/')

# Add virtualenv to path
sys.path.append(PROJECT_DIR + '/virtualenv/lib/python3.5/site-packages/')

# Export the Flask app object from the project as wsgi application object
from myserver import app as application

The relevant bits are the addition of the src/ folder to the system path (as we did not define it as a python package, no __init__.py file) and the addition of the virtual environment. Finally, we export the Flask app object on myserver.py as the wsgi application object.

Although we added the necessary paths to sys.path, we could have used the httpd.conf file for that. In the WSGIDaemonProcess entry we could have added, for instance, the path to the virtual environment like this:

WSGIDaemonProcess myproject processes=2 threads=12 python-path=/home/myusername/webapps/myproject/lib/python3.5:/home/myusername/webapps/myproject/virtualenv/lib/python3.5/site-packages

There is also a WSGIPythonPath that can be used to add additional directories to search for Python modules, but I prefer the current solution since it allows me to have an index.py file in my source code that I can copy to the server and play with it.

Save the file, restart the apache server, reload the url on your browser and you should now see "Hello World!". Congratulations, you have successfully set up a Flask application on WebFaction.