Introduction
Django is a free and open-source Python web framework. This article provides a step-by-step guide to setting up a Django project on a Debian 11 server with Nginx, Gunicorn, and a free Let's Encrypt TLS certificate.
Prerequisites
Deploy a new Debain 11 server at RCS
Create a sudo user. The example user is
django_userPoint a domain name to the server. A domain name is required for Let's Encrypt SSL/TLS certificates. This guide uses the apex domain example.com and the fully-qualified domain name mydjango.example.com, and assumes the same IP address is assigned to both names.
1. Install Dependencies
Log in to the server with a non-root sudo user via SSH.
$ ssh django_user@mydjango.example.comInstall
UFW,Vim, andNginxwith the apt package manager.$ sudo apt -y install ufw vim nginxAllow all outbound traffic.
$ sudo ufw default allow outgoingBlock all inbound traffic except SSH (port 22), HTTP (port 80), and HTTPS (port 443).
$ sudo ufw default deny incoming $ sudo ufw allow ssh $ sudo ufw allow http $ sudo ufw allow httpsEnable and reload
UFW.$ sudo ufw enable $ sudo ufw reload
2. Install PostgresSQL
Install PostgreSQL with the apt package manager, a postgres user is created during the installation.
$ sudo apt install -y postgresql postgresql-contribSwitch to the postgres user.
$ sudo su - postgresLog in to PostgresSQL.
$ psqlCreate a PostgresSQL database role for the Django project.
postgres=# CREATE ROLE dbuser WITH LOGIN;Set password for
dbuserrole.postgres=# \password dbuserSet
dbuserencoding toUTF-8.postgres=# ALTER ROLE dbuser SET client_encoding TO 'utf8';Set
dbuserdefault transaction isolation to read commit.postgres=# ALTER ROLE dbuser SET default_transaction_isolation TO 'read committed';Set
dbusertimezone toUTC.postgres=# ALTER ROLE dbuser SET timezone TO 'UTC';Create the Django project database.
postgres=# CREATE DATABASE appdb;Grant the
dbuserall privileges toappdb.postgres=# GRANT ALL PRIVILEGES ON DATABASE appdb TO dbuser;Exit the postgres client.
postgres=# \qExit to sudo
exitRestart the PostgresSQL server.
$ sudo systemctl restart postgresql
3. Setup the Python Environment
A Python virtual environment should be used when deploying Python web apps.
Install the Python
venv.$ sudo apt -y install python3-venvCreate app dir, where the virtual environment will be, and the app files. example
app_dir. Change into the home directory.$ cd ~Create the directory.
$ mkdir app_dirChange to
app dirand create the virtual environment.$ cd app_dir $ python3 -m venv venvActivate the virtual environment.
$ source venv/bin/activateInstall Python PostgresSQL library
psycopg2-binaryinstead of thepsycopg2, aspsycopg2will need to be built during installation.$ pip install psycopg2-binaryInstall Django and Gunicorn.
$ pip install django gunicorn
4. Upload or Create the Django Project
The project source code needs to be uploaded to the app directory
app_dirwith git or scp, for this article, create a sample project calledexample_app.$ django-admin startproject example_app .Check the folder structure of the
app_dir.$ ls example_app manage.py venvCreate a folder to hold the static files of the Django project inside the app folder.
$ mkdir staticOpen the
settings.pyinexample_appwithVimto configure the project.$ vim example_app/settings.pyConfigure the allow host to the domain.
ALLOWED_HOSTS = ['mydjango.example.com']Configure the
DATABASESvariable with the details created fromsection 2.DATABASES = { "default" : { "ENGINE" : "django.db.backends.postgresql", "NAME" : "appdb", "USER" : "dbuser", "PASSWORD" : "dbpassword", "HOST" : "127.0.0.1", "PORT" : "5432", } }Set the static folder path. Look for
STATIC_URLand underneath it addSTATIC_ROOT.STATIC_ROOT = "/home/django_user/app_dir/static"Save and close the
settings.pyfile.Create the project migrations.
$ python manage.py makemigrationsRun the migrations.
$ python manage.py migrateCopy all the static files of the project to the static folder. Type
yeswhen prompted.$ python manage.py collectstaticCreate an admin user for the project.
$ python manage.py createsuperuserAllow port
8000onUFW.$ sudo ufw allow 8000Test the project to make sure it runs without errors.
$ python manage.py runserver 0.0.0.0:8000 Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). November 16, 2022 - 21:19:26 Django version 4.1.3, using settings 'example_app.settings' Starting development server at http://0.0.0.0:8000/ Quit the server with CONTROL-C.
5. Configure Gunicorn
A Gunicorn service will be created to serve the project and start the project on server restarts.
Test Gunicorn to see if it can serve the project without errors.
$ gunicorn --bind 0.0.0.0:8000 example_app.wsgiPress CTRL+C to stop it.
Deactivate the virtual environment
$ deactivateEdit the
settings.pyfile withVim.$ vim example_app/settings.pyDisable debug. Look for the variable
DEBUGand set it toFalse.DEBUG = FalseSave and close the
settings.pyfile.Deny incoming traffic to port 8000 with
UFW.$ sudo ufw deny 8000Create a
django_app.servicefile inside/etc/systemd/systemdirectory.$ sudo vim /etc/systemd/system/django_app.serviceWrite the following into the file.
[Unit] Description=Gunicorn for the Django project After=network.target [Service] User=django_user Group=www-data WorkingDirectory=/home/django_user/app_dir Environment="PATH=/home/django_user/app_dir/venv/bin" ExecStart=/home/django_user/app_dir/venv/bin/gunicorn --workers 2 --bind unix:django_app.sock example_app.wsgi [Install] WantedBy=multi-user.targetUnder
[Service],USER=django_usersets the user of the service, andGroup=www-datasets the group of the service to the Nginx group.WorkingDirectory=/home/django_user/app_dirsets the service working directory to the projects folder.Environment="PATH=/home/django_user/app_dir/venv/bin"sets the virtualenv that the service should use.ExecStart=/home/django_user/app_dir/venv/bin/gunicorn --workers 2 --bind unix:django_app.sock example_app.wsgirun gunicorn worker services for the project and binds them to a Unix socket for faster data exchange for the Nginx proxy server.
Save and close the file.
Enable the service to start at boot.
$ sudo systemctl enable django_appStart the service.
$ sudo systemctl start django_app
6. Configure Nginx
An Nginx proxy server is needed to serve Django apps in production and to handle SSL certificates.
Configure a server block in Nginx by creating a file
django_appin/etc/nginx/sites-available.$ sudo vim /etc/nginx/sites-available/django_appWrite the following into the file.
server { # listen to port 80 (HTTP) listen 80; # listen to the domain name server_name example.com mydjango.example.com; location /static/ { alias /home/django_user/app_dir/static/; expires max; } location / { include proxy_params; # pass the request to the project service sock proxy_pass http://unix:/home/django_user/app_dir/django_app.sock; } }Save and close the file.
Create a link for
django_appto Nginx'ssites-enabled.$ sudo ln -s /etc/nginx/sites-available/django_app /etc/nginx/sites-enabledCheck the Nginx configuration for any errors.
$ sudo nginx -tRestart the Nginx server.
$ sudo systemctl reload nginx
7. (Optional) Add HTTPS with Let's Encrypt
If you configured a domain in the beginning, then you can add an SSL/TLS certificate with Let's Encrypt to provide HTTPS support.
Install
Certbotwith theNginxplugin.$ sudo apt install certbot python3-certbot-nginxConfigure
CertbotwithNginx.$ sudo certbot --nginx -d example.com -d mydjango.example.comThe first time running Certbot, an admin email for the certificates will be requested. Certbot will also configure Nginx and redirect all HTTP requests to HTTPS by configuring the server block.
Saving debug log to /var/log/letsencrypt/letsencrypt.log Plugins selected: Authenticator nginx, Installer nginx Enter email address (used for urgent renewal and security notices) (Enter 'c' to cancel): me@example.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Please read the Terms of Service at https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must agree in order to register with the ACME server. Do you agree? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Y)es/(N)o: y - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Would you be willing, once your first certificate is successfully issued, to share your email address with the Electronic Frontier Foundation, a founding partner of the Let's Encrypt project and the non-profit organization that develops Certbot? We'd like to send you email about our work encrypting the web, EFF news, campaigns, and ways to support digital freedom. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Y)es/(N)o: y Account registered. Requesting a certificate for example.com and mydjango.example.com Performing the following challenges: http-01 challenge for example.com http-01 challenge for mydjango.example.com Waiting for verification... Cleaning up challenges Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/django_app Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/django_app Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/django_app Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/django_app - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations! You have successfully enabled https://example.com and https://mydjango.example.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Subscribe to the EFF mailing list (email: me@example.com). IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/example.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/example.com/privkey.pem Your certificate will expire on 2023-02-15. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-leRestart
Nginx.$ sudo systemctl reload nginx
- Visit the domain of the server
mydjango.example.com/adminin your browser.
You now have a working Django site on Debian 11.