Django is a python framework used for developing dynamic websites and applications. It follows the MVC (Model-View-Controller) architecture. Using Django speeds up the development process as most of the underlying tasks are handled by it.

This tutorial will teach you how to install the Django framework on a Ubuntu 22.04 server. You will also create a demo project and test it out.

Prerequisites

  • A server running Ubuntu 22.04.

  • A non-root sudo user.

  • Make sure everything is updated.

    $ sudo apt update 
    $ sudo apt upgrade
    

Install Django

There are several methods by which you can install Django. Deciding which works for you best depends on how you want to configure your development environment and your needs. Each method has its own set of pros and cons. Let us go through all these methods.

Install from Ubuntu Repositories

The simplest method to install Django is using Ubuntu’s repositories. Ubuntu 22.04 ships with Python 3.10. You can verify it using the following command.

$ python3 -V
Python 3.10.4

Install Django using the following command.

$ sudo apt install python3-django

Run the following command to verify the successful install.

$ django-admin --version
3.2.12

The Django version shipped with Ubuntu is the current LTS version which is supported till 2024. However, it is not the current version. If you want to install a recent release, this method is not for you.

Install using pip

This is the most common way to install Django. The recommended way to do it is by creating a virtual Python environment. This allows you to install Python packages without affecting the system.

Install pip and venv module. venv is used to create Python virtual environments and pip install Python packages.

$ sudo apt install python3-pip python3-venv

Let us create a demo project directory.

$ mkdir ~/sampleproject
$ cd ~/sampleproject

Create a virtual environment using the following command. Replace sample_env with the name, you want to call your virtual environment.

$ python3 -m venv sample_env

This installs a portable version of Python, pip within your project directory. To install any packages into the project, you must activate the environment using the following command.

$ source sample_env/bin/activate

Your shell prompt will change to reflect the virtual environment.

(sample_env) [email protected]:~/sampleproject$

Now that the virtual environment is activated, use pip to install Django. Run the following command to install Django.

(sample_env) $ pip install django

Verify the installation.

(sample_env) $ django-admin --version
4.0.5

Depending upon your project requirements, you can install a different version of Django in the following way.

(sample_env) $ pip install django==3.2.1

Verify the installation.

(sample_env) $ django-admin --version
3.2.1

As you can see, this method installs a newer version of Django than the version obtained from the Ubuntu repository.

To exit the virtual environment, run the following command.

(sample_env) $ deactivate

Install the development version

You can also use pip to install the development version of Django. For this, we will grab the development version from Django’s Github repository.

Clone the repository to the ~/django-dev directory using the following command.

$ git clone https://github.com/django/django ~/django-dev

Switch to the newly created directory.

$ cd ~/django-dev

Create the virtual environment.

$ python3 -m venv dev_django_env

Activate the environment.

$ source dev_django_env/bin/activate

Install Django using pip. The -e flag installs it in an editable mode which is required if you are installing from version control.

(dev_django_dev) $ pip install -e ~/django-dev

Verify the installation.

(dev_django_dev) $ django-admin --version
4.2.dev20220628195651

As you can see, the version here is the latest development version. The development version of Django is not useful for production environments.

Create a Sample Project

Let us build a sample Django project. Create a directory for the sample project.

$ mkdir ~/dj-sample
$ cd ~/dj-sample

Create a Python virtual environment.

$ python3 -m venv sample_proj

Activate the environment.

$ source sample_proj/bin/activate

Install Django.

(sample_proj) $ pip install django

To build the project, we need to use the startproject command. This command creates another directory that includes:

  • A management script, manage.py, used to administer Django-specific tasks.
  • A directory with the same name as the project that includes the project code.

We will create the project directory in our current working directory. To do that, use the dot(.) character at the end of the following command.

(sample_proj) $ django-admin startproject demoproject .

Next, we need to migrate the database using the migrate command. Migrations in Django propagate changes you make to your models into your database schema. Python uses SQLite database by default.

(sample_proj) $ python manage.py migrate

You will get the following output.

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

Next, create an administrative user to access Django’s admin interface.

