Local S3 with MinIO in Django


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

settings.py

# 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/"

utils/storage_backend.py

"""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
python  django  s3