Skip to content

Instantly share code, notes, and snippets.

@dungdm93 dungdm93/0-README.md
Last active Jul 7, 2019

Embed
What would you like to do?
Django serve staticfiles

Django Staticfiles

Django staticfiles is the most common problem you need to solve before putting your app on the production. In this tutorial, I and you will walk through the solution options, included.

  • runserver: Only in development environment.
  • gunicorn: Use gunicorn as your app server.
  • nginx: Put nginx web server in a front of your app server (gunicorn).
  • whitenoise: Let your django app self-serve static files.

TL;DR

Goto session 4: whitenoise

runserver

In order to python manage.py runserver serve staticfiles, bellow configs are required:

  1. In setting.py

    DEBUG = True

That's all. However, runserver should never used in production because of performance reasons.

gunicorn

In production environment, you should use gunicorn as Python WSGI HTTP Server. However, in order to gunicorn serve static files, you need some extra configs:

  1. Enable debug mode

    # settings.py
    DEBUG = True
  2. Configs STATIC_ROOT and urlpatterns

    # settings.py
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/1.11/howto/static-files/
    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR, 'static')
    # urls.py
    from django.conf.urls.static import static
    from . import settings
    
    urlpatterns = [ ... ]
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
  3. Collect statics files, by running this command:

    python manage.py collectstatic

Now, run gunicorn YOUR_APP.wsgi to see the result.

WebServer

gunicorn is a good App Server, but enable DEBUG mode is not recommended in production. One option is put a Web Server like NginX, Apache HTTPD,... in front of gunicorn and let it serve your static files.

1. NginX

upstream app_server {
    # fail_timeout=0 means we always retry an upstream even if it failed
    # to return a good HTTP response

    # for a TCP configuration
    server 127.0.0.1:8000 fail_timeout=0;

    # for UNIX domain socket setups
    # server unix:/run/gunicorn.sock fail_timeout=0;
}

server {
    listen 80;
    server_name example.com;

    keepalive_timeout 5;
    client_max_body_size 4G;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /absolute/path/to/your/static/dir;
    }

    location / {
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $server_name;
        proxy_set_header X-Real-IP         $remote_addr;

        proxy_pass http://app_server;
    }

    error_page 500 502 503 504 /500.html;
    location = /500.html {
        root /absolute/path/to/your/static/dir;
    }
}

whitenoise

NginX/Apache HTTPD work every well in VM world. If your deployment environment use container technologies like Docker and Kubernetes, it become bulky and hard to config. Recently, I discovered a lighter alternative solution call whitenoise

  1. Install

    pip install whitenoise

    or, add it into requirements.txt

    whitenoise~=4.1
    
  2. add WhiteNoise to the MIDDLEWARE_CLASSES list

    # settings.py
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'whitenoise.middleware.WhiteNoiseMiddleware',
        ...
    ]

    Note: WhiteNoiseMiddleware must be above all other middleware apart from Django’s SecurityMiddleware.

  3. Configs STATIC_ROOT

    # settings.py
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/1.11/howto/static-files/
    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR, 'static')
  4. And you still need collect statics files

    python manage.py collectstatic

That's it. Now you can run whatever app server you want, somethings like:

python manage.py runserver
# or
gunicorn YOUR_APP.wsgi # change YOUR_APP to your real app

Bonus

  1. Dockerfile

    FROM python:3.6
    
    LABEL maintainer="Development Team<dev@company.com>"
    WORKDIR /app
    EXPOSE 8000
    
    ADD requirements.txt /app/requirements.txt
    RUN pip install --no-cache-dir -r requirements.txt
    ADD . /app
    RUN python manage.py collectstatic --no-input
    
    CMD [ "gunicorn", "YOUR_APP.wsgi", "--bind=0.0.0.0:8000" ]
  2. docker-compose.yml

    version: "3.5"
    
    services:
      django:
        build: .
        # Change image if you prefer different name
        image: django/demo:dev
        # Override command in Dockerfile for development environment
        command: [ "python", "manage.py", "runserver", "0.0.0.0:8000" ]
        ports:
        - 8000:8000
        # Mount source code for hot reload
        volumes:
        - .:/app
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.