(sample_proj) $ python manage.py createsuperuser

You will be prompted for a username, email, and password.

Username (leave blank to use 'navjot'):
Email address: [email protected]
Password:
Password (again):
Superuser created successfully.

Test the Development Server

It is time to test the application. For that, you need to modify the ALLOWED_HOSTS directive in the Django settings. This directive defines the list of IP addresses and domain names that are given access to the Django application.

Open the settings file using the following command.

(sample_proj) $ nano demoproject/settings.py

Find the following entry.

ALLOWED_HOSTS = []

Enter your server IP address in the square brackets. Each entry should be enclosed within quotations and multiple entries need to be separated via commas. Entering www.example.com will be matched exactly. However, .example.com will match example.com and www.example.com, and any other subdomain of example.com. Therefore, using the period sign to prefix a domain name to match it and its subdomains is recommended.

ALLOWED_HOSTS = ['']

We have used the IP address to match our server. Save the file by pressing Ctrl X and entering Y when prompted.

Before you test the development server, you need to configure the firewall to allow Django to work. Django uses port 8000 by default. Open the port using the Uncomplicated Firewall(UFW).

(sample_proj) $ sudo ufw allow 8000

Start the development server.

(sample_proj) $ python manage.py runserver 0.0.0.0:8000

Launch the URL http://:8000 in your browser and you will get the following screen.

<img alt="Django Homepage" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/01/echo/django-home.png63d40f3e0c574.jpg" ezimgfmt="rs rscb5 src ng ngcb5" height="687" loading="lazy" referrerpolicy="no-referrer" src="data:image/svg xml,” width=”750″>

You can access the admin interface by following the URL http://:8000/admin/ and you will see the following login screen.

<img alt="Django Login Page" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/01/echo/django-login.png63d40f3e4b158.jpg" ezimgfmt="rs rscb5 src ng ngcb5" height="446" loading="lazy" referrerpolicy="no-referrer" src="data:image/svg xml,” width=”718″>

Enter the credentials created earlier to log in to the admin panel shown below.

<img alt="Django Admin Panel" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/01/echo/django-admin-panel.png63d40f3e934b7.jpg" ezimgfmt="rs rscb5 src ng ngcb5" height="281" loading="lazy" referrerpolicy="no-referrer" src="data:image/svg xml,” width=”750″>

Once you finish your demo project, you can close the server by pressing Ctrl C in your terminal.

Install and Test Gunicorn

Run persistent Django server using nohup

So far, Django’s service is not persistent. To make the service persistent, there are two methods. The first method involves using the nohup utility. The nohup is a POSIX command which means no hang up. It is used to execute commands in a way that does not stop even when a user logs out.

Make sure you have exited the server from the terminal by pressing Ctrl C.

Run the following command to run Django’s development server.

(sample_proj) $ nohup python manage.py runserver 0.0.0.0:8000 &

Now, your Django server will keep running until you kill it manually. The command will give you the process ID and output another command.

[1] 42595
(sample_proj) $ nohup: ignoring input and appending output to 'nohup.out'
^C

Press Ctrl C to exit. The original Django server will keep running. You can verify by opening the URL in your browser.

Once you are finished, you need to kill the process. The nohup command gives you one process ID. But in reality, two processes are being run. To find the IDs for both processes, run the following command.

(sample_proj) $ ps aux | grep manage.py
navjot     42650  3.6  1.8  47792 38168 pts/0    S    02:28   0:00 python manage.py runserver 0.0.0.0.:8000
navjot     42651  7.9  2.0 344904 41708 pts/0    Sl   02:28   0:00 /home/navjot/dj-sample/sample_proj/bin/python manage.py runserver 0.0.0.0:8000
navjot     42657  0.0  0.1   6612  2172 pts/0    S    02:28   0:00 grep --color=auto manage.py

As you can see, two processes are being run, one with ID 42650 and the other one with ID 42651.

Run the following command to shut the server using the process IDs you obtained above.

(sample_proj) $ sudo kill -9 42650 42651

