Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
setting up a stack on webfaction

Setting up Webfaction for modern Django deployment

last updated: 4/5/2011

note that this stuff is always a moving target, much of this has been cribbed and combined from various blog posts. Much of the information was out of date from those, and if it is more than a couple months after the last updated date above, consider some of this likely to now be out of date.

Getting your basic python and home settings sorted out

mkdir -p ~/bin ~/lib/python2.6/site-packages
easy_install-2.6 --prefix=$HOME pip
pip install virtualenv

edit .bashrc and/or .bash_profile such that $HOME/bin and $HOME/sbin are on your path.

nginx and uwsgi can be shared among projects and do not need to be rebuilt for each site:

cd ~
mkdir tmp src bin sbin etc conf mylogs sock

Download and make packages

cd src
curl | tar -xvz

grab upload progress module:

git clone

get uwsgi itself: I've found this latest version has compile errors on webfaction while this does not:

curl | tar -xz

build uwsgi:

cd uwsgi-x-x-x-x
make -f Makefile.Py26
cp uwsgi $HOME/bin/

uwsgi module included by default in nginx since 0.8.40 so you do not need to specify it at configure time. Webfaction has sought to make the "logs" directory in your home folder owned by root - so we need to configure alternate log destinations.

build nginx:

cd ../nginx-0.8.54/
./configure --add-module=../nginx-upload-progress-module/ \
--prefix=$HOME \
--error-log-path=$HOME/mylogs/nginx-error.log \
--lock-path=$HOME/mylogs/nginx.lock \
--pid-path=$HOME/mylogs/ \
make install

expect to see errors about deprecated sys_errlist:

Setting up the webfaction app

create custom port listening app:

cd webapps/<appname>/
virtualenv venv
source venv/bin/activate

Set up Django project

cd out to webapps/<appname> startproject test_project

get the django project settings and urls set up for admin to work

collect static files: collectstatic

Get the runtime stuff setup

get project on python path:

cd venv/<site-packages>
ln -s $HOME/webapps/<appname> .

start up nginx, note that it will not daemonize but can be started like this for testing:

nginx -p `pwd`/ -c ../test_project/etc/nginx.conf

create a file in project root:

import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'test_project.settings'

import django.core.handlers.wsgi

application = django.core.handlers.wsgi.WSGIHandler()

startup uwsgi, again, won't daemonize - you'll need two terminals to run both nginx and uwsgi in this test mode:

uwsgi -p 4 -s $HOME/sock/teststack_uwsgi.sock -H $HOME/webapps/teststack/venv/ --pythonpath $HOME/webapps/teststack/test_project -w wsgi

You should now be able to load up the Django-admin

Running the setup with supervisor

create another custom port app in webfaction control panel

install supervisor with pip install supervisor

see supervisord.conf


*/10 * * * * /home/ptone/bin/ start > /dev/null 2>&1 nohup start

#user nobody;
worker_processes 2;
# pid ptone-logs/;
# error_log ptone-logs/nginx-error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
# for running via supervisor:
daemon off;
events {
worker_connections 1024;
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 35;
gzip on;
# Directories
client_body_temp_path tmp/client_body/ 2 2;
fastcgi_temp_path tmp/fastcgi/;
proxy_temp_path tmp/proxy/;
uwsgi_temp_path tmp/uwsgi/;
# Logging
access_log log/nginx-access.log combined;
# teststack server:
include /home/ptone/webapps/teststack/test_project/etc/nginx.conf;
# another virtual host using mix of IP-, name-, and port-based configuration
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
# HTTPS server
#server {
# listen 443;
# server_name localhost;
# ssl on;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_timeout 5m;
# ssl_protocols SSLv2 SSLv3 TLSv1;
# ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
# Note that this file is located in a deploy or etc folder in the django project root
# uWSGI serving Django.
# probably not required in a non-loadbalancing scenario
# upstream django {
# Distribute requests to servers based on client IP. This keeps load
# balancing fair but consistent per-client. In this instance we're
# only using one uWGSI worker anyway.
# ip_hash;
# server unix:/home/ptone/sock/teststack_uwsgi.sock;
# }
server {
# teststack
listen 50218;
charset utf-8;
access_log mylogs/teststack.access.log combined;
# Django admin media.
location /static/admin/ {
alias /home/ptone/webapps/teststack/venv/lib/python2.6/site-packages/django/contrib/admin/media/;
# Your project's static media.
location /static/ {
alias /home/ptone/webapps/teststack/test_project/media/;
# Finally, send all non-media requests to the Django server.
location / {
# uncomment below if you are using the upstream approach
# uwsgi_pass django;
uwsgi_pass unix:/home/ptone/sock/teststack_uwsgi.sock;
include uwsgi_params;
#! /bin/bash
OPTS="-c /home/ptone/etc/supervisord.conf"
test -x $SUPERVISORD || exit 0
export PATH="${PATH:+$PATH:}/usr/local/bin:/usr/sbin:/sbin:/home/ptone/bin:/home/ptone/sbin:"
if [ $PID -gt 0 ]; then
return 1
return 0
pidof_daemon() {
PIDS=`pidof -x $PS` || true
[ -e $PIDFILE ] && PIDS2=`cat $PIDFILE`
for i in $PIDS; do
if [ "$i" = "$PIDS2" ]; then
return 1
return 0
start () {
echo "Starting Supervisor daemon manager..."
if [ "${isAlive}" -eq $TRUE ]; then
echo "Supervisor is already running."
$SUPERVISORD $OPTS || echo "Failed...!"
echo "OK"
stop () {
echo "Stopping Supervisor daemon manager..."
$SUPERVISORCTL shutdown || echo "Failed...!"
echo "OK"
case "$1" in
exit 0
[inet_http_server] ; inet (TCP) server setings
port= ; (ip_address:port specifier, *:port for all iface)
username=<user> ; (default is no username (open server))
password=<pass> ; (default is no password (open server))
logfile=/home/ptone/webapps/supervisor/logs/user/supervisor/supervisord.log ; (main log file ; default $CWD/supervisord.log)
logfile_maxbytes=20MB ; (max main logfile bytes b4 rotation ; default 50MB)
logfile_backups=10 ; (num of main logfile rotation backups ; default 10)
loglevel=debug ; (log level ; default info ; others: debug,warn,trace)
pidfile=/home/ptone/etc/ ; (supervisord pidfile ; default
nodaemon=false ; (start in foreground if true ; default false)
minfds=1024 ; (min. avail startup file descriptors ; default 1024)
minprocs=200 ; (min. avail process descriptors ; default 200)
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
command=uwsgi -p 2 -s /home/ptone/sock/teststack_uwsgi.sock -H /home/ptone/webapps/teststack/venv/ --pythonpath /home/ptone/webapps/teststack/test_project -w wsgi
Copy link

adamfast commented Nov 21, 2013

This helped me out - but I'll add that my OPTS declaration needed -j $PIDFILE added at the end to make sure the pidfile was written, and in the location the script was expecting to be able to find it.

Copy link

landonlewis commented Dec 28, 2013

+1 on this being helpful.

Copy link

steppo40 commented Feb 12, 2014

Not sure about the restart command, isn't it stopping supervisor and killing all the children processes every 10 minutes prior to start again? How about the processes serving requests in the meantime?

Copy link

mindcruzer commented Apr 8, 2014

+1 excellent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment