Store Django Media Files on Backblaze B2
Store Django uploads on Backblaze B2 through its S3-compatible API: create a bucket and application key, configure django-storages with the B2 endpoint, serve private files with presigned URLs, and put public files behind Cloudflare.
Problem statement
Backblaze B2 is low-cost object storage with an S3-compatible API. For Django apps with lots of user uploads (photos, documents, audio), it can cut storage costs substantially compared with the big clouds, and its egress partnership with Cloudflare can make public file delivery inexpensive. Using it from Django takes the S3 backend in django-storages, the right endpoint, an application key scoped to one bucket, and a few B2-specific details.
- Create a private bucket in B2 and note its S3 endpoint (for example
s3.eu-central-003.backblazeb2.com). - Create an application key restricted to that bucket with read and write capabilities.
- Configure
S3Storagewithendpoint_url,bucket_name,region_name(the region part of the endpoint), and the key ID and key. - Keep files private and use presigned URLs, or make a separate public bucket and serve it through Cloudflare.
- Set lifecycle rules: B2 keeps old file versions by default unless you configure otherwise.
- Use unique upload paths.
Step-by-step solution
1. Bucket and application key
In the Backblaze console, create a bucket (Files in Bucket: Private). The bucket details show the S3 endpoint. Under App Keys, create a key with access to only that bucket. The keyID is the access key and the applicationKey is the secret; the secret is shown once, so store it immediately in your secrets manager.
2. Configure Django
STORAGES = {
"default": {
"BACKEND": "storages.backends.s3.S3Storage",
"OPTIONS": {
"bucket_name": "myapp-media",
"endpoint_url": "https://s3.eu-central-003.backblazeb2.com",
"region_name": "eu-central-003",
"access_key": env("B2_KEY_ID"),
"secret_key": env("B2_APP_KEY"),
"default_acl": None, # B2 uses bucket-level visibility
"querystring_auth": True,
"querystring_expire": 900,
"file_overwrite": False,
"signature_version": "s3v4",
},
},
"staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"},
}
B2 does not support per-object ACLs the way S3 does; visibility is set per bucket. Leave default_acl unset so uploads do not fail on unsupported ACL headers.
3. Public files through Cloudflare
For public assets, create a second bucket set to Public and serve it through a Cloudflare-proxied CNAME pointing at the bucket's friendly hostname, with a Cloudflare rule rewriting the path to /file/<bucket>/.... Data transfer from B2 to Cloudflare is free under their Bandwidth Alliance, which makes this a cheap way to serve large public media. Configure custom_domain on a public storage alias accordingly.
4. Lifecycle rules
By default, B2 keeps every version of a file: deleting or overwriting creates a hidden version that still counts toward storage. Set the bucket lifecycle to "Keep only the last version" for media, or keep versions for a fixed number of days if you want recovery from accidental deletes.
5. Backups and redundancy
B2 stores data in one region. For important media, replicate to another region with B2 Cloud Replication or copy to a different provider with rclone.
Verification check: upload a file through the admin and see it in the B2 browser; open the presigned URL from the page and confirm it expires; delete the file in Django and confirm the lifecycle rule removes the hidden version on schedule.
Explanation
Backblaze offers a native B2 API and an S3-compatible API. The S3-compatible endpoint supports SigV4 authentication, presigned URLs, and multipart uploads, so boto3 and django-storages work unchanged apart from the endpoint and a few unsupported features such as object ACLs. B2's versioning-by-default and bucket-level visibility reflect its backup-storage origins, which is why lifecycle rules and bucket separation matter more than on S3.
Edge cases / notes
- Application key capabilities must include
listBucketsfor some client operations; if uploads fail with access errors, check the key's capabilities. - Large files use multipart uploads automatically through boto3.
- Download fees apply above a free allowance when not going through partners such as Cloudflare.
- CORS for direct browser uploads is configured per bucket with the B2 CLI or API.
Next steps
- Compare with another low-cost option: Store Django Media Files on Cloudflare R2.
- Keep copies elsewhere with Backup Django Media Files.
FAQ
Does django-storages work with Backblaze B2?
Yes, through the S3 backend and B2's S3-compatible endpoint.
Why do uploads fail with an ACL error?
B2 does not support per-object ACLs. Leave default_acl unset.
Why is my B2 storage larger than expected?
B2 keeps old versions by default. Add a lifecycle rule to keep only the latest version.
How do I serve public B2 files cheaply?
Through Cloudflare, since transfer from B2 to Cloudflare is free under their partnership.