DEV Community

Cover image for In-Depth guide to deploying Django & React to a Linux Server using Apache and WSGI
jan
jan

Posted on

In-Depth guide to deploying Django & React to a Linux Server using Apache and WSGI

This is an in-depth guide showing you how to deploy your Django/React application to a Linux server using Apache. In this specific example, the Linux server will be debian-based, running Ubuntu 22.04, so any commands you are seeing will be for a debian-based distribution.

You can take any part of this post to deploy your app standalone (you can only take the Django part and deploy your full stack Django application and vice versa).

At this time, we will be deploying a Django Rest Framework application using mod wsgi and a React app. Our database is going to be PostgreSQL and we will be using Apache as our web server.

Anything that is contained in brackets [] means you need to enter your own information that is limited to you specifically.


CONTENT

0 - What you will need
1 - Setting up Linux Server
2 - Setting up PostgreSQL
3.0 - Setting up Django project
3.1 - Setting up Apache and WSGI
3.2 - Apache/Django permissions
4 - Deploying React
5 - Finish
6 - Final Words

[0] - What you will need

This is a list of requirements you should have ready and be comfortable with using.

  1. Linux Server (you can use any distribution, I am using Ubuntu)
    • If you do not have a Linux server, you can get it from Linode That's what I use, they are quite cheap and easy to configure.
  2. Django Application
  3. React Application
  4. A little bit of PostgreSQL knowledge
  5. A little bit of Linux knowledge

[1] - Setting up Linux Server

At this point, I am assuming you have your Linux server up and running. First of all, SSH connect to your server. It should look something like:

 ssh root@your-ip # Example: ssh root@192.168.1.102 
Enter fullscreen mode Exit fullscreen mode

Enter your root password, and you should be in.

Before starting anything, make sure your packages are updated:

 apt-get update && apt-get upgrade 
Enter fullscreen mode Exit fullscreen mode

We wanna set the host name of the new machine using:

 hostnamectl set-hostname [your hostname] # Example: hostnamectl set-hostname django 
Enter fullscreen mode Exit fullscreen mode

We also need the set the hosts name in the hosts file:

 nano /etc/hosts 
Enter fullscreen mode Exit fullscreen mode

Add your hostname:

 127.0.0.1 localhost [your server ip] [your new hostname] # Example: 192.168.1.102 django 
Enter fullscreen mode Exit fullscreen mode

The next step is creating a limited user. As the root user, you can execute any command. Even though that sounds good, you should always deploy using a limited user to avoid any issues.

You can create a new user by running:

 adduser [your user] 
Enter fullscreen mode Exit fullscreen mode

It's going to ask you to fill out some information. This is completely optional and you can just leave it blank. It really doesn't matter.

We want this new user to be able to run root commands (sudo). We can do that by running:

 adduser [your user] sudo 
Enter fullscreen mode Exit fullscreen mode

This will add that user to the sudo group and allow it to run root commands.

Exit out of your server and login as the new user you created:

 exit ssh [your user]@[your server ip] # Example: ssh myuser@192.168.1.102 
Enter fullscreen mode Exit fullscreen mode

We want to install a package called ufw. It allows us to allow, deny and in general configure our server firewall very easily. This is a lot easier than using something like iptables.

 sudo apt-get install ufw 
Enter fullscreen mode Exit fullscreen mode

Next, run these ufw commands. WARNING THE COMMANDS WE ARE ABOUT TO RUN CAN LOCK YOU OUT OF SSH-ING INTO YOUR SERVER, DO NOT MESS THIS UP

 sudo ufw default allow outgoing sudo ufw default deny incoming sudo ufw allow ssh sudo ufw allow 8000 sudo ufw enable 
Enter fullscreen mode Exit fullscreen mode

We are firstly going to be testing our Django app, so we are not allowing HTTP, but only the specific port 8000 on which our Django app is going to run on. If everything works correctly, we will allow the HTTP port (80).

You can use this command to check what ports you have open:

 sudo ufw status 
Enter fullscreen mode Exit fullscreen mode

[2] - Setting up PostgreSQL

In this example, we are using PostgreSQL as our database. To install it, simply run

 sudo apt update sudo apt install postgresql postgresql-contrib 
Enter fullscreen mode Exit fullscreen mode

Make sure the service is started:

 sudo systemctl start postgresql.service 
Enter fullscreen mode Exit fullscreen mode

To enter your PostgreSQL CLI:

 sudo -i -u postgres psql 
Enter fullscreen mode Exit fullscreen mode

At this point, we will create a database, a database user with his password and grant him all privileges on that database. This requires 3 simple commands. In this example, my database name is going to be "coffee", my user is going to be "drinker" and my password is going to be "cup".

 sql CREATE DATABASE coffee; CREATE USER drinker with encrypted password 'cup'; GRANT ALL PRIVILEGES ON DATABASE coffee TO drinker; 
Enter fullscreen mode Exit fullscreen mode

This created our database, our user and granted him all the privileges on that database. This completes the PostgreSQL setup. Make sure to remember the database name, user and password, as we will be needing it later.


[3.0] - Setting up Django Project

Firstly, we have to install a couple of things

 sudo apt-get install python3-pip sudo apt-get install python3-venv 
Enter fullscreen mode Exit fullscreen mode

This will install Python version 3 if it hasn't already been installed and allow us to create a virtual environment for our Django application.

When entering your server, you are most likely located in your user home directory. To make sure, you can check your working directory with

 pwd # This should return /home/[user] 
Enter fullscreen mode Exit fullscreen mode

If everything is correct and you are inside your user home directory, you can clone your Django application from any VCS.

 git clone https://gitlab.com/username/django-project.git 
Enter fullscreen mode Exit fullscreen mode

Enter the cloned directory. Next, we will create a virtual environment inside our directory.

 python3 -m venv venv 
Enter fullscreen mode Exit fullscreen mode

Activate the environment:

 source venv/bin/activate 
Enter fullscreen mode Exit fullscreen mode

After activating the environment, we are going to install our packages. As such a skilled software developer, I am assuming you have your requirements in a requirements.txt file. Install them.

 pip install -r requirements.txt 
Enter fullscreen mode Exit fullscreen mode

Now we need to mess with the settings.py of your Django application to get it ready for deployment. Find your database configuration and change it to the database information we set up earlier, it should look something like this

 python DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'coffee', 'USER': 'drinker', 'PASSWORD': 'cup', 'HOST': 'localhost' 'PORT': '' } } 
Enter fullscreen mode Exit fullscreen mode

Ideally, you will have all these information in a seperate .env file.
Next, find your ALLOWED_HOSTS and edit them with the ip address of your server, it should look like this:

 python ALLOWED_HOSTS = [your server ip] # Example: ALLOWED_HOSTS = ['192.168.1.102'] 
Enter fullscreen mode Exit fullscreen mode

Don't forget to set your Debug to False if this is going to production Leaving Debug to True will give too much information in case an error occurs. For security reasons, we do not want this.
Next up, find your static and media URL-s and edit them, they should look like this:

 python STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static') MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 
Enter fullscreen mode Exit fullscreen mode

This is an important step because it will allow us to serve static files for things like swagger and access our media folder.
This should be it for the settings configuration, at least for now. Return to your app directory and with the virtual environment activated, we are going to run a couple of commands.

 python manage.py makemigrations python manage.py migrate python manage.py collectstatic 
Enter fullscreen mode Exit fullscreen mode

These commands will create the necessary migrations, create the tables in our PostgreSQL database and collect the static needed in the directory we just defined in our settings file.


Finally, we can test if we did everything correctly. We opened up port 8000 at the beginning of this guide, so let's run our server.

 python manage.py runserver 0.0.0.0:8000 
Enter fullscreen mode Exit fullscreen mode

If you did everything correctly and go to your server IP address following with port 8000, you should see your Django application.

Django Default Screen Hooray!


[3.1] - Setting up Apache and WSGI

Now of course, we can't have our Django project just running with the runserver command. We want it to be served on our IP address and later domain 24/7 without needing to start anything. This is where Apache and WSGI come into play. Let's install them.

 sudo apt-get install apache2 sudo apt-get install libapache2-mod-wsgi-py3 
Enter fullscreen mode Exit fullscreen mode

The first step to configuring Apache is happening in the apache2 sites directory. This is where the Apache configuration files are. Head into that directory:

 cd /etc/apache2/sites-available 
Enter fullscreen mode Exit fullscreen mode

In this directory, make a new configuration file, let's call it django-project.

 sudo nano django-project.conf 
Enter fullscreen mode Exit fullscreen mode

This is the most important step to setting up your WSGI
In this configuration file we will configure our virtual host. In here we will declare on what port we are going to run our Django application on, the directories it should serve and the WSGI Daemon process that is going to run our Django application. Start off by adding the virtual host.

 <VirtualHost *:80> </VirtualHost> 
Enter fullscreen mode Exit fullscreen mode

This means that on your server IP address (*), your Django project will run on port 80. This is the default HTTP port that opens up when you go to your IP address. If you are only deploying Django without React, leave it at 80. If you are going to deploy a React app to interact with Django, set the port to be 8000.


This virtual host example is going to assume the user is named djangouser, the repository you cloned is named django-project and your project inside the repository is named django-project.
Inside your virtual host tags, add the ServerAdmin and DocumentRoot. These are optional steps.

 <VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /home/djangouser/django-project </VirtualHost> 
Enter fullscreen mode Exit fullscreen mode

Add the log directory locations:

 <VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /home/djangouser/django-project ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost> 
Enter fullscreen mode Exit fullscreen mode

Next up, we are going to use an alias to tell Apache to map requests starting with static to our Django application static folder.

 <VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /home/djangouser/django-project ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined Alias /static /home/djangouser/django-project/static <Directory /home/djangouser/django-project/static> Require all granted </Directory> </VirtualHost> 
Enter fullscreen mode Exit fullscreen mode

We are going to do the same thing with our media folder.

 <VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /home/djangouser/django-project ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined Alias /static /home/djangouser/django-project/static <Directory /home/djangouser/django-project/static> Require all granted </Directory> Alias /media/ home/djangouser/django-project/media <Directory /home/djangouser/django-project/media> Require all granted </Directory> </VirtualHost> 
Enter fullscreen mode Exit fullscreen mode

Next, we will grant access to the wsgi.py inside our Django project. This makes sure Apache can access our wsgi.py file which is how our application talks to Apache.

 <VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /home/djangouser/django-project ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined Alias /static /home/djangouser/django-project/static <Directory /home/djangouser/django-project/static> Require all granted </Directory> Alias /media/ home/djangouser/django-project/media <Directory /home/djangouser/django-project/media> Require all granted </Directory> <Directory /home/djangouser/django-project/django-project> <Files wsgi.py> Require all granted </Files> </Directory> </VirtualHost> 
Enter fullscreen mode Exit fullscreen mode

Next, we are going set up the Daemon mode.

 <VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /home/djangouser/django-project ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined Alias /static /home/djangouser/django-project/static <Directory /home/djangouser/django-project/static> Require all granted </Directory> Alias /media/ home/djangouser/django-project/media <Directory /home/djangouser/django-project/media> Require all granted </Directory> <Directory /home/djangouser/django-project/django-project> <Files wsgi.py> Require all granted </Files> </Directory> WSGIScriptAlias / /home/djangouser/django-project/django-project/wsgi.py WSGIDaemonProcess django_app python-path=/home/djangouser/django-project python-home=/home/djangouser/django-project/venv WSGIProcessGroup django_app </VirtualHost> 
Enter fullscreen mode Exit fullscreen mode

This finished our Virtual Host configuration. Exit out of nano. Now, we have to enable that site through Apache. To do that:

 sudo a2ensite [your configuration file] # Example: sudo a2ensite django-project.conf 
Enter fullscreen mode Exit fullscreen mode

A default site comes with Apache, we don't need it so let's disable it:

 sudo a2dissite 000-default.conf 
Enter fullscreen mode Exit fullscreen mode

[3.2] - Apache/Django permissions

To make sure Apache and Django can easily communicate, access media folders, etc.. we have to set up proper permissions. Let's give Apache the ownership of the Django project and the media folder.

 sudo chown :www-data django-project sudo chmod 775 django-project sudo chown -R :www-data django-project/media sudo chmod -R 775 django-project/media 
Enter fullscreen mode Exit fullscreen mode

If you are running only the Django application and you are running it on port 80, delete the allow rule of port 8000 and allow HTTP. If you are deploying Django with React, just allow the http/tcp, but don't remove port 8000.

 sudo ufw delete allow 8000 sudo ufw allow http/tcp 
Enter fullscreen mode Exit fullscreen mode

Lastly, restart apache.

 sudo systemctl restart apache2 
Enter fullscreen mode Exit fullscreen mode

If you have followed everything correctly up to this point, you should have Apache web server set up, PostgreSQL set up with a database and user connected to your Django and your Django project set up and served on port 80 or 8000. If you are only deploying Django, this is the end of the guide for you. You have successfully deployed your Django project on a Linux server. If you are deploying React alongside Django, continue reading.


[4] - Deploying React

Congratulations, you have deployed your Django application. However, your front-end is missing. Luckily, deployment of a React application is much easier than Django. Move back to your home directory and clone your React application. (Blank cd will move you to your home directory)

 cd git clone https://gitlab.com/username/react-project.git 
Enter fullscreen mode Exit fullscreen mode

Before setting up the React project, we're gonna be needing Node/NPM. If they aren't already installed, you can run:

 sudo apt update sudo apt install nodejs sudo apt install npm 
Enter fullscreen mode Exit fullscreen mode

After a successful installation, move into your React project directory and install your packages.

 javascript npm install 
Enter fullscreen mode Exit fullscreen mode

Next, we will create a build version for our React project by running the following command:

 javascript npm run build 
Enter fullscreen mode Exit fullscreen mode

Before creating a virtual host that will serve our React application, we have to create a config file that will allow routes other than the index. This file is created in the build directory.

 cd react-project/build sudo nano .htaccess 
Enter fullscreen mode Exit fullscreen mode

Edit the file to look like this:

 RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule . /index.html [L] 
Enter fullscreen mode Exit fullscreen mode

After saving the file, let's make sure Apache can use the rewrite engine mod.

 sudo a2enmod rewrite 
Enter fullscreen mode Exit fullscreen mode

We need to restart our apache after enabling a mod:

 sudo systemctl restart apache2 
Enter fullscreen mode Exit fullscreen mode

Next up, we have to configure the virtual host, the process is similar as with Django, but easier. Create a new configuration file in the sites available directory.

 sudo nano /etc/apache2/sites-available/react-project.conf 
Enter fullscreen mode Exit fullscreen mode

Make sure the virtual host looks like the following, exchanging the example names with yours:

 <VirtualHost *:80> ServerName 192.168.1.102 [enter your server ip here] DocumentRoot /home/djangouser/react-project/build ErrorLog /home/djangouser/react-project/log/error.log CustomLog /home/djangouser/react-project/log/requests.log combined <Directory /home/djangouser/react-project/build> AllowOverride all Require all granted Options FollowSymlinks </Directory> </VirtualHost> 
Enter fullscreen mode Exit fullscreen mode

This will tell apache to serve our React project on port 80, while our Django backend is serving on port 8000. Like the Django project, enable the site and restart apache:

 sudo a2ensite react-project.conf sudo systemctl restart apache2 
Enter fullscreen mode Exit fullscreen mode

Let's give apache ownership of our React project directory

 cd sudo chown :www-data react-project sudo chmod 775 react-project 
Enter fullscreen mode Exit fullscreen mode

To ensure our back-end and front-end communicate without issues, let's edit our apache configuration.

 sudo nano /etc/apache2/apache2.conf 
Enter fullscreen mode Exit fullscreen mode

In here we have to make a few changes, first, add these lines:

 <Directory /home/djangouser> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory> 
Enter fullscreen mode Exit fullscreen mode

Now scroll to the bottom of the file and add the last thing needed in the apache configuration file:

 WSGIPassAuthorization On 
Enter fullscreen mode Exit fullscreen mode

To finish off, restart apache one more time:

 sudo systemctl restart apache2 
Enter fullscreen mode Exit fullscreen mode

[5] - Finish

Congratulations! You have made it to the end. Hopefully everything went smoothly and you have either a Django application deployed or even a React + Django application deployed. I will leave the connecting of the two up to you, I am sure you are skilled enough for that.

[6] - Final words

Thank you for reading my guide. I hope it has helped you entirely deploy your applications from scratch or even help you with a part you were stuck on. Deploying applications is not an easy task, especially without any prior knowledge, which compelled me to write this in depth guide explaining what needs to be done and why. Perhaps some time in the future, I create a part 2 to this guide, connecting these two applications to actual domains and adding SSL certificates.


If you have any questions or you feel like I made a mistake or left something out, feel free to contact me or leave a comment. I will correct it with an explanation. :)

Top comments (1)

Collapse
 
chazfg profile image
chazfg • Edited

Thank you so much. This is really comprehensive. Took me two days but it's running!