Skip to Content

Django local development with nginx on OSX

Posted on 3 mins read

Installing nginx using brew is quite easy:

$ brew install nginx

Post install instruction explains how to run nginx on starup by adding it’s launchd config to ~/Library/LaunchAgents/ and it will run nginx on port 8080. If you need to run nginx on port 80 you have to run it as root:

$ sudo nginx

But I want to have nginx running on port 80 when system starts. To accomplish that, I copy nginx launchd config to /Library/LaunchDaemons/ (so it runs even before any user is logged in):

$ sudo cp /usr/local/Cellar/nginx/1.0.6/org.nginx.nginx.plist /Library/LaunchDaemons/ # nginx version number may vary

And slightly edit it, so it finally looks like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
    <key>Label</key>
    <string>nginx</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>UserName</key>
    <string>root</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/Cellar/nginx/1.0.6/sbin/nginx</string>
        <string>-g</string>
        <string>daemon off;</string>
    </array>
    <key>WorkingDirectory</key>
    <string>/usr/local</string>
    </dict>
</plist>

I set a Label option to nginx, so I can easily restart it with:

$ sudo launchctl stop nginx # this will stop and start nginx again since OnDemand option is set to false by default

UserName is set to root, so nginx runs on port 80. If you need to pass some additional arguments to nginx executable, you can list them in ProgramArguments.

Further, I edit nginx config, which is located at /usr/local/etc/nginx/nginx.conf and add my generic code snippet I use everywhere for local Django development with nginx (it’s a server directive so I usually add it before the last closing bracket):

server {
    server_name ~^(?P<subdomain>\w+\.)?(?P<project_name>\w+)\.dev$;
    access_log off;
    gzip off;
    set $home_dir /Users/foomor;
    set $project_path $home_dir/Projects/$project_name;
    set $python_version 2.7;
    set $virtualenv_path $home_dir/.virtualenvs/$project_name/lib/python$python_version/site-packages;

    location ~ ^/(static|media)/(.*)$ {
        alias $project_path/$1/$2;
    }

    location ^~ /static/admin {
        alias $virtualenv_path/django/contrib/admin/media;
    }

    location /__debug__/m {
        alias $virtualenv_path/debug_toolbar/media/debug_toolbar;
    }

    location /favicon.ico {
        alias $project_path/static/img/favicon.png;
    }

    location / {
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:8000;
        client_max_body_size 100m;
    }
}

I have a personal development convention, that all local Django projects are located within ~/Projects directory, virtual environments are located within ~/.virtualenvs and virtual environment name is similar to it’s corresponding project name. Project assets are located within static directory in the project root and uploaded files are located within media. In addition to above, nginx also serves django admin static files, django-debug-toolbar and favicon alias. Logging access is disabled until I need it. If you are going to use this config, set appropriate values to $home_dir, $project_path, $python_version and $virtualenv_path variables.

My local DNS transfers all requests made to *.dev to 127.0.0.1, server_name captures project name in domain name and all project static files are served with nginx. For instance I have a project named mydjangoapp and it’s development server (python manage.py runserver) is currently running, so I just open http://mydjangoapp.dev in the browser and it works.

If you do not have a local DNS that will map *.dev to 127.0.0.1, you will have do that in your /etc/hosts file.

When configuration file is ready, you can finally load nginx launchd config and it will run every time on the system startup:

$ sudo launchctl load -w /Library/LaunchDaemons/org.nginx.nginx.plist
comments powered by Disqus