Deploy Django and React APP in a production VPS (Django + React + PostgreSQL + NGINX + Ubuntu Server)
Assuming You have backend and frontend codes in /home/backend
and /home/frontend/
(Use git to upload)
sudo apt-get update
sudo apt-get install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx
We'll setup backend first. Go to your backend folder
cd /home/backend/
Enter postgres environment
sudo -u postgres psql
Create database and user with password and assign that user to that database. Change the db_name
, db_user_name
and db_user_password
with yours.
CREATE DATABASE db_name;
CREATE USER db_user_name WITH PASSWORD 'db_user_password';
GRANT ALL PRIVILEGES ON DATABASE db_name TO db_user_name;
\q
sudo -H pip3 install --upgrade pip
sudo -H pip3 install virtualenv
virtualenv venv
Activate it
source venv/bin/activate
Use pip freeze > requirements.txt
from your local environment to export all required packages names with their versions. Now install those packages
pip install -r requirements.txt
For GCP VMs, using just pip will not work (you've to run it in sudo mode), it'll cause issue of permission error. So in case of GCP try this:
sudo /home/backend/venv/bin/python -m pip install -r requirements.txt
Install gunicorn
and psycopg2
(for postgresql) additionally
pip install gunicorn psycopg2
You should now have everything you need to deploy your django backend.
Migrate your database migrations
python manage.py migrate
Now try if you're able to start the project.
python manage.py runserver
If you face any module not found errors or some other issues, solve it here before moving to next stage.
Now Create an exception for port 8000:
sudo ufw allow 8000
Finally, you can test our your project by starting up the Django development server with this command:
python manage.py runserver 0.0.0.0:8000
In your web browser, visit your server’s domain name or IP address followed by :8000:
http://server_domain_or_IP:8000
You should see your project running. If not fix it before moving to next step.
Change project_name
with your project folder name (where your project urls.py, wsgi.py
etc exists)
gunicorn --bind 0.0.0.0:8000 project_name.wsgi
This will start Gunicorn on the same interface that the Django development server was running on. You can go back and test the app again.
Note: The admin interface will not have any of the styling applied since Gunicorn does not know about the static CSS content responsible for this.
If everything so far gone very well, deactivate the virtualenvionment by
deactivate
The virtual environment indicator in your terminal will be removed.
We have tested that Gunicorn can interact with our Django application, but we should implement a more robust way of starting and stopping the application server. To accomplish this, we’ll make a systemd service file.
Create and open a systemd service file for Gunicorn with sudo
privileges
sudo nano /etc/systemd/system/gunicorn.service
Paste this code in the editor (nano
)
Change root
with your username and project_name
with your project folder name.
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=root
Group=www-data
WorkingDirectory=/home/backend
ExecStart=/home/backend/venv/bin/gunicorn \
--env DJANGO_SETTINGS_MODULE='project_name.settings' \
--access-logfile /home/backend/logs/gunicorn.log \
--workers 3 --bind 127.0.0.1:8000 project_name.wsgi:application
[Install]
WantedBy=multi-user.target
Now press ctrl + x
to exit editing, enter y
and then press Enter
.
We can now start the Gunicorn service that we created and enable it so that it starts at boot:
sudo systemctl start gunicorn
sudo systemctl enable gunicorn
Check the status of the process to find out whether it was able to start:
sudo systemctl status gunicorn
Next, check for the existence of the myproject.sock file within your project directory:
ls /home/backend
If the systemctl status command indicated that an error occurred or if you do not find the backend.sock
file in the directory, it’s an indication that Gunicorn was not able to start correctly. Check the Gunicorn process logs by typing:
sudo journalctl -u gunicorn
Take a look at the messages in the logs to find out where Gunicorn ran into problems. There are many reasons that you may have run into problems, but often, if Gunicorn was unable to create the socket file, it is for one of these reasons:
- The project files are owned by the
root
user instead of asudo
user - The WorkingDirectory path within the
/etc/systemd/system/gunicorn.service
file does not point to the project directory - The configuration options given to the gunicorn process in the
ExecStart
directive are not correct. Check the following items:- The path to the gunicorn binary points to the actual location of the binary within the virtual environment
- The
--bind
directive defines a file to create within a directory that Gunicorn can access - The
project_name.wsgi:application
is an accurate path to the WSGI callable.
If you make changes to the /etc/systemd/system/gunicorn.service
file, reload the daemon to re-read the service definition and restart the Gunicorn process by typing:
sudo systemctl daemon-reload
sudo systemctl restart gunicorn
Make sure you troubleshoot any of the above issues before continuing.
Check existing enabled sites by
ls /etc/nginx/sites-enabled/
You'll see a file called default
, if there any other files, remove them
sudo rm /etc/nginx/sites-enabled/filename
Check available servers:
ls /etc/nginx/sites-available/
You'll see a file called default
, if there any other files, remove them as well.
Backup the default server file.
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak
Now open that default server definition file.
sudo nano /etc/nginx/sites-available/default
Paste following block of codes.
server {
root /home/frontend/build;
index index.htm index.html index.nginx-debian.html;
server_name 1**.**.**.*9;
location / {
try_files $uri $uri/ /index.html =404;
}
location ~ ^/api {
proxy_pass http://localhost:8000;
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name 1**.**.**.*9;
return 404;
}
Change server_name
value with your domain or ip address.
Look at that
root
, we defined/home/frontend/build
where will our frontend codes be available.
Test your Nginx configuration for syntax errors by typing:
sudo nginx -t
If no errors are reported, go ahead and restart Nginx by typing:
sudo systemctl restart nginx
Finally, 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'
And our backend is ready
Move to your frontend
folder
cd /home/frontend
Install nodejs and npm
sudo apt-get install nodejs npm
Install required libraries.
sudo npm install
Build the project
sudo npm run build
Restart nginx service
sudo service nginx restart
You should now be able to go to your server’s domain or IP address to view your application.
By previous settings you won't be able to access django-admin
. If you want to access it, along with styling (Gunicorn will not load any styling), change your nginx
configuration file as bellow.
server {
root /home/frontend/build;
index index.htm index.html index.nginx-debian.html;
server_name 1**.**.**.*9;
location / {
try_files $uri $uri/ /index.html =404;
}
location ~ ^/api {
proxy_pass http://localhost:8000;
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~ ^/admin {
proxy_pass http://localhost:8000;
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location = /favicon.ico {
access_log off;
log_not_found off;
}
location /django-static {
autoindex on;
alias /home/backend/staticfiles;
}
location /media {
autoindex on;
alias /home/backend/media;
}
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name 1**.**.**.*9;
return 404;
}
In your django settings file, add this.
STATIC_URL = "/django-static/"
STATIC_ROOT = "staticfiles"
Note: If your django admin lives in other url than default
/admin/
change that url herelocation ~ ^/admin
.
Now run this management command to collect all static files to that folder (staticfiles).
python manage.py collectstatic
Now you should access your django admin area along with styling (CSS & JS).
http://server_domain_or_IP/admin
As you update your configuration or application, you will likely need to restart the processes to adjust to your changes.
If you update your Django application, you can restart the Gunicorn process to pick up the changes by typing:
sudo systemctl restart gunicorn
If you change gunicorn systemd service file, reload the daemon and restart the process by typing:
sudo systemctl daemon-reload
sudo systemctl restart gunicorn
If you change the Nginx server block configuration, test the configuration and then restart Nginx by typing:
sudo nginx -t && sudo systemctl restart nginx
These commands are helpful for picking up changes as you adjust your configuration.
Comment bellow if you face any error while deploying your application, I'll try to reply. Don't forget to put down your application url in comment box after you successfully deloyed it.