The basic Puppet template that I use to deploy is this:

class main::my_app($server_name, $owner, $group) {
    $web_root = '/srv/http/my-app'
    $backend_root = '/usr/local/lib/my-app'
    $wsgi_path = "${backend_root}/my-app.wsgi"

    file { $backend_root:
        ensure => directory,
        owner => $owner,
        group => $group,
        mode => '0755'

    apache::vhost { $server_name:
        port => '80',
        docroot  => $web_root,
        wsgi_application_group => '%{GLOBAL}',
        wsgi_daemon_process => 'my-app',
        wsgi_process_group => 'my-app',
        wsgi_daemon_process_options => {
            home => $backend_root,
            python-path => $backend_root
        wsgi_script_aliases => { '/' => $wsgi_path }

Here, wsgi_script_aliases is the really key point, as this is the canonical way to specify the entry point for a WSGI application. The other settings here are mostly form-filling.

  • Permissions are set user-wise to allow syncing code easily from a dev machine.
  • The value of wsgi-daemon-process needs to be unique across the whole server.
  • Name the .wsgi file with the basename of the project.
  • The Flask entry point should be '' as is customary.
  • This then allows the .wsgi file to import the app directly.
  • You can touch the script file to force a code reload. This will reload all the code, equivalent to a full process kill and restart.

The contents of my-app.wsgi can just be something like this, in the case of a Flask application:

from app import app

application = app

mod_wsgi expects the name application to contain a WSGI app.