Last updated:

Deployment using Passenger WSGI (as used on several shared hosting providers such as Dreamhost) is fairly straightforward, once you have a python3 environment working. However, on some web hosts, setting up python3 isn’t quite obvious.

Building Python 3

First you need a Python 3 environment. On most shared hosting providers you can create one by using ssh to log in to your shell account and then downloaded the Python source distribution. For example, for version 3.7.0, you’d do:

tar xzvf Python-3.7.0.tgz

Then building it is fairly straightforward:

cd Python-3.7.0
./configure --prefix=$HOME/opt/python-3.7.0 --enable-optimizations
nice -19 make build_all
make install

The nice -19 is to reduce the chances that Dreamhost’s process killer kicks in for the build, and build_all builds Python without building unit tests.

At this point you’ll want to add Python 3 to the environment, by adding the following lines to your login script (usually ~/.bash_profile):

# python3
export PATH=$HOME/opt/python-3.7.0/bin:$HOME/.local/bin:$PATH

Log out and back in (or run the export line directly) and you should now have Python 3.7.0 on your path. You can verify this by typing

python3 --version
pip3 --version

If all worked well, you can now install pipenv:

pip3 install --user pipenv

and, optionally, add this line to your ~/.bash_profile to get better shell tab completion:

eval "$(pipenv --completion)"

Set up the virtual environment

Now it’s time to set up your virtual environment for Publ. Again, ssh to your webhost and do the following:

cd (website_directory)
pipenv --three
pipenv install Publ

This sets up the virtual environment and installs Publ and its dependencies. Now you need to write a script that tells Passenger how to run it. A fuller example is in the files for this site but a minimal version is below:

import sys
import os
import subprocess

# hack to keep click happy
os.environ['LANG'] = 'C.UTF-8'
os.environ['LC_ALL'] = 'C.UTF-8'

INTERP = subprocess.check_output(
    ['pipenv', 'run', 'which', 'python3']).strip().decode('utf-8')
if sys.executable != INTERP:
    os.execl(INTERP, INTERP, *sys.argv)


from main import app as application

Configure the website

Once you get your files on your server, it’s generally a matter of telling Passenger where to find the files. Generally, this involves going to your web host’s configuration panel and configuring your website to use Passenger. Different hosts may have different requirements. Known configurations are below:


Configure your web domain as follows:

  • Remove WWW from URL
  • Web directory: /home/(username)/(site file directory)/public
  • HTTPS (via LetsEncrypt): Yes
  • Passenger (Ruby/NodeJS/Python apps only): Yes

You will also need to have a public directory, ideally with a symbolic link to your static directory inside of it; you can set this up by logging in and doing something like:

cd (website_directory)
mkdir -p public
cd public
ln -s ../static .

You will also probably want to create a .htaccess file under public/ with the following contents:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*\.php)$ /$1.PUBL_PATHALIAS [L]

Care and feeding

Upgrading Publ should just be a matter of sshing to your host, cding into the site directory, and running:

pipenv update

After doing this, or after changing any site templates, you’ll want to restart your website. On Dreamhost you do this with:

cd (website_directory)
mkdir -p tmp    # only necessary the first time
touch tmp/restart.txt