Running Flask on macOS with mod_wsgi/wsgi-express

18 minute read

Flask is a (micro) web development Framework for Python. It is fairly simple to get started. All you need to do is to pip install Flask into your virtualenv, give the FLASK_APP environment variable your file and run flask run (described in detail in installation and quick start). This will launch the development server and you can instantly start hacking around.

When you want to use your Apache webserver, however, you need to install and configure a WSGI module. When I first wanted to do this I tried to install mod_wsgi via brew (brew install mod_wsgi from the homebrew/apache tap), but quickly ran into some (apparently common) issues with the XCode toolchain. Then I discovered that there is a much easier way of installing mod_wsgi as a Python package.

On the PyPI page it says…

[it] will compile not only the Apache module for mod_wsgi, but will also install a Python module and admin script for starting up a standalone instance of Apache directly from the command line with an auto generated configuration.

Let’s try it out

At this point I assume you have virtualenv and the XCode cli tools (xcode-select --install) installed (and of course the standard Apache from macOS). Everything else we will do together in the following steps.

Setting up virtualenv

Let’s start by creating the directory for the application and setting up our virtual environment:

$ mkdir my_app
$ cd my_app/
$ virtualenv venv
New python executable in /your/path/my_app/venv/bin/python2.7
Also creating executable in /your/path/my_app/venv/bin/python
Installing setuptools, pip, wheel...done.

Now we activate our environment:

$ source venv/bin/activate
(venv) $ # <- new prompt

Installing mod_wsgi

Installing mod_wsgi is now easily done via pip:

(venv) $ pip install mod_wsgi

Let’s see if it worked by launching the server:

(venv) $ mod_wsgi-express start-server
Server URL         : http://localhost:8000/
Server Root        : /tmp/mod_wsgi-localhost:8000:501
Server Conf        : /tmp/mod_wsgi-localhost:8000:501/httpd.conf
Error Log File     : /tmp/mod_wsgi-localhost:8000:501/error_log (warn)
Request Capacity   : 5 (1 process * 5 threads)
Request Timeout    : 60 (seconds)
Startup Timeout    : 15 (seconds)
Queue Backlog      : 100 (connections)
Queue Timeout      : 45 (seconds)
Server Capacity    : 20 (event/worker), 20 (prefork)
Server Backlog     : 500 (connections)
Locale Setting     : de_DE.UTF-8

By pointing your browser to http://localhost:8000/, you should be greeted by this page:

mod_wsgi test page

Looks good. Let’s stop the server with ctrl-c.

Installing Flask and creating the web app

If you already have your Flask app, you can skip the next few commands, but for the sake of completeness, let’s install Flask and create a small sample web app (make sure you are still in your virtualenv):

(venv) $ pip install Flask

Let’s create another directory to put the code of our web application in, and fire up an editor for creating the code file:

(venv) $ mkdir my_app 
(venv) $ vim my_app/__init__.py # <- choose the cli editor you want or just create the file with a gui editor 

Paste this code (from the Quick Start tutorial) into your file:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

Creating the wsgi script

To let the server know about our application, let’s create the wsgi script which we will later point to when starting the server.

(venv) $ pwd
/your/path/my_app # <- we are here
(venv) $ vim my_app.wsgi # <- choose the cli editor you want or just create the file with a gui editor 

Paste this code into the new file:

from my_app import app as application

UPDATE: I initially set sys.path.insert(0,"/your/path/my_app/") in the snippet above. This is not needed, as the directory you run mod_wsgi-express setup-server or mod_wsgi-express start-server in is added to sys.path automatically. See these three tweets.

Launching the server with the wsgi script

(venv) $ mod_wsgi-express start-server my_app.wsgi

By going to http://localhost:8000/ you should now see the “Hello, World!” from our Flask app.

Doesn’t work for you? Didn’t work for me at first either as I had a typo in the code 🙂. Check out the error logs and you will see a hint of what might be wrong: /tmp/mod_wsgi-localhost:8000:501/error_log.

Running the server in the background

If you want to continue with your shell session, but leave the server running, you can use nohup.

(venv) $ nohup mod_wsgi-express start-server my_app.wsgi &

This will leave the server running, while you can continue working in the session (output will go to the file nohup.out). To bring it back to the foreground, use fg.

UPDATE: Graham Dumpleton reached out to me and noted that the better way of running it in the background is by generating scripts via setup-server and then use apachectl to start/stop. So you might want to execute the following (as root) instead of using nohup:

(feel free to change the port)

mod_wsgi-express setup-server my_app.wsgi --port=8000 --user _www --group _www --server-root=/etc/mod_wsgi-express-8000

And then control the server state via:

/etc/mod_wsgi-express-8000/apachectl start
/etc/mod_wsgi-express-8000/apachectl stop

Watching for changes

When you develop your app and don’t want to reboot the server for each change, there is a convenient way of starting the server with the --reload-on-changes option. Now you can change your files and have the changes immediately served.

(venv) $ mod_wsgi-express start-server --reload-on-changes my_app.wsgi

More info

You can find more info about mod_wsgi (e.g. running it on a privileged port as root) on the PyPI page: mod_wsgi.

The command documentation is available here:

(venv) $ mod_wsgi-express start-server --help

Like to comment? Feel free to send me an email or reach out on Twitter.

Did this or another article help you? If you like and can afford it, you can buy me a coffee (3 EUR) ☕️ to support me in writing more posts. In case you would like to contribute more or I helped you directly via email or coding/troubleshooting session, you can opt to give a higher amount through the following links or adjust the quantity: 50 EUR, 100 EUR, 500 EUR. All links redirect to Stripe.