Install Gunicorn

The second method to run a persistent Django server requires you to install Gunicorn and Nginx web servers. Gunicorn is a Python WSGI HTTP server. It will interface with the Django application and then Nginx will act as a reverse proxy to Gunicorn. This method has the added benefit of providing you with the security and performance that comes with using Nginx.

Install Gunicorn.

(sample_proj) $ pip install gunicorn

Before proceeding, we need to test Gunicorn’s ability to serve the project. Run the following command to run Gunicorn.

(sample_proj) $ gunicorn --bind 0.0.0.0:8000 demoproject.wsgi

This will start Gunicorn on the same interface Django was running on. To verify, open the URL http://:8000 in your browser and you will get the same Django homepage. This means that Gunicorn is running perfectly.

When you are finished testing, press Ctrl C on the terminal to exit Gunicorn.

Deactivate the virtual environment to go back to your regular shell.

(sample_proj) $ deactivate

Create a Socket and Service file for Gunicorn

The first step is to create a Gunicorn socket file. The Gunicorn socket will be created at boot time and listen for connections. When a connection occurs, the systems will automatically start the Gunicorn process to handle it.

Create and open the Gunicorn socket file for editing.

$ sudo nano /etc/systemd/system/gunicorn.socket

Paste the following code in it.

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Save the file by pressing Ctrl X and entering Y when prompted.

Next, create and open the Gunicorn service file for editing.

$ sudo nano /etc/systemd/system/gunicorn.service

Paste the following code in it.

[Unit]
Description=django gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=navjot
Group=nginx
WorkingDirectory=/home/navjot/dj-sample
ExecStart=/home/navjot/dj-sample/sample_proj/bin/gunicorn 
          --access-logfile - 
          --workers 3 
          --bind unix:/run/gunicorn.sock 
          demoproject.wsgi:application

[Install]
WantedBy=multi-user.target

Save the file by pressing Ctrl X and entering Y when prompted. Replace navjot with your system username. The nginx group will allow the Nginx server to communicate with Django.

Reload the system daemon to refresh the systemd files.

$ sudo systemctl daemon-reload

Enable and start the Gunicorn socket file.

$ sudo systemctl start gunicorn.socket
$ sudo systemctl enable gunicorn.socket

Check the status of the Gunicorn socket.

$ sudo systemctl status gunicorn.socket

You will receive a similar output.

? gunicorn.socket - gunicorn socket
     Loaded: loaded (/etc/systemd/system/gunicorn.socket; disabled; vendor preset: enabled)
     Active: active (listening) since Thu 2022-06-30 01:43:24 UTC; 20s ago
   Triggers: ? gunicorn.service
     Listen: /run/gunicorn.sock (Stream)
     CGroup: /system.slice/gunicorn.socket

Jun 30 01:43:24 django systemd[1]: Listening on gunicorn socket.

The Gunicorn service is still not running as you can check.

$ sudo systemctl status gunicorn.service
? gunicorn.service - django gunicorn daemon
     Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
     Active: inactive (dead)
TriggeredBy: ? gunicorn.socket

To test the socket activation mechanism, run the following command.

$ curl --unix-socket /run/gunicorn.sock localhost

You will receive the HTML output of the Django homepage in your terminal. This also starts Gunicorn to serve the application. Check the status of the service again and you will see that it is running now.

$ sudo systemctl status gunicorn.service
? gunicorn.service - django gunicorn daemon
     Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
     Active: active (running) since Thu 2022-06-30 01:45:09 UTC; 12s ago
TriggeredBy: ? gunicorn.socket
   Main PID: 42365 (gunicorn)
      Tasks: 4 (limit: 2241)
     Memory: 91.7M
        CPU: 1.128s
     CGroup: /system.slice/gunicorn.service
             ??42365 /home/navjot/dj-sample/sample_proj/bin/python3 /home/navjot/dj-sample/sample_proj/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock demoproject.wsgi:application
             ??42366 /home/navjot/dj-sample/sample_proj/bin/python3 /home/navjot/dj-sample/sample_proj/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock demoproject.wsgi:application
             ??42367 /home/navjot/dj-sample/sample_proj/bin/python3 /home/navjot/dj-sample/sample_proj/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock demoproject.wsgi:application
             ??42368 /home/navjot/dj-sample/sample_proj/bin/python3 /home/navjot/dj-sample/sample_proj/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock demoproject.wsgi:application

