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.
- Log into your WebFaction console.
- Go to the "Domains/Websites" tab and click on "Websites".
- Click on "Add new website".
- Create the application:
- Select a name (use "myproject" to match the project name above).
- Create a domain.
- On "Contents" select Create a new application. Use "myproject" as name, mod_wsgi as "App category" and Python 3.5 as "App type".
- 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..
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.
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.
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.
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.