Set Up Ubuntu To Serve A Django Website Step By Step

Last modified:


This is a guide to have a website in an Ubuntu server using nginx and systemd.

Ubuntu uses systemd to manage system and service daemons and processes, we will create configuration files to:

  • create temporary files
  • manage gunicorn system service
  • manage gunicorn system socket

And lastly configuring nginx to handle requests.

We assume the following configuration to set up the server, this is a basic configuration example to follow the tutorial and be easier to customize with your own data:

  • website domain:
  • Django source code located in /home/chengue/sites/example
  • environment variable pointing to production settings: DJANGO_SETTINGS_MODULE=example.settings.production
  • Python virtual environment for the example project at: /home/chengue/.virtualenvs/example
  • Ubuntu User: chengue

At the end of the tutorial we will have:

  • nginx handling requests to
  • traffic gets redirected from non-www to www

Configuring Systemd services


To handle incoming Gunicorn requests we create a unix socket controlled by systemd. As the configuration filename should end in .socket we create the following file: /etc/systemd/system/gunicorn.socket.

Description=gunicorn socket


*systemd* will listen on this socket and start **Gunicorn** automatically in response to traffic.

This sockets will be used by Gunicorn service using the parameter --bind unix:/run/gunicorn/socket in the service file.


Then the service that use the above socket, In /etc/systemd/system/gunicorn.service:

Description=gunicorn daemon

ExecStart=/home/chengue/.virtualenvs/example/bin/gunicorn \
          --access-logfile /home/chengue/sites/logs/example.access.log \
          --error-logfile /home/chengue/sites/logs/example.error.log \
          --pid /run/gunicorn/pid   \
          --env DJANGO_SETTINGS_MODULE=example.settings.production \
          --bind unix:/run/gunicorn/socket example.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

Be sure to adjust WorkingDirectory to your application root.

Temporal file

Temporal files handled by systemd are located in /etc/tmpfiles.d/, we create /etc/tmpfiles.d/gunicorn.conf with the following content:

d /run/gunicorn 0755 chengue www-data -
www-data is the Linux group of nginx.

Web proxy

Lastly, configure the web proxy to send traffic to the Gunicorn socket. Create a virtual site in /etc/nginx/sites-available/

server {
	# redirect www to non-www
    return 301 $scheme://$request_uri;

server {

	#location /static {
	#    alias /home/chengue/sites/example/static;

	#location /media {
	#    alias /home/chengue/sites/example/media;

	location / {
		proxy_set_header Host $host;
		proxy_pass http://unix:/run/gunicorn/socket;

Now that we have our configuration as an available website in nginx, we should enable it creating a symbolic link in /etc/nginx/sites-enabled/ pointing to the above file:

sudo ln -s /etc/nginx/sites-available/


Enabling services

Enable services to autostart at boot:

systemctl enable gunicorn.socket systemctl enable nginx.service

Activate services

Manually activating services:

systemctl start gunicorn.socket systemctl start nginx


To test if Gunicorn works curl –unix-socket /run/gunicorn/socket http should retrieve an HTML from the server.


$ systemctl status gunicorn.socket
● gunicorn.socket - gunicorn socket
   Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2017-07-03 18:52:15 EDT; 2h 58min ago
   Listen: /run/gunicorn/socket (Stream)

Jul 03 18:52:15  systemd[1]: Closed gunicorn socket.
Jul 03 18:52:15  systemd[1]: Stopping gunicorn socket.
Jul 03 18:52:15  systemd[1]: Listening on gunicorn socket.
$ systemctl status gunicorn.service
   Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
   Active: active (running) since Mon 2017-07-03 18:52:15 EDT; 2h 59min ago
 Main PID: 14216 (gunicorn)
    Tasks: 2 (limit: 4915)
   Memory: 41.8M
      CPU: 5.544s
   CGroup: /system.slice/gunicorn.service
           β”œβ”€14216 /home/chengue/.virtualenvs/example/bin/python3.6 /home/chengue/.virtualenvs/example/bin/gunicorn --access
           └─14252 /home/chengue/.virtualenvs/pullgravity/bin/python3.6 /home/chengue/.virtualenvs/example/bin/gunicorn --access

Jul 03 18:52:15  systemd[1]: Started gunicorn daemon.
$ systemctl status nginx.service
   Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
  Drop-In: /etc/systemd/system/nginx.service.d
   Active: active (running) since Mon 2017-07-03 18:52:21 EDT; 3h 1min ago
     Docs: man:nginx(8)
 Main PID: 14353 (nginx)
    Tasks: 2 (limit: 4915)
   Memory: 2.8M
      CPU: 205ms
   CGroup: /system.slice/nginx.service
           β”œβ”€14353 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
           └─14357 nginx: worker process

Jul 03 18:52:21  systemd[1]: Starting A high performance web server and a reverse proxy server...
Jul 03 18:52:21  systemd[1]: Started A high performance web server and a reverse proxy server.


An useful command to see systemd logs filtered by service is journalctl, it query the systemd journal and with the -u parameter show messages for the specified systemd unit.

Using our services:

journalctl -u nginx.service journalctl -u gunicorn.service journalctl -u gunicorn.socket


The files created in this tutorial are available in this Github repository:


Marcelo Canina
I'm Marcelo Canina, a developer from Uruguay. I build websites and web-based applications from the ground up and share what I learn here.
comments powered by Disqus

How to set up Ubuntu to serve a Django website using systemd and Gunicorn.

Clutter-free software concepts.
Translations English EspaΓ±ol

Except as otherwise noted, the content of this page is licensed under CC BY-NC-ND 4.0 . Terms and Policy.

Powered by SimpleIT Hugo Theme