2

Context

I have a single page web app using the following stack:

  • React for the frontend
  • Django for the backend
  • Nginx for the webserver

The web application is dockerized using docker-compose. My React app, fetches data from the Django server (django is built as an api endpoint using Django Rest Framework).

Question/Problem

I am having an issue on deployment where I am unable to serve my media files via Nginx.

What have I tried so far

My initial thought was to serve the media files as shown on this stackoverflow post - which is pretty straight forward. Though, since Nginx runs in its own docker (and so does my django server), I unable to point to my django media files since they are in different containers.

Ideally, I would not want to use webpack, and have Nginx take care of serving media files.

If you look at the nginx Dockerfile below, you will see that I am copying my static files into /usr/share/nginx/html/static/staticfiles to then serve them using nginx (see location ^~ /static/ in nginx.conf). I have tried to do the same thing for my media file (as a test) and it works - though, all the files I am uploding once the site is up are not accessible since the copy happens when I build my container.

File Structure

Root Dirc  
 |__ docker-compose.yml  
 |__ backend  
      |__ root  
           |__ Project
                |__ api
                     |__ models.py
                     |__ ...
                |__ media
           |__ teddycrepineau
                |__ settings.py
                |__ ...
           |__ production
                |__ Dockerfile
 |__ nginx
      |__ Dockerfile
      |__ nginx.conf
 |__ frontend
      |__ ...

Relevant Code

docker-compose.yml

version: '3'

volumes:
  postgres_data: {}
  postgres_backup: {}

services:
  postgres:
    image: postgres
    volumes: 
      - postgres_data:/var/lib/postgresql/data
      - postgres_backup:/backups
    env_file: .env

  nginx:
    container_name: nginx
    build:
      context: .
      dockerfile: ./nginx/Dockerfile
    image: nginx
    restart: always
    depends_on: 
      - django
    ports:
      - "80:80"

  django:
    container_name: django
    build:
      context: backend
      dockerfile: ./root/production/Dockerfile
    hostname: django
    ports:
      - 8000:8000
    volumes:
      - ./backend:/app/
    depends_on: 
      - postgres
    command: >
      bash -c '
      python3 ./root/manage.py makemigrations &&
      python3 ./root/manage.py migrate &&
      python3 ./root/manage.py initadmin &&
      gunicorn teddycrepineau.wsgi -b 0.0.0.0:8000 --chdir=./root/'

    env_file: .env

nginx.conf

user nginx;
worker_processes 1;

error_log   /var/log/nginx/error.log warn;
pid         /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include         /etc/nginx/mime.types;
    default_type    application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log      /var/log/nginx/access.log main;

    upstream app {
        server django:8000;
    }

    server {
        listen  80 default_server;
        listen  [::]:80 default_server;
        server_name 0.0.0.0;
        charset utf-80;

        root /usr/share/nginx/html;
        index index.html;

        location / {
            try_files $uri $uri/ @proxy_to_app;
        }

        location @proxy_to_app {
            rewrite ^(.+)$ /index.html last;
        }

        location ^~ /static/ {
            autoindex on;
            alias /usr/share/nginx/html/static/;
        }

        location ~ ^/api {
            proxy_pass http://django:8000;
        }
        location ~ ^/admin {
            proxy_pass http://django:8000;
        }
    }
}

nginx Dockerfile

FROM nginx:latest
ADD ./nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./frontend/build /usr/share/nginx/html
COPY ./backend/root/staticfiles /usr/share/nginx/html/static/staticfiles

Django Dockerfile

FROM python:3.7

ENV PYTHONUNBUFFERED 1

RUN export DEBIAN_FRONTEND=noninteractive

RUN mkdir /app

RUN pip install --upgrade pip
ADD /root/requirements.txt /app/

WORKDIR /app/
ADD . /app/

RUN pip install -r requirements.txt

EXPOSE 8000

Django settings.py

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
APPS_DIR = os.path.join(BASE_DIR, 'project')
....
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(APPS_DIR, 'media/')

Django urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^api/', include('project.api.urls')),
    path('summernote/', include('django_summernote.urls')),
] 
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


Update

When I mount a shared volum and reference it in my nginx.conf I get a 404 page not found when trying to access the image uploaded in the django backend.

docker-compose.yml

version: '3'

volumes:
  postgres_data: {}
  postgres_backup: {}

services:
  postgres:
    image: postgres
    volumes: 
      - postgres_data:/var/lib/postgresql/data
      - postgres_backup:/backups
    env_file: .env

  nginx:
    container_name: nginx
    build:
      context: .
      dockerfile: ./nginx/Dockerfile
    image: nginx
    restart: always
    depends_on: 
      - django
    ports:
      - "80:80"
    volumes:
      - ./static:/app/backend/root/staticfiles
      - ./media:/app/backend/root/project/media

  django:
    container_name: django
    build:
      context: backend
      dockerfile: ./root/production/Dockerfile
    hostname: django
    ports:
      - 8000:8000
    volumes:
      - ./backend:/app/
      - ./static:/app/backend/root/staticfiles
      - ./media:/app/backend/root/project/media
    depends_on: 
      - postgres
    command: >
      bash -c '
      python3 ./root/manage.py collectstatic --no-input &&
      python3 ./root/manage.py makemigrations &&
      python3 ./root/manage.py migrate &&
      python3 ./root/manage.py initadmin &&
      gunicorn teddycrepineau.wsgi -b 0.0.0.0:8000 --chdir=./root/'

    env_file: .env 

nginx.conf

user nginx;
worker_processes 1;

error_log   /var/log/nginx/error.log warn;
pid         /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include         /etc/nginx/mime.types;
    default_type    application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log      /var/log/nginx/access.log main;

    upstream app {
        server django:8000;
    }

    server {
        listen  80 default_server;
        listen  [::]:80 default_server;
        server_name localhost;
        charset utf-80;

        root /usr/share/nginx/html;
        index index.html;

        location / {
            try_files $uri $uri/ @proxy_to_app;
        }

        location @proxy_to_app {
            rewrite ^(.+)$ /index.html last;
        }

        location ^~ /static/ {
            autoindex on;
            alias /usr/share/nginx/html/static/;
        }

        location ^~ /media/ {
            autoindex on;
            alias /app/backend/root/project/media/;
        }

        location ~ ^/api {
            proxy_pass http://django:8000;
        }
        location ~ ^/admin {
            proxy_pass http://django:8000;
        }
    }
}

1 Answer 1

0

If this issue is occurring from Django only, you can try changing the static file format -

STATICFILES_DIRS = [
     os.path.join(BASE_DIR, 'static'),
 ]

generally some issue occurs with static files when we are working on production so it may help !

this doc can help !

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.