1

I was following this tutorial to create a backend and it worked.

I made simple a Django REST admin panel to upload images, it worked.

Then I created Vue frontend app, run npm run serve while in 'VScode remote' and it worked (images are fetched from Django and styled by Vue in my localhost).

The PROBLEM is it's not obvious how to make all this work in the production VPS server (i mean from Vue 'dist' folder after vue run build). Everything I tried just gives me a 404 error or ruins the Django admin panel.

Here are my NGINX settings :

server {
server_name kruglovks.xyz www.kruglovks.xyz;
client_max_body_size 100m;

location = /favicon.ico { access_log off; log_not_found off; }

location /static {
    root /home/kirill/myprojectdir/myproject;
}

location /media {
    root /home/kirill/myprojectdir/myproject;
}

location /dist {
    try_files $uri $uri/ /index.html;
    alias /home/kirill/myprojectdir/myproject;
}

location / {
    include proxy_params;
    proxy_pass http://unix:/run/gunicorn.sock;
}

Vue router is set to history mode.

Please, I need some info on how to make Vue work in this configuration.

P.S. Maybe there is an alternative way of using Vue alongside Django?

Thank you so much and have a nice day!

4 Answers 4

3

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}
Sign up to request clarification or add additional context in comments.

Comments

2

That was a huge step forward, not a 404 but blank page called 'frontend' and browser refused to find css and js.

Later with a friends help and thisguide i could reconfigure nginx settings to this :

server {
listen      80;
server_name kruglovks.xyz www.kruglovks.xyz;
root /home/sasha/myprojectdir/myproject/dist/;
client_max_body_size 100m;

location = /favicon.ico { access_log off; log_not_found off; }

location /static {
    alias /home/sasha/myprojectdir/myproject/static;
}

location /media {
    alias /home/sasha/myprojectdir/myproject/media;
}

location / {
    try_files $uri $uri/ /index.html;
}

location ^~ /api/ {
    include proxy_params;
    proxy_pass http://unix:/run/gunicorn.sock;
}

location ^~ /admin {
     include proxy_params;
     proxy_pass http://unix:/run/gunicorn.sock;
}

and it works now !

Thank you so muck @Danizavtz, you saved me like weeks of life ...

Comments

1

You are not listening to any ports. Notice that in location /dist you also have to change the order of operations

Try to change your default to this configuration:

server {
    listen      80;
    server_name kruglovks.xyz www.kruglovks.xyz;
    root        /var/www;
    #... configurations
    location /dist {
        alias /home/kirill/myprojectdir/myproject;
        try_files $uri $uri/ /index.html;

}

And the vuejs application will be served in www.kruglovks.xyz/dist.

Comments

1

This is for production, not intended for development environment. Django application deployed using Guncorn

# settings.py

ALLOWED_HOSTS=['yourdomain.com', 'localhost'] # 'localhost' is optional 
...
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

If using 'corsheaders' then add yourdomain.com

# settings.py

CORS_ALLOWED_ORIGINS = `[`
    "https://yourdomain.com",
    "http://yourdomain.com", # Use this if SSL in not configured
    "http://localhost"
]
        

Update 'urls.py' for the project, and choose path for your application; mine is 'app/api/' so it will direct to http://yourdomain.com/app/api/

urlpatterns = [
    path('app/api/', include('api.urls')),
    path('app/admin/', admin.site.urls),
]

NGINX Configurations

# /etc/nginx/sites-available/yourdomain.com.conf
server {
    root /var/www/app/dist; # Vue app production build
    index index.html index.htm index.nginx.debian.html;

    server_name yourdomain.com www.yourdomain.com;

    # Django folder
    location /static/ {
        root /path/to/django/project/folder;
    }

    # Django web requests handled here
    location /app/ {
        # Gunicorn and other configurations
        ...
    }

    # Everything else handled by Vue
    location / {
        try_files /$uri /$uri/ /index.html$args;
    }

    ...
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.