This guide assumes that you have got a similar server and Github account, where you push your code and your project is ready to clone - how to do this you can find here.  We will use Gunicorn - Python WSGI HTTP Server for UNIX,  Ufw - simple firewall,  Supervisor - client/server system which allows to monitor and control processes on UNIX-like systems, Nginx - HTTP server and reverse proxy, IMAP/POP3 proxy server. We are going to use it mainly for serving static files. If you want something simpler to handle statics, choose WhiteNoise.

Initial server set up

  1. Open console/terminal (on your server, not local computer). Most probably you are root user. For security sake add new user:
$ useradd username

2. Now add your new user to sudo group:

$ usermod -aG sudo username

3. Log as user you've just created:

$ su username

4. Install pip, nginx, supervisor, ufw:

$ sudo apt install python3-pip nginx supervisor ufw

Of course, you can install packages separately  by running the install with only one package: sudo apt install python3-pip etc. There is a possibility that you could have some packages already installed on your server. Mine is very minimal, only with very basic services.

5. Install virtual environment with pip:

$ pip3 install virtualenv

You could sometimes face problems with permission here. Use then '--user' switcher:

$ pip3 install virtualenv --user

6. Create a virtual environment. If you want, you can create a specific directory for your venvs. I'm going to create only one - called 'django' in  /home directory :

$ virtualenv django

7. Setup firewall. In step 4 we have installed ufw - uncomplicated firewall. Let's configure it. Open configuration file:

$ sudo nano /etc/ufw/ufw.conf

Now edit ENABLED option. Change it to 'yes' . It allows ufw to start with system. You can as well switch to 'yes' IPv6 protocol setting:


Deny incoming connections, allow for outgoing:

$ sudo ufw default deny incoming
$ sudo ufw default allow outgoing

Start ufw:

$ sudo ufw enable

More about ufw configuration you can find here: Ubuntu firewall.

Copy to server, setup Django project and its environment

8. Activate virtualenv:

$ source django/bin/activate

some shells support  '.' instead of 'source' .  In such case, activate venv with such command:

$ . django/bin/activate

9. Now you can clone your project from Github:

(django) $ git clone

Of course, you need to replace link with your repository, as well u need replace github user. You can find link to repo on GitHub after clicking Code button:


My project tree looks like below:

├── api/
├── .git/</strong>
├── media/
├── todo/
├── todowoo/
├── db.sqlite3
├── .gitignore
├── Procfile
└── requirements.txt</pre>

6. If you have got requirements.txt inside your project., install it. First, enter the project root directory (where is

(django) $ cd todowoo-project-drf

Now run the command below:

(django) ~/todowoo_project_drf$ pip install -r requirements.txt

If you don't have requirements.txt, you need to install Django and other packages manually:

(django) ~/todowoo_project_drf$ pip install Django

7. I'm using the default Django database - SQlite and all I need to do now is to make migrations:

(django) ~/todowoo_project_drf$ python migrate

If you want to use, for example, Postgres, you will need first to install it on the server. How to do this you find here: Install postgres on linux.

8. Go to the project directory where is file - in my case it is todowoo. Assuming you are now in root project directory run command:

(django) ~/todowoo_project_drf$ cd todowoo

9. If you are using or .env file, or any outer file where you keep sensitive data, create it now:

(django) ~/todowoo_project_drf/todowoo$ nano filename

Fill in your file with all needed data. For example my .env file looks like below:


Remember to save the file before exiting!

10. Open

(django) ~/todowoo_project_drf/todowoo$ nano

11. Add STATIC_ROOT setting at the end of file:

STATIC_ROOT = os.path.join(BASE_DIR, 'static')

If you haven't got ALLOWED_HOSTS and DEBUG settings in outer file, modify them now:

ALLOWED_HOSTS=your_domain, your_host_IP

Remember to save the file before exiting!

12. If you haven't already, install Gunicorn now in your venv:

(django) ~/todowoo_project_drf/todowoo$ pip install gunicorn

When this is finished, you can test Gunicorn’s ability to serve your app:

(django) ~/todowoo_project_drf/todowoo$ gunicorn --bind todowoo.wsgi

13. Collect static files (go to root project directory - where is

(django) $ cd ..
(django) ~todowoo-project-drf$ python collectstatic

14. Deactivate venv:

(django) ~todowoo-project-drf$ deactivate

Supervisor configuration

15. Create Supervisor configuration file:

$ cd ..
$ nano /etc/supervisor/conf.d/gunicorn.conf

The sample below should work fine, but if you want a more finely tuned configuration file, head up to the Supervisor Docs.

command=/path/to/envs/django_project/bin/gunicorn django_project.wsgi:application --workers 3 --bind --log-level info;
stdout_logfile = /path/to/django_project/logs/gunicorn/access.log
stderr_logfile = /path/to/django_project/logs/gunicorn/error.log
command=/home/croolic/django/bin/gunicorn todowoo.wsgi:application --workers 3 --bind --log-level info;
stdout_logfile = /home/croolic/todowoo-django-drf/logs/gunicorn/access.log
stderr_logfile = /home/croolic/todowoo-django-drf/logs/gunicorn/error.log

16. As we instructed Supervisor to save logs in logs/gunicorn directory, we have to create both of them:

$ mkdir todowoo-project-drf/logs
$ mkdir todowoo-project-drf/logs/gunicorn

17. Re-read the configuration files and update Supervisor to start it:

$ sudo supervisorctl reread
$ sudo supervisorctl update

It can also be started manually using:

$ sudo supervisorctl start django_project

Configure Nginx

18. Create new server block in Nginx’s sites-available directory:

$ sudo nano /etc/nginx/sites-available/todowoo-project-drf

In the file open up a new “server” block. We will be specifying that this block should listen on the normal port 80 and that it should respond to our server’s domain name or IP address:

server {
    listen 80;

Next, we will tell Nginx to ignore any problems with finding a favicon. We will also tell it where to find the static assets that we collected in our ~/todowoo-project-drf/static directory.

server {
    listen 80;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /path/to/django_project/django_project;
    location / {
        include proxy_params;

Save and close the file when you are done editing.

19. Enable our project by linking it to Nginx's sites-enabled directory:

$ sudo ln -s /etc/nginx/sites-available/django_project /etc/nginx/sites-enabled

If all went well, we could restart Nginx by typing:

$ sudo systemctl restart nginx

20. Finally, open up firewall to normal traffic on port 80:

$ sudo ufw allow 'Nginx Full'

That's it! Bravo and well done. The world is able to see your site.

Here's some courses you might like: