S3 (Object Storage) Provider¶
The S3 provider requires the [s3] extra to be installed. It uses RustFS by default -- a fully S3-compatible, Apache 2.0 licensed storage server.
Why RustFS instead of MinIO?
MinIO's open-source repository was archived in February 2026. Docker images and pre-built binaries are no longer published. RustFS is API-compatible and uses the same port conventions (9000/9001), making migration seamless.
Installation¶
This installs boto3, which is used for bucket creation and is typically already present in projects using django-storages.
Auto-Detection¶
The provider activates automatically from any of these settings:
| Setting | Detected when |
|---|---|
STORAGES[*]['BACKEND'] |
Contains s3boto3 |
DEFAULT_FILE_STORAGE |
Contains s3boto3 |
AWS_STORAGE_BUCKET_NAME |
Present and non-empty |
Minimal Setup¶
TEST_RUNNER = 'django_testcontainers_plus.runner.TestcontainersRunner'
STORAGES = {
'default': {
'BACKEND': 'storages.backends.s3boto3.S3Boto3Storage',
},
'staticfiles': {
'BACKEND': 'storages.backends.s3boto3.S3StaticStorage',
}
}
AWS_STORAGE_BUCKET_NAME = 'test-uploads'
A single S3 container serves both media and static files.
Default Container¶
| Property | Value |
|---|---|
| Image | rustfs/rustfs |
| S3 API port | Randomly assigned by Docker (maps to 9000) |
| Console port | Randomly assigned by Docker (maps to 9001) |
| Access key | rustfsadmin |
| Secret key | rustfsadmin |
Configuration Options¶
TESTCONTAINERS = {
's3': {
'image': 'rustfs/rustfs',
'access_key': 'rustfsadmin', # used for boto3 + container auth
'secret_key': 'rustfsadmin',
'bucket_name': 'test-bucket', # auto-created bucket
'environment': { # extra container env vars
'RUSTFS_CONSOLE_ENABLE': 'true',
},
}
}
Settings Injection¶
After the S3 container starts, the provider updates your settings automatically:
| Setting | Value |
|---|---|
AWS_S3_ENDPOINT_URL |
http://{host}:{port} pointing to the container |
AWS_ACCESS_KEY_ID |
Container access key (always overridden to match container) |
AWS_SECRET_ACCESS_KEY |
Container secret key (always overridden to match container) |
AWS_STORAGE_BUCKET_NAME |
Bucket name (only if not already set) |
S3_CONSOLE_URL |
URL to the RustFS web console |
The provider also auto-creates the bucket specified in AWS_STORAGE_BUCKET_NAME (or test-bucket by default).
Example Test¶
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
def test_file_upload():
# RustFS container is automatically started and configured
content = ContentFile(b"Test file content")
path = default_storage.save('uploads/test.txt', content)
assert default_storage.exists(path)
assert default_storage.open(path).read() == b"Test file content"
default_storage.delete(path)
assert not default_storage.exists(path)