You can also use Nginx to serve Vue and another Nginx instance to work as reverse proxy to serve Django statics (Gunicorn will serve Django dynamic content i.e API calls/ responses) - you will get faster serving of static content and improved security layer on the way.
Here is why tu use Nginx -> https://djangoadventures.com/what-is-the-point-of-using-nginx-in-front-of-a-django-application/
Nice example here for Docker, Django, Postres, Nginx -> https://testdriven.io/blog/dockerizing-django-with-postgres-gunicorn-and-nginx/
For Vue - Nginx can also serve that. Remember to edit settings.py with Cors and allowed hosts.
If you want incorporate nginx proxy to the frontend then you can do that by 2 separate nginx files like:
frontend file -> nginx.conf
server {
listen 8080;
location / {
root /code;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
backend file -> default.conf
upstream backend {
server backend:8000;
}
upstream frontend {
server frontend:8080;
}
server {
listen 80;
root /usr/share/nginx/html;
include /etc/nginx/mime.types;
# Vue frontend.
location / {
proxy_pass http://frontend;
}
# Django API.
location /api {
proxy_pass http://backend;
autoindex off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
# Django static assests.
location /static/ {
autoindex on;
alias /code/staticfiles/;
}
}
Dockerfile for Django
# Build from minimal image for speed and security
FROM python:3.10.2-slim-bullseye
ENV WORKDIR=/code
ENV USER=code
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR $WORKDIR
COPY requirements.txt $WORKDIR
# make static files dirs in order to avoid error from collectstatic
RUN mkdir $WORKDIR/staticfiles && \
mkdir $WORKDIR/staticfiles/admin && \
mkdir $WORKDIR/staticfiles/rest_framework
RUN pip install --upgrade pip && \
pip install -r requirements.txt && \
adduser --system --group $USER && \
chown -R $USER:$USER $WORKDIR
COPY ./app/backend $WORKDIR
USER $USER
EXPOSE 8000
Dockerfile for vue
FROM node:lts-alpine as build-stage
ENV WORKDIR=/code
WORKDIR $WORKDIR
# copy both 'package.json' and 'package-lock.json' (if available) from you project dir
COPY app/frontend/package*.json $WORKDIR/
# install project dependencies
RUN npm install --legacy-peer-deps
COPY app/frontend $WORKDIR
RUN npm run build
# Serve Vue
FROM nginx:alpine as production-stage
RUN mkdir $WORKDIR
COPY app/nginx/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build-stage $WORKDIR/dist $WORKDIR
EXPOSE 8080
Then Docker compose
version: "3.5"
volumes:
postgres_data:
static_volume:
services:
backend:
container_name: django_backend
env_file:
- .env
build:
context: .
dockerfile: Dockerfile.django
restart: unless-stopped
# Should go into separate *.sh file but for simplicity - you starting Gunicorn to serve Django
command: >
sh -c "python manage.py wait_for_db &&
python manage.py migrate --noinput &&
python manage.py collectstatic --no-input &&
gunicorn your_project.wsgi:application --bind 0.0.0.0:8000"
volumes:
- static_volume:/code/staticfiles
depends_on:
- database
frontend:
container_name: vue_frontend
build:
context: .
dockerfile: Dockerfile.vue
restart: unless-stopped
nginx:
container_name: nginx_proxy_server
build:
context: .
dockerfile: Dockerfile.nginx_proxy
restart: unless-stopped
ports:
- 80:80
volumes:
- static_volume:/code/staticfiles
depends_on:
- frontend
- backend
database:
container_name: postgres_db
build:
context: .
dockerfile: Dockerfile.postgres
volumes:
- postgres_data:/var/lib/postgresql/data/
# from .env
environment:
- POSTGRES_HOST=${DATABASE_HOST}
- POSTGRES_USER=${DATABASE_USER}
- POSTGRES_PASSWORD=${DATABASE_PASSWORD}
- POSTGRES_DB=${DATABASE_NAME}