Jun 30 01:45:09 django systemd[1]: Started django gunicorn daemon.
Jun 30 01:45:09 django gunicorn[42365]: [2022-06-30 01:45:09  0000] [42365] [INFO] Starting gunicorn 20.1.0
Jun 30 01:45:09 django gunicorn[42365]: [2022-06-30 01:45:09  0000] [42365] [INFO] Listening at: unix:/run/gunicorn.sock (42365)
Jun 30 01:45:09 django gunicorn[42365]: [2022-06-30 01:45:09  0000] [42365] [INFO] Using worker: sync
Jun 30 01:45:09 django gunicorn[42366]: [2022-06-30 01:45:09  0000] [42366] [INFO] Booting worker with pid: 42366
.......

Install Nginx

The last step is to install and configure Nginx. Ubuntu 22.04 ships with an older version of Nginx. You need to download the official Nginx repository to install the latest version.

Import Nginx’s signing key.

$ curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor 
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

Add the repository for Nginx’s stable version.

$ echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg arch=amd64] 
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" 
| sudo tee /etc/apt/sources.list.d/nginx.list

Update the system repositories.

$ sudo apt update

Install Nginx.

$ sudo apt install nginx

Verify the installation.

$ nginx -v
nginx version: nginx/1.22.0

Configure Nginx

Create and open the file /etc/nginx/conf.d/django-gunicorn.conf for editing.

$ sudo nano /etc/nginx/conf.d/django-gunicorn.conf

Paste the following code in it.

server {
    listen 80;
    server_name server_domain_or_IP;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/navjot/dj-sample;
    }

    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

Replace the root location in the above file with the directory on your server.

Save the file by pressing Ctrl X and entering Y when prompted.

Open the file /etc/nginx/nginx.conf for editing.

$ sudo nano /etc/nginx/nginx.conf

Add the following line before the line include /etc/nginx/conf.d/*.conf;.

server_names_hash_bucket_size  64;

Save the file by pressing Ctrl X and entering Y when prompted.

Verify your Nginx configuration.

$ sudo nginx -t

If you see no errors, it means you are good to go.

You will also need to add the domain name to your ALLOWED_HOSTS directive. Open the settings.py file.

$ nano ~/dj-sample/demoproject/settings.py

Change the value for ALLOWED_HOSTS variable.

ALLOWED_HOSTS = ['','django.example.com']

Save the file by pressing Ctrl X and entering Y when prompted.

Restart Gunicorn Socket and Service.

$ sudo systemctl restart gunicorn.socket
$ sudo systemctl restart gunicorn.service

Start the Nginx server.

$ sudo systemctl start nginx

Open the HTTP port. You can also delete the 8000 port if you are not going to use it anymore.

$ sudo ufw delete allow 8000
$ sudo ufw allow http

Verify by opening the URL http://django.example.com and the Django homepage will load up.

Install SSL

So far, your Django application is being served over a plaintext HTTP connection. It is highly recommended that you protect it via an SSL certificate. For this, use the Certbot tool using the Snapd tool. It is already installed on a Ubuntu 22.04 system.

Install the core Snapd repository.

$ sudo snap install core

Install Certbot.

$ sudo snap install --classic certbot
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Generate the certificate. The following command will also automatically configure Nginx.

$ sudo certbot --nginx --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m [email protected] -d django.example.com

Open the HTTPS port first.

$ sudo ufw allow https

Visit the URL https://django.example.com in your browser to confirm.

Conclusion

This concludes our tutorial, where you learned how to install Django and Gunicorn and Nginx on a Ubuntu 22.04 server. You also installed an SSL certificate to enhance the security of your Django project. If you have any questions, post them in the comments below.