Cookiecutter-django: Add nginx to serving static files

Created on 13 Nov 2019  路  4Comments  路  Source: pydanny/cookiecutter-django

Add nginx as a option like whitenoise, google storage and aws to handle static only would be cool.

The current options require third party connections / configuration to work ..
While this is not fully enforced by whitenoise, it is highly recommended.

I saw that in the past, nginx was used, but it was tricky to implement lets encrypt, but using nginx behind traefik would not be a problem, since it does not have to handle ssl. Also, no third party configs are required.

Most helpful comment

@marciks my network situation required the same thing. Here's what I added to this project to serve static files from a docker volume.

Note: for convenience I use dockerhub's base nginx image, it was super easy plug and play.

Create a docker volume to the files you want to serve

docker volume create {VOLUME_CONFIG_PARAMS} {DOCKER_VOLUME}

In this example DOCKER_VOLUME_TARGET was the name of the directory within the container that contains the static files.

Add the nginx container

production.yml additions

  nginx:
    image: nginx
    volumes:
      - {DOCKER_VOLUME}:/usr/share/nginx/html/{DOCKER_VOLUME_TARGET}
    ports:
      - "8099:80"

The only nginx config needed is mapping the docker volume to the default /usr/share/nginx/html/ location and mapping the default external port to an internal port 80 so that nginx can act as a webserver. It can't have an external port because traefik is using that.

Tell Django to look here for static files

In .django config
DJANGO_STATIC_ROOT=/{DOCKER_VOLUME_TARGET}/staticfiles/

Finally connect this in to Traefik

traefik.toml additions

[file]
[backends]
  [backends.django]
    [backends.django.servers.server1]
      url = "http://django:5000"
  [backends.nginx]
    [backends.nginx.servers.server1]
      url = "http://nginx"

.
.
.

[frontends.nginx]
    backend = "nginx"
    passHostHeader = true
    [frontends.nginx.routes.dr1]
      rule = "{HOST};PathPrefix:/{DOCKER_VOLUME_TARGET}/staticfiles"

You may have already finished your solution by now, but let me know if there's anything I can clarify, and good luck!

All 4 comments

@marciks
Hello, I thought the same thing as you about 2 months ago for a small project, I created a version that allows to have nginx that serves media and static files, the problem is that I couldn't 'scale' the docker since it is directly linked to the django container, there would be the possibility by making a docker who only shares files, but I found a better alternative that is much simpler and does not require a big change in django cookie cutter, The use of "MINIO" that has exactly the same "API" as amazon, I share from a cache.domainname.com, there is a little hack to do at the settings level but nothing complicated :)

example .env/.production/.django:

# AWS
# ------------------------------------------------------------------------------
DJANGO_AWS_ACCESS_KEY_ID=key
DJANGO_AWS_SECRET_ACCESS_KEY=key
DJANGO_AWS_STORAGE_BUCKET_NAME=namebucket
DJANGO_AWS_S3_ENDPOINT_URL=https://cache.domainname.com

example setting:

# STORAGES
# ------------------------------------------------------------------------------
# https://django-storages.readthedocs.io/en/latest/#installation
INSTALLED_APPS += ["storages"]  # noqa F405
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
AWS_ACCESS_KEY_ID = env("DJANGO_AWS_ACCESS_KEY_ID")
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
AWS_SECRET_ACCESS_KEY = env("DJANGO_AWS_SECRET_ACCESS_KEY")
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
AWS_STORAGE_BUCKET_NAME = env("DJANGO_AWS_STORAGE_BUCKET_NAME")
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
AWS_QUERYSTRING_AUTH = False
# DO NOT change these unless you know what you're doing.
_AWS_EXPIRY = 60 * 60 * 24 * 7
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
AWS_S3_OBJECT_PARAMETERS = {
    "CacheControl": f"max-age={_AWS_EXPIRY}, s-maxage={_AWS_EXPIRY}, must-revalidate"
}
#  https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
AWS_DEFAULT_ACL = None
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
AWS_S3_REGION_NAME = env("DJANGO_AWS_S3_REGION_NAME", default=None)
# STATIC
# ------------------------
STATICFILES_STORAGE = "config.settings.production.StaticRootS3Boto3Storage"
COLLECTFAST_STRATEGY = "collectfast.strategies.boto3.Boto3Strategy"
AWS_S3_ENDPOINT_URL = env("DJANGO_AWS_S3_ENDPOINT_URL", default=None)
AWS_S3_USE_SSL = True
AWS_AUTO_CREATE_BUCKET = True
STATIC_URL = f"{AWS_S3_ENDPOINT_URL}/{AWS_STORAGE_BUCKET_NAME}/static/"
# MEDIA
# ------------------------------------------------------------------------------
# region http://stackoverflow.com/questions/10390244/
# Full-fledge class: https://stackoverflow.com/a/18046120/104731
from storages.backends.s3boto3 import S3Boto3Storage  # noqa E402


class StaticRootS3Boto3Storage(S3Boto3Storage):
    location = "static"
    default_acl = "public-read"


class MediaRootS3Boto3Storage(S3Boto3Storage):
    location = "media"
    file_overwrite = False


# endregion
DEFAULT_FILE_STORAGE = "config.settings.production.MediaRootS3Boto3Storage"
MEDIA_URL = f"{AWS_S3_ENDPOINT_URL}/{AWS_STORAGE_BUCKET_NAME}/media/"

@scwall Thanks for your tip! I actually wanted to avoid the configuration on third party services like gcloud, AWS or cloud flare just for serving static.. the only way to do this is by using a webserver (which traefik is not).. also, I think that it is possible to run in a separate container! This answer on stackoverflow explains it a little bit: https://stackoverflow.com/a/51903852/8213102

@marciks my network situation required the same thing. Here's what I added to this project to serve static files from a docker volume.

Note: for convenience I use dockerhub's base nginx image, it was super easy plug and play.

Create a docker volume to the files you want to serve

docker volume create {VOLUME_CONFIG_PARAMS} {DOCKER_VOLUME}

In this example DOCKER_VOLUME_TARGET was the name of the directory within the container that contains the static files.

Add the nginx container

production.yml additions

  nginx:
    image: nginx
    volumes:
      - {DOCKER_VOLUME}:/usr/share/nginx/html/{DOCKER_VOLUME_TARGET}
    ports:
      - "8099:80"

The only nginx config needed is mapping the docker volume to the default /usr/share/nginx/html/ location and mapping the default external port to an internal port 80 so that nginx can act as a webserver. It can't have an external port because traefik is using that.

Tell Django to look here for static files

In .django config
DJANGO_STATIC_ROOT=/{DOCKER_VOLUME_TARGET}/staticfiles/

Finally connect this in to Traefik

traefik.toml additions

[file]
[backends]
  [backends.django]
    [backends.django.servers.server1]
      url = "http://django:5000"
  [backends.nginx]
    [backends.nginx.servers.server1]
      url = "http://nginx"

.
.
.

[frontends.nginx]
    backend = "nginx"
    passHostHeader = true
    [frontends.nginx.routes.dr1]
      rule = "{HOST};PathPrefix:/{DOCKER_VOLUME_TARGET}/staticfiles"

You may have already finished your solution by now, but let me know if there's anything I can clarify, and good luck!

Hey @nickdnickd! I have found everything I needed in this repo: https://github.com/umputun/nginx-le
I will be using nginx instead of traefik..
Thanks for your help though! For clients, less registration is better and in case I need django cookiecutter, I will definitely use your work around

Was this page helpful?
0 / 5 - 0 ratings

Related issues

webyneter picture webyneter  路  3Comments

yunti picture yunti  路  4Comments

sebastian-code picture sebastian-code  路  4Comments

yemarnevets picture yemarnevets  路  3Comments

pygabo picture pygabo  路  3Comments