In production I would consider it best practice to use a S3 solution for serving assets. Namely static files and user-generated media.
This describes my setup on how to do this locally too.
The main benefit for me is that there is less of a difference between environments and I can test S3 specific features in my app.
Setup
I will assume a already working Django project and MacOS with [[brew]] installed, but brew specific parts are easilly replicated on different systems using their native package managers.
# Installation
brew install minio/stable/minio minio/stable/mc
# Prepare directory for MinIO
cd PROJECT_ROOT
echo "# Local storage for MinIO"
echo ".local-s3" >> .gitignore
mkdir local-s3
# Start MinIO to check it works
minio server local-s3 --console-address "127.0.0.1:9090"
Afterwards you can create a bucket using the minio UI or CLI and a user.
Django Configuration
Disclaimer: You find a lot of ways to do this online, some of them working, some of them not. This is what I came up with and it works for me, your mileage may vary.
Some notes on the configuration and why I do things the way I do:
- As local development credentials are of no security concern to me I use
os.environ.get()
defaults to just fall back to local settings - In production I simply inject environment variables and get it working with my production MinIO or DigitalOcean Spaces
- I want to separate static files and user uploads, the best way I know is using custom storage backends
# S3 settings
AWS_ACCESS_KEY_ID = os.environ.get("S3_ACCESS_KEY", "***")
AWS_SECRET_ACCESS_KEY = os.environ.get("S3_SECRET_KEY", "***")
AWS_STORAGE_BUCKET_NAME = os.environ.get("S3_BUCKET_NAME", "***")
AWS_S3_ENDPOINT_URL = os.environ.get("S3_ENDPOINT", "http://127.0.0.1:9000")
# Staticfiles backend & URL
STATICFILES_STORAGE = "utils.storage_backends.StaticStorage"
STATIC_URL = f"{AWS_S3_ENDPOINT_URL}/{AWS_STORAGE_BUCKET_NAME}/static/"
# Media files backend & URL
DEFAULT_FILE_STORAGE = "utils.storage_backends.PublicMediaStorage"
MEDIA_URL = f"{AWS_S3_ENDPOINT_URL}/{AWS_STORAGE_BUCKET_NAME}/media/"
"""Custom S3 storage backends to allow for scoping of static files and user uploads"""
from storages.backends.s3boto3 import S3Boto3Storage
class StaticStorage(S3Boto3Storage):
location = "static"
default_acl = "public-read"
class PublicMediaStorage(S3Boto3Storage):
location = "media"
default_acl = "public-read"
file_overwrite = False