15

When i try to access http://127.0.0.1:8000/admin, i get this.

enter image description here

my folder structure is :

django-react-nginx
|
|_ _ docker-compose.yml
|
  > backend
        |
        |_ Dockerfile
        |
        |_ entrypoint.sh

     > languages
          |
          |_ settings.py
     > media
     > static # This folder appears after running docker-compose -d --build
  > nginx
      |
      |_ default.conf
      |
      |_ Dockerfile

now

here is the files

Django

settings.py

DEBUG = True

ALLOWED_HOSTS = ['*']

STATIC_URL = '/static/'

STATIC_ROOT = os.path.join(BASE_DIR, 'static')

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')        

MEDIA_URL = '/media/'

Docker file

FROM python:3.8

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

WORKDIR /backend

COPY  requirements.txt /backend/

RUN pip install -r requirements.txt && \
    pip install --upgrade pip

COPY ./entrypoint.sh /

ENTRYPOINT ["sh", "/entrypoint.sh"]

entrypoint.sh

#!/bin/sh

python manage.py migrate --no-input

python manage.py collectstatic --no-input

gunicorn languages.wsgi:application --bind 0.0.0.0:8000

Nginx

default.conf

upstream django {
    server backend:8000;
}

server {
    listen 80;

    location / {
        proxy_pass http://django;
    }

    location /static/ {
        autoindex on;
        alias /backend/static;
    }

    location /media/ {
        autoindex on;
        alias /backend/static;
    }

}

Dockerfile

FROM nginx:1.19.8-alpine

COPY ./default.conf  /etc/nginx/conf.d/default.conf

Root Folder

Docker-compose

version: "3.7"

services:
  backend:
    build: ./backend/languages
    stdin_open: true # interactive
    tty: true        # interactive
    restart: "on-failure"
    env_file:
      .env
    volumes:
      - ./backend/languages:/backend
      - ./backend/languages/static:/backend/static
    ports:
      - 8000:8000
    networks:
      - nginx_network
      - db_network
    depends_on:
      - db
  db:
    image: postgres:11
    restart: "on-failure"
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    networks:
      - db_network
  
  nginx:
    build: ./nginx
    restart: always
    volumes:
      - ./backend/languages:/backend
      - ./backend/languages/static:/static
    ports:
      - 80:80
    networks:
      - nginx_network
    depends_on:
      - backend

networks:
  nginx_network:
    driver: bridge
  db_network:
    driver: bridge

volumes:
  postgres_data:

As mentioned above i am not sure how to make the static files work, they been copied to the folder mentioned above because when i can see the folder created and when i check the logs i get this 165 static files copied to '/backend/static'.

desertnaut
  • 57,590
  • 26
  • 140
  • 166
