How to deploy Django and Vue using Nginx
Deployment is an art. We can do it as difficult and complex as we can, but we can’t forget that complexity and maintainability usually are opposite concepts.
To do it easy, we will follow the KISS philosophy. So we go:
Prepare the ingredients
As a kitchen recipe, we need ingredients and these are the ones we need:
- Django project ready to prod
- Vue project ready to prod
- An instance of UNIX-Like server (I pref Ubuntu)
Prepare the instance
Create a user to run the application
For security reasons, it is a good practice to run the application with a non-root user.
$ sudo adduser <app_name> # Add user
$ sudo passwd -l <app_name> # Look login via username-password
Install Docker
We can use this script-like block to install Docker. You can follow step by step or copypaste it.
# Update apt first as a good practice
sudo apt-get update# Install docker dependencies
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common# Add docker GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -# Setting up the repository
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"# Update the package index
sudo apt-get update# Install docker
sudo apt-get install docker-ce docker-ce-cli containerd.io# Try if it works
sudo docker run hello-world
Once we install Docker we must give the <app_name> permissions to run it.
# Create docker group
sudo groupadd docker# Add <app_name> to group
sudo usermod -aG docker <app_name># Start on boot docker
sudo systemctl enable docker
Install docker-compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-composesudo chmod +x /usr/local/bin/docker-compose
Add repositories into the server
In a real-world project, we normally use tools to make the continuous delivery of the code. But in this guide, we will focus only on the deploy phase.
$ scp -r django_project username@myhost_or_ip:/home/<app_name>/backend/
$ scp -r vue_project username@myhost_or_ip:/home/<app_name>/frontend/
Recipe for backend
Using docker-compose we can build all we need. This docker-compose configuration is just an example, build what you need.
docker-compose.yml
version: '3'
volumes:
local_postgres_data: {}
local_postgres_data_backups: {}
services:
django:
image: python:3.7
container_name: <app_name>_django
depends_on:
- postgres
volumes:
- .:/app
- ./static:/static
working_dir: /app
env_file:
- .env
ports:
- "8000:8000"
command: bash -c "pip install -r requirements.txt && python manage.py migrate --noinput && python manage.py collectstatic --noinput && gunicorn -w 4 -t 180 -b 0.0.0.0:8000 config.wsgi"
postgres:
image: postgres:11
container_name: <app_name>_postgres
volumes:
- local_postgres_data:/var/lib/postgresql/data
- local_postgres_data_backups:/backups
env_file:
- .env
redis:
image: redis:5
container_name: <app_name>_redis
Now we can execute and let it run over 8000 port.
# Change user to app_name
sudo -u <app_name> -i# Go to backend directory
cd backend# execute docker-compose
docker-compose up -d
To verify that everything is ok:
docker-compose ps
Name Command State Ports---------------------------------------------------------------------------------------<app_name>_redis docker-entrypoint.sh redis ... Up 6379/tcp<app_name>_django bash -c pip install -r req ... Up 0.0.0.0:8000->8000/tcp<app_name>_postgres docker-entrypoint.sh postgres Up 5432/tcp
Recipe for frontend
In the same way, we use docker-compose to build without installing any dependency over the server.
version: '3'
services:
vue:
image: node:12
container_name: <app_name>_vue
volumes:
- ./:/code
working_dir: /code
env_file:
- .env
command: bash -c "npm i && npm run build"
Install and configure NGINX
The installation process is simple.
sudo apt install nginx
Let’s create a configuration file for our app
sudo nano /etc/nginx/sites-available/<app_name>
This configuration is an example, modify it what you need.
server {
server_name <domain>;
access_log /home/<app_name>/logs/nginx-access.log;
error_log /home/<app_name>/logs/nginx-error.log;
root /home/<app_name>/frontend/dist/;
client_max_body_size 10M;
location /static { # Serve the statics of backend
alias /home/<app_name>/backend/static;
}
location /media { # Serve the media of backend
alias /home/<app_name>/backend/media;
}
location / { # Catch all but defined routes to serve the frontend
try_files $uri $uri/ /index.html;
}
location ^~ /api/ { # Define routes to be directed to backend as proxy
include proxy_params;
proxy_pass http://0.0.0.0:8000;
}
location ^~ /admin {
include proxy_params;
proxy_pass http://0.0.0.0:8000;
}
location ^~ /sitemap.xml { # For example if you use django.contrib.sitemaps
include proxy_params;
proxy_pass http://0.0.0.0:8000;
}
}
Once you are ready, you must link the sites-available conf to sites-enabled and test the configuration.
sudo ln -s /etc/nginx/sites-available/<app_name> /etc/nginx/sites-enabled/<app_name>sudo nginx -t
If everything is ok, we can just restart NGINX
sudo service nginx restart
PLUS: Serve your content via HTTPS
First, we need to install dependencies of Certbot.
sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
Then, install Certbot
sudo apt-get install certbot python-certbot-nginx
And execute Certbot:
sudo certbot --nginx
Once you execute it, Certbot asks you if the content must be redirected to HTTPS, say yes.
We deploy it successfully, don’t we?
Take it into account that this approach is extremely simple but a good point to start. For example, we can build the images with our code and save it into a repository to pull and run the containers but in this guide, I tried to make it an example without building anything.
And that’s all. I hope you enjoy this guide and feel free to let some comments to give me feedback. This is my first post and I hope to contribute frequently.