Introduction
Deploying a Django app with AWS S3 for media files and static files seems like a simple checklist item at first glance.
But when you actually get your hands dirty, things quickly spiral out of control:
- Access Denied errors,
- ACL configuration nightmares,
- Bucket Policies blocking everything,
- A broken Django Admin with no CSS,
- Silent upload failures,
- And general confusion over how public access actually works in S3.
As a developer, I wanted to make my project more scalable and more professional by hosting all my images, CSS, and JavaScript assets on AWS S3 instead of keeping everything on the local server.
This is the full journey — the mistakes, the fixes, and the lessons I learned — to build a reliable, fast, and secure integration between Django and AWS S3.
If you're planning to connect Django to S3, this guide is for you — no sugarcoating, real-world problems included.
1. Creating the S3 Bucket
First step: create a new S3 bucket.
AWS Console ➔ S3 ➔ Create bucket.
First trap:
AWS by default recommends blocking all public access.
So I did.
Big mistake.
- If you block everything, even your own frontend, Django, and yourself won't be able to access the files.
Real solution:
- ✅ Keep \"Block public access to new and existing ACLs\" checked.
- ❌ Uncheck \"Block public access via new bucket policies\" and \"Block public access via any bucket policies\".
This way, you can control public access only through a Bucket Policy.
2. Setting the Bucket Policy
Next, you need to explicitly allow public read access using a Bucket Policy.
Here’s the policy I applied:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPublicReadAccessToObjects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
✅ This allows everyone to read your files,
❌ but nobody can upload, modify, or delete files unless you authorize them.
3. Creating an IAM User and Access Keys
Next step: create an IAM user dedicated to accessing S3 from your Django app.
In AWS Console ➔ IAM ➔ Users ➔ Create user:
- Programmatic access ✅
- Attach policy ➔ AmazonS3FullAccess (for setup, then you can restrict later)
Save your:
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
You will need them in your Django settings.
4. Configuring Django to Use S3
Inside your settings.py
:
AWS_ACCESS_KEY_ID = '...'
AWS_SECRET_ACCESS_KEY = '...'
AWS_STORAGE_BUCKET_NAME = 'your-bucket-name'
AWS_S3_REGION_NAME = 'eu-north-1'
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.{AWS_S3_REGION_NAME}.amazonaws.com'
STATICFILES_STORAGE = 'storage.StaticStorage'
DEFAULT_FILE_STORAGE = 'storage.MediaStorage'
STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/static/'
MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/media/'
Inside storage.py
:
from storages.backends.s3boto3 import S3Boto3Storage
class StaticStorage(S3Boto3Storage):
location = 'static'
class MediaStorage(S3Boto3Storage):
location = 'media'
✅ Now Django will upload and fetch media and static files directly from S3.
5. Real Problems I Faced and How I Solved Them
Problem 1: AccessDenied when trying to read files
- ✅ Solved by correctly setting the Bucket Policy.
Problem 2: AccessControlListNotSupported error during upload
- My bucket had \"Bucket owner enforced\" enabled, meaning ACLs are not allowed.
- ✅ Solution: do not use
--acl public-read
when uploading with AWS CLI.
Instead, simply upload:
aws s3 cp media/ s3://your-bucket-name/media/ --recursive
Problem 3: Django Admin with no CSS, no JavaScript
- Because static files were not uploaded to S3 yet.
- ✅ Solution:
python manage.py collectstatic
aws s3 cp staticfiles/ s3://your-bucket-name/static/ --recursive
(Always upload from staticfiles/
, not from static/
.)
6. Automating Static Uploads with a Simple Bash Script
I created a simple upload_static.sh
script to save time:
#!/bin/bash
echo "📦 Collecting static files..."
python manage.py collectstatic --noinput
echo "☁️ Uploading static files to AWS S3..."
aws s3 cp staticfiles/ s3://your-bucket-name/static/ --recursive
echo "✅ Upload complete!"
Make it executable:
chmod +x upload_static.sh
Now, just run:
./upload_static.sh
and everything gets updated in seconds 🚀.
Conclusion
Connecting Django to AWS S3 seems easy, but the devil is in the details:
- Understanding how S3 public access really works,
- Managing Bucket Policies vs ACLs,
- Keeping your static and media files organized,
- Anticipating AccessDenied and ACL errors,
- Automating your deployment process.
By learning through real mistakes and solving them, I made my project faster, scalable, and more secure.
If you're building a Django project and aiming for professional-grade deployment,
I hope this guide saves you hours (if not days) of frustration.
Good luck with your project 🚀👨💻 — and stay strong if AWS throws some surprises your way!