
Introduction
Django is a powerful web framework that enables you to quickly build and deploy Python-based applications or websites. While Django comes with a development server for testing purposes, a more secure and efficient web server is necessary for production environments. In this tutorial, you will learn how to set up and configure various components on Ubuntu to host Django applications. Specifically, you will install and configure MySQL (instead of the default SQLite database), Gunicorn application server, and Nginx web server. Nginx will act as a reverse proxy for Gunicorn, providing additional security and performance benefits for your Django apps.
Prerequisite
To deploy a Django application on an Ubuntu server using NGINX, you will need to follow these steps:
- Install NGINX, Gunicorn, and PostgreSQL (or any other database management system that you want to use) on the server using the
aptpackage manager. - Create a Python virtual environment and install the necessary packages and dependencies for your Django application using
pip. - Transfer your Django application code and static files to the server.
- Update the Django settings file with the correct database and static file configurations.
- Collect the static files into the appropriate directory.
- Create a systemd gunicorn socket and service
- Create an NGINX configuration file for your Django application.
- Start the Gunicorn server to run your Django application.
- Test the application by visiting the domain or IP address of your server in a web browser.
Login and create a sudo user (Optional**)
Get the IP address from the console and login.
$ ssh root@your-ip-addressRun updates and upgrade the server packages.
$ apt-get update && apt-get upgrade -yAdd user
$ adduser usernameGrant user sudo privileges
$ usermod -aG sudo usernameYou can then switch to your user with:
$ su - usernameYou can run sudo commands using
$ sudo(Optional**) Allow current user to execute sudo without password prompt:
echo "$USER ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/$USERInstalling Packages
Update your server:
sudo apt-get update && sudo apt-get upgrade -y
Install NGINX, Gunicorn, and MySQL on the server using the apt package manager.
To install NGINX, you can use the following command:
sudo apt update
sudo apt install nginx
sudo apt-get install python3-pip python3-dev python3-venv pkg-config libmysqlclient-dev curlTo install MySQL, you can use the following command:
sudo apt install mysql-serverConfigure MySQL Database
For fresh installations, you’ll want to run the included security script. This changes some of the less secure default options for things like remote root logins and sample users. On older versions of MySQL, you needed to initialize the data directory manually as well, but this is done automatically now.
Run the security script:
sudo mysql_secure_installation
## Output
This will take you through a series of prompts where you can make some changes to your MySQL installation’s security options. The first prompt will ask whether you’d like to set up the Validate Password Plugin, which can be used to test the strength of your MySQL password. Regardless of your choice, the next prompt will be to set a password for the MySQL root user. Enter and then confirm a secure password of your choice.
From there, you can press Y and then ENTER to accept the defaults for all the subsequent questions. This will remove some anonymous users and the test database, disable remote root logins, and load these new rules so that MySQL immediately respects the changes you have made.In order to use a password to connect to MySQL as root, you will need to switch its authentication method from auth_socket to mysql_native_password. To do this, open up the MySQL prompt from your terminal:
sudo mysqlmysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
mysql> FLUSH PRIVILEGES;
mysql> exitTry signing without password and you will be denied access.
sudo mysqlTo login use:
mysql -u root -pThen enter the password
Create Database User and Database for your Application
Now login to MySQL server and create your database.
mysql -u root -p
mysql> create database dbname;
mysql> CREATE USER 'username'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
mysql> GRANT ALL ON dbname.* TO 'username'@'%';Change dbname to the name you want for your database.
Create a user for the database. Remember not to use the root user credentials in your app. By the way, we disabled the root user remote login, so you will definitely get an error if you try to use the credentials.
The goal is to create a user and grant access to only the database created.
Change the password to a stronger one.
Exit the mysql prompt and try to log in as the new user and verify that the user has access to only the database given.
mysql -u username -pEnter the password. You can list your databases by typing:
mysql> show databases;The user has access if you see your database, as we defined above.
Update the Django settings file with the correct database and static file configurations.
In the Django settings file, you will need to set the DATABASES setting to use the MySQL database engine, name, user, and password. You will also need to set the STATIC_ROOT setting to the directory where you want to store the collected static files.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'dbname',
'USER': 'username',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '3306',
}
}Creating a Python Virtual Environment for your Project
Clone your project, create a virtualenv and install the requirements
git clone https://github.com/ktechhub/ktechhub.git
cd ktechhub/
python3 -m venv venv
. venv/bin/activate
pip install --upgrade pip setuptools
pip install -r requirements.txt Install other packages with pip:
To install Gunicorn, you can use the following command:
pip install gunicornInstall MySQL client:
pip install mysqlclientSettings File
Update your settings file to look like below:
STATICFILES_DIRS = [
BASE_DIR / "static",
]
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = os.path.join(BASE_DIR, 'media/')
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = 'static/'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.' + os.environ.get('DBENGINE', 'sqlite3'),
'NAME': os.environ.get('DBNAME', BASE_DIR / 'db.sqlite3'),
'USER': os.environ.get('DBUSER', ''),
'PASSWORD': os.environ.get('DBPASSWORD', ''),
'HOST': os.environ.get('DBHOST', '127.0.0.1'),
'PORT': os.environ.get('DBPORT', '3306'),
'OPTIONS': json.loads(
os.getenv('DATABASE_OPTIONS', '{}')
),
}
}
# Email configuration
if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
else:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = os.environ.get("EMAIL_HOST", "smtp.gmail.com")
EMAIL_USE_TLS = os.environ.get("EMAIL_USE_TLS", True)
EMAIL_PORT = os.environ.get("EMAIL_PORT", 587)
EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER", 'dummy@ktechhub.com')
EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD", '')
EMAIL_USE_SSL = False
DEFAULT_FROM_EMAIL = EMAIL_HOST_USERCreate a .env and enter the following:
DEBUG=False
DJANGO_ALLOWED_HOSTS=yourdomain.com,127.0.0.1,localhost
SECRET_KEY=your-secret-here
MAIL_MAILER=smtp
EMAIL_HOST=mail.privateemail.com
EMAIL_USE_TLS=True
EMAIL_PORT=587
EMAIL_HOST_USER=info@ktechhub.com
EMAIL_HOST_PASSWORD=
DBENGINE=mysql
DBNAME=dbname
DBHOST=dbhost
DBPASSWORD=password
DBPORT= 3306
DBUSER=usernameComplete the initial project setup
Using the management scripts, we can migrate the initial database schema to our database. After that, we create an administrative user for the project. You must select a username, provide an email address, and choose and confirm a password.
We can collect all of the static content into the directory location we configured.
You will have to confirm the operation. The static files will then be placed in a directory called static within your project directory.
./manage.py makemigrations
./manage.py migrate
./manage.py createsuperuser
./manage.py collectstatic --verbosity=2Let's test the application
If you followed the initial server setup guide, you should have a UFW firewall protecting your server. In order to test the development server, we’ll have to allow access to the port we’ll be using.
Create an exception for port 8000 by typing:
sudo ufw allow 8000Finally, you can test our your project by starting up the Django development server with this command:
./manage.py runserver 0.0.0.0:8000In your web browser, visit your server’s domain name or IP address followed by :8000:
http://server_domain_or_IP:8000Testing Gunicorn’s Ability to Serve the Project
The last thing we want to do before leaving our virtual environment is test Gunicorn to ensure it can serve the application. We can do this by entering our project directory and using gunicorn to load the project’s WSGI module:
gunicorn --bind 0.0.0.0:8000 myproject.wsgiThis will start Gunicorn on the same interface that the Django development server was running on. You can go back and test the app again.
We passed Gunicorn a module by specifying the relative directory path to Django’s wsgi.py file, which is the entry point to our application, using Python’s module syntax. Inside of this file, a function called application is defined, which is used to communicate with the application.
When you are finished testing, hit CTRL-C in the terminal window to stop Gunicorn.
We’re now finished configuring our Django application. We can back out of our virtual environment by typing:
deactivateThe virtual environment indicator in your prompt will be removed.
Configure Gunicorn with systemd Socket and Service Files
Start by creating and opening a systemd socket file for Gunicorn with sudo privileges:
sudo nano /etc/systemd/system/gunicorn.socketInside, we will create a [Unit] section to describe the socket, a [Socket] section to define the socket location, and an [Install] section to make sure the socket is created at the right time:
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.targetSave and close the file when you are finished.
Next, create and open a systemd service file for Gunicorn with sudo privileges in your text editor. The service filename should match the socket filename with the exception of the extension:
sudo nano /etc/systemd/system/gunicorn.service[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/ktechhub
ExecStart=/home/ubuntu/ktechhub/venv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/gunicorn.sock \
myproject.wsgi:application
[Install]
WantedBy=multi-user.targetWith that, our systemd service file is complete. Save and close it now.
We can now start and enable the Gunicorn socket. This will create the socket file at /run/gunicorn.sock now and at boot. When a connection is made to that socket, systemd will automatically start the gunicorn.service to handle it:
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket
sudo systemctl start gunicorn.service
sudo systemctl enable gunicorn.serviceChecking socket and service statuses:
sudo systemctl status gunicorn.socket
sudo systemctl status gunicorn.serviceConfigure Nginx to Proxy Pass to Gunicorn
Now that Gunicorn is set up, we need to configure Nginx to pass traffic to the process.
Start by creating and opening a new server block in Nginx’s sites-available directory:
sudo nano /etc/nginx/sites-available/myprojectserver {
listen 80;
server_name domain_OR_IP;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
alias /home/ubuntu/ktechhub/staticfiles/;
}
location /media/ {
alias /home/ubuntu/ktechhub/media/;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
Save and close the file when you are finished. Now, we can enable the file by linking it to the sites-enabled directory:
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabledTest your Nginx configuration for syntax errors by typing:
sudo nginx -tIf no errors are reported, go ahead and restart Nginx by typing:
sudo systemctl restart nginxFinally, we need to open up our firewall to normal traffic on port 80. Since we no longer need access to the development server, we can remove the rule to open port 8000 as well:
sudo ufw delete allow 8000
sudo ufw allow 'Nginx Full'You should now be able to go to your server’s domain or IP address to view your application.
Configure SSL/TLS on your Domain
To configure TLS, use the link below:

Experienced DevOps Engineer proficient in Python, Docker, Kubernetes, CI/CD, and cloud technologies. Specialized in automating processes and building scalable infrastructure for seamless development and deployment.


