How to load static files using Nginx, Docker, and Django?
Problem:
I'm trying to load static files using nginx and gunicorn, it works when i go to http://0.0.0.0/static/css/base.css >static hosted via nginx, but doesn't when i add django port http://0.0.0.0:8000/static/css/base.css >static not found using django server
these are the errors i get in my nginx container
nginx_1 | 172.22.0.1 - - [04/Oct/2023:04:59:33 +0000] "GET /static/css/base.css HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36" "-" nginx_1 | 2023/10/04 04:59:34 [error] 8#8: *2 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.22.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "localhost", referrer: "http://localhost/static/css/base.css" nginx_1 | 172.22.0.1 - - [04/Oct/2023:04:59:34 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://localhost/static/css/base.css" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36" "-" |
This is all what I've tried, in conf.d i put container name as server_name and tried to add types to determine css files, but it didnt work settings.py
STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR / 'static' |
Dockerfile
FROM python:3.10
ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONBUFFERED=1 \ POETRY_VERSION=1.4.2 \ POETRY_VIRTUALENVS_CREATE="false"
RUN pip install "poetry==$POETRY_VERSION"
WORKDIR /education_platform
COPY pyproject.toml poetry.lock docker-entrypoint.sh ./
RUN poetry install --no-interaction --no-ansi --no-dev
COPY . /education_platform
EXPOSE 8000 RUN chmod +x wait-for-it.sh |
docker-compose.yaml
version: '3'
services: db: image: postgres:13.1-alpine restart: unless-stopped env_file: - ./.env volumes: - ./data/db:/var/lib/postgresql/data environment: - POSTGRES_DB=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres
cache: image: redis:7.0.4 restart: unless-stopped volumes: - .data/cache:/data
web: restart: unless-stopped build: context: . dockerfile: Dockerfile command: [ "/education_platform/wait-for-it.sh", "db:5432", "--", "gunicorn", "education_platform.wsgi:application", "-b", "0.0.0.0:8000"] env_file: - ./.env volumes: - .:/education_platform - ./static:/education_platform/static ports: - "8000:8000" environment: - DJANGO_SETTINGS_MODULE=education_platform.settings.prod - POSTGRES_DB=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres depends_on: - db - cache
nginx: image: nginx:1.23.1 restart: unless-stopped volumes: - ./config/nginx/conf.d:/etc/config/nginx/conf.d/default.conf - ./static:/usr/share/nginx/html/static ports: - "80:80" command: ["/bin/sh", "-c", "nginx -g 'daemon off;'"] depends_on: - web |
conf.d
server { listen 80; server_name education_platform_web_1; error_log stderr warn; access_log /dev/stdout main;
location / { proxy_pass http://education_platform_web_1:8000; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $host; }
location /static/ { alias /usr/share/nginx/html/static/;
}
location /media/ { alias /education_platform/media/; }
types { text/javascript js; application/javascript js; text/css css; } } |
Solution:
There are a couple of things missing for as far as I could see:
A volume in your Dockerfile that mounts the static files
Collectstatic
In order to prevent an extensive command in your docker-compose file I would like to suggest to following in your Dockerfile itself:
from this (docker-compose):
command: [ "/education_platform/wait-for-it.sh", "db:5432", "--", "gunicorn", "education_platform.wsgi:application", "-b", "0.0.0.0:8000"] |
to something like this (at the end of your Dockerfile):
ENTRYPOINT ["/entrypoint.sh"]
VOLUME ["/education_platform/static"]
EXPOSE 8000
CMD ["gunicorn", "education_platform.wsgi:application", "-b", "0.0.0.0:8000"] |
Now you might be wondering, where did my wait-for-it.sh script went to? And what is entrypoint.sh doing.
Here is the entrypoint.sh file:
#!/bin/bash
# wait for Postgres /wait-for-it.sh $POSTGRES_HOST:$POSTGRES_PORT -t 60
# run migrations, collect media, start the server python manage.py migrate echo "yes" | python manage.py collectstatic --noinput
exec $@ |
And, one more thing, I see two ways of specifying environment variables. Both the env_file and environment entries are being used in your docker-compose file. Now, there might be a valid reason in your case, but if not, it might be better to use one or the other:
web: restart: unless-stopped build: context: . dockerfile: Dockerfile env_file: <---- HERE - ./.env volumes: - .:/education_platform - ./static:/education_platform/static ports: - "8000:8000" environment: <---- HERE - DJANGO_SETTINGS_MODULE=education_platform.settings.prod - POSTGRES_DB=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres depends_on: - db - cache
|
Suggested blogs:
>How you can Show or hide elements in React?
>Implement nested serializers in the Django rest framework
>Define blade component property in server - Laravel
>Can not run phpstan under docker with memory lack error
>Attempt to read property "data_audit" on array laravel view loop foreach
>How to use start and limit on DataTables in Laravel for mobile API?
>Login to Laravel API- Laravel
>Make Xdebug stop at breakpoints in PhpStorm using Docker
>Creating a service in Laravel using app(FQCN)