Deployment Static Files not serving

Hi, I working with docker compose, nginx and gunicorn. I can’t get the static files to render. I’m fair new and have stumped for a few days now. Ill provide some details…

The site renders Django seems fine.

I can access the css sheet directly through the browser and I have these errors upon inspection on the page:

Thank you for any insight or feedback.

The Cross-Origin-Opener-Policy header has been ignored, because the URL's origin was untrustworthy. It was defined either in the final response or a redirect. Please deliver the response using the HTTPS protocol. You can also use the 'localhost' origin instead. See https://www.w3.org/TR/powerful-features/#potentially-trustworthy-origin and https://html.spec.whatwg.org/#the-cross-origin-opener-policy-header.Understand this errorAI
0.0.0.0/:1 Refused to apply style from 'http://0.0.0.0:8000/static/css/base.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.Understand this errorAI
django_workflow.jpg:1 
        
        
       Failed to load resource: the server responded with a status of 404 (Not Found)Understand this errorAI
0.0.0.0/:1 Refused to apply style from 'http://0.0.0.0:8000/static/css/base.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.Understand this errorAI
    listen 80;
    server_name localhost;

    location /static/ {
        alias /app/staticfiles/;
        autoindex on;                
        access_log /var/log/nginx/static_access.log;
        error_log /var/log/nginx/static_error.log debug;
    }

    location / {
        proxy_pass http://web:8000;  
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
# https://docs.djangoproject.com/en/5.1/howto/static-files/

STATIC_URL = '/static/'

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

#os.path.join(BASE_DIR, 'static'),

#STATIC_ROOT = BASE_DIR / 'staticfiles'
#STATIC_ROOT = str(BASE_DIR.joinpath('staticfiles'))

#Collect static here production
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

STATICFILES_FINDERS = [
    "django.contrib.staticfiles.finders.FileSystemFinder",
    "django.contrib.staticfiles.finders.AppDirectoriesFinder",

]```

```services:
  web:
    build:
      context: .
      dockerfile: Dockerfile  
    command: gunicorn config.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - static_volume:/app/staticfiles
    env_file:
      - .env
    ports:
      - "8000:8000"  
    depends_on:
      - db

  db:
    image: postgres:14
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    environment:
      POSTGRES_USER: django_user
      POSTGRES_PASSWORD: password3
      POSTGRES_DB: django_db

  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - static_volume:/app/staticfiles
    depends_on:
      - web

volumes:
  static_volume:
  postgres_data:  

Logs


/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: /etc/nginx/conf.d/default.conf differs from the packaged version
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2024/12/18 13:31:57 [notice] 1#1: using the "epoll" event method
2024/12/18 13:31:57 [notice] 1#1: nginx/1.27.3
2024/12/18 13:31:57 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14) 
2024/12/18 13:31:57 [notice] 1#1: OS: Linux 6.10.14-linuxkit
2024/12/18 13:31:57 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2024/12/18 13:31:57 [notice] 1#1: start worker processes
2024/12/18 13:31:57 [notice] 1#1: start worker process 28
2024/12/18 13:31:57 [notice] 1#1: start worker process 29
2024/12/18 13:31:57 [notice] 1#1: start worker process 30
2024/12/18 13:31:57 [notice] 1#1: start worker process 31
(my_blog-9hdozHfb) joelburns@Joels-iMac my_blog % 

What is your STATIC_URL setting set to? According to your nginx config it should be STATIC_URL = "/static/"

What do your nginx access and error logs look like for the requests for your static files?

When and how are you running collectstatic to copy those files to the volume? (You need to ensure the volume gets initialized every time, otherwise it’s only initialized when it’s first created.)

My static URL in Nginx matches the the static URL in Django settings.py.

Here is my Dockerfile:

FROM python:3.11-slim

# Set working directory
WORKDIR /app

#Install dependencies 
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

EXPOSE 8000

RUN python manage.py collectstatic --noinput

CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"]

How would I go about checking the volume is initialized everytime?

The logs I also provided above, I used docker compose nginx container logs for the output. Is there another method?

Hi, so newbie mistake, it took me this long to figure out. Everything was configured correctly. I was directly accessing gunicorn through my browser… I needed to access through Nginx.
The static files had been collected and stored in Nginx as they should’ve been. Everthing is now functioning as it should so far…

Could you please describe how you fixed it?

So, from my current understanding of how this all works:

The 8000 port being exposed from the docker file is the Gunicorn server. I was accessing my application through this port and bypassing the NGINX server completely. Hence no static files being rendered.

My current configuration in docker-compose has the collected static files mounted on a volume in the NGINX server. I have the volume in my application disabled because the possibility of issues with the redundancy of volumes having duplicate data.

I’m attempting to model this setup according to best practices.

I’m trying to implement NGINX as a reverse proxy, handle requests and to serve the static files.

So, access through here 0.0.0.0:8000 bypasses NGINX
I needed to access through localhost or 80 which has the collected static files
(port 80 is the default port for HTTP requests, 443 for HTTPS)

Again, I could be wrong or missing something but, this is my level of understanding at this point.

FROM python:3.11-slim

# Set working directory
WORKDIR /app

#Install dependencies 
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

Docker
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy the project files
COPY . .

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Expose the application port
EXPOSE 8000

# Collect static files and run migrations during the build
RUN python manage.py collectstatic --noinput

# Start the application
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"]

Docker Compose

  web:
    build:
      context: .
      dockerfile: Dockerfile  
    command: gunicorn config.wsgi:application --bind 0.0.0.0:8000
    #volumes:
      #- #static_volume:/app/staticfiles
    env_file:
      - .env
    ports:
      - "8000:8000"  
    depends_on:
      - db

  db:
    image: postgres:14
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    environment:
      POSTGRES_USER: django_user
      POSTGRES_PASSWORD: password3
      POSTGRES_DB: django_db

  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - static_volume:/app/staticfiles
    depends_on:
      - web

volumes:
  static_volume:
  postgres_data:  
2 Likes