cu__007
  • 495
  • 1
  • 4
  • 14
  • Does this answer your question? [serving Django static files with Docker, nginx and gunicorn](https://stackoverflow.com/questions/50685775/serving-django-static-files-with-docker-nginx-and-gunicorn) – addu390 Feb 10 '22 at 18:54

2 Answers2

10

Nginx config

Nginx config looks fine. Container is mapped to /backend/languages/static with /static and the alias points to the same folder inside container - /static.

nginx.conf

location /static/ {
        autoindex on;
        alias /static;
    }

upd issue detected: an alias must have the same ending slash to work properly. so it has to be alias /static/

nginx in compose

  nginx:
    ...
    volumes:
      - ./backend/languages:/backend
      - ./backend/languages/static:/static

Django config

But Django config looks broken. Here you've configured to collect static to static folder within BASE_DIR

settings.py

STATIC_ROOT = os.path.join(BASE_DIR, 'static')
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')        

and this is exactly what collectstatic is reporting to you:

165 static files copied to '/backend/static'

files are in the /backend/static, not /static. Hovewer the container is configured to root folder /static:

django in compose

  backend
    ...
    volumes:
      - ./backend/languages:/backend
      - ./backend/languages/static:/static

looks like the issue can be fixed by pointing to

    volumes:
      - ./backend/languages/static:/backend/static

Nevertheless there is still some work to do: media files are not supposed to be served by Django as well, so it is recommended to configure Nginx to serve media files too.

Django Dockerfile

I believe the mapping ./backend/languages:/backend works but what's the point of dockerizing then? There is no app in this docker image, just dependencies. It is "better" to include sources in the image so the deployment in the end would require to update images and restart containers only.

So, as a side note, I'd suggest to (at least try to):

  • update configs to serve media files with Nginx
  • include django app sources inside your docker image
  • collect static files during the build and not to include them into docker image
  • remove collectstatic from the entrypoint
  • supply static files and treat them as a separate product, think of them if you were going to deliver them to a CDN, a separate hosting - it is a very common solution
  • whilst they are still on the same hosting, keep mapping them to the container as you do now, but in a "reversed" way: deliver static files to the host folder, access them from the container in ro mode
Ivan Starostin
  • 8,798
  • 5
  • 21
  • 39
  • thank you so much for the detailed response Ivan. I am super new to Docker therefore not 100% of what i am doing, even after reading all these books about Docker. Do you mind showing me what you mean by the last two bullet points above? I am not sure to understand how to do it myself at this point. I have updated the codes above to serve media files in nginx Thank you so much again – cu__007 Mar 24 '21 at 12:29
  • Currently you perform `collectstatic` inside container and produce static files from the inside of container out to the host. My suggestion is to collect static during **build** phase and treat static files as one of **build artifacts**. After that deploy them to the destinations folder (which may be far away from project sources) and map docker container volume to that folder. `ro` is read-only. Someday you might consider storing static files on a CDN service / different hosting and running `collectstatic` from container _each time it starts_ would become a strange and useless operation. – Ivan Starostin Mar 24 '21 at 13:26
  • However this is still a suggestion, one of possible ways to go and is not required. – Ivan Starostin Mar 24 '21 at 13:27
  • If your original problem with paths solver please mark the answer as accepted. – Ivan Starostin Mar 24 '21 at 15:44
  • the biggest problem was just so simple in the main urls.py i should have used `urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)` instead of `urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)` thats the solution – cu__007 Mar 24 '21 at 22:33
  • 2
    Not really. On prod and/or with Debug=False Django is not supposed to server neither static nor media files. These url patterns work when Debug=True only and are no good for prod. – Ivan Starostin Mar 24 '21 at 22:39
  • I see, i mean locally I see the static files and thats what i needed now to build an app and see the changes and also i checked the container of nginx, all the data are copied there when i check with `docker exec -it imageID /bin/sh` I believe in prod i should be able to work with static_files + media_files, no? – cu__007 Mar 24 '21 at 22:59
  • I'm not sure I understood described combination of nginx + files and django with debug=true. Django should not serve those files. Also if those urlpatterns helped **with nginx running** then nginx does not seem to participate at all, perhaps there are bigger problems with configs. Also note `upd` in the answer about nginx conf. Small error detected. And of course logs of nginx, django and other components should tell more about errors. – Ivan Starostin Mar 25 '21 at 05:39
  • got it this is a very informative conversation. so i might be serving them from django not nginx at the end of the day, which is not correct. reading more about nginx to find ou. thank you for your time. – cu__007 Mar 25 '21 at 10:05
1

You can use whitenoise by configuring it in your settings file. Set up Whitenoise with Django here

Abdul Wahab
  • 27
  • 1
  • 4
  • 1
    [Link only answers](https://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-answers/8259#8259) are considered very low quality and [can get deleted](https://stackoverflow.com/help/deleted-answers), please put the important parts from the linked resource into the answer body. – helvete Dec 06 '21 at 16:36
  • 1
    It would be better if you could explain how whitenoise would help specifically in this situation. Copying a link is way too vague – roshandeep singh Oct 19 '22 at 16:57