How to Securely Connect Django to AWS S3 — Two Recommended Approaches — Secure Setup Guide

When deploying Django apps on AWS, securely managing your access to S3 is critical. Hardcoding credentials is a major security risk. Fortunately, AWS provides two safe and effective ways to authenticate your backend when interacting with S3:


🔐 Two Secure Options to Access S3 from Django

OptionDescriptionBest ForSecurity
1. Environment Variables (env vars)You define your AWS keys directly in Elastic Beanstalk as environment variablesQuick setup, staging/dev environmentsSecure (if access to EB is restricted)
2. IAM Role Attached to EC2 (Instance Profile)EC2 instances automatically receive short-lived credentials using IAM RolesProduction environments, high complianceBest Practice

In this guide, we’ll walk you through both.


Option 1: Using Environment Variables

This method is ideal for local development and quick production deployments.

1. The Problem: Hardcoded Credentials

Avoid this in your settings.py:

AWS_ACCESS_KEY_ID = "AKIA123..."
AWS_SECRET_ACCESS_KEY = "abcd123..."

If this file is ever committed to version control, your AWS account could be compromised.


2. Load AWS Credentials from Environment Variables

Instead, use:

AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY")
AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_STORAGE_BUCKET_NAME")

This keeps sensitive values outside your codebase.


3. Create a Loader Script: set_env.sh

At the root of your Django project:

#!/bin/bash
export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="abcd..."
export AWS_STORAGE_BUCKET_NAME="your-bucket-name"

Then:

chmod +x set_env.sh

4. Add It to .gitignore

# Environment secrets
set_env.sh

Never commit this script to Git.


5. Automate Loading with Virtual Environment

Edit your virtual environment’s activate script:

nano envs/bin/activate

Add at the bottom:

# Load AWS credentials automatically
source /absolute/path/to/your/project/set_env.sh

Example:

source /Users/yourname/Projects/su-backend/set_env.sh

6. Final Test

After activating your virtualenv:

source envs/bin/activate
echo $AWS_ACCESS_KEY_ID

You should see your access key printed.


💡 Why This Matters

Environment variables keep your keys out of code and Git — reducing the risk of accidental exposure. This is a great solution for development or non-critical deployments.


Option 2: Use IAM Roles (Recommended for Production)

IAM Roles allow AWS to handle authentication automatically using temporary credentials, without you storing anything.


Step 1: Create an IAM Role

  1. Go to AWS Console > IAM > Roles
  2. Click Create role
  3. Select:
    • AWS Service
    • Use case: EC2
  4. Attach the policy: AmazonS3FullAccess (or a custom policy for tighter security)
  5. Name it: DjangoStarsUp-InstanceRole-S3Only
  6. Click Create role

Step 2: Attach the Role to Elastic Beanstalk

  1. Go to Elastic Beanstalk > su-env > Configuration
  2. Under Service access, click Edit
  3. In EC2 instance profile, select: CopyEditDjangoStarsUp-InstanceRole-S3Only
  4. Click Apply

Elastic Beanstalk will restart the instance(s) with this new role.


Common Issue: Health Check Failure

When EB launches new instances, it checks the health by hitting /. If your Django app returns 404 or requires auth, EB will mark the instance as “unhealthy”.


Step 3: Add a Public Health Check Endpoint in Django

In urls.py, add:

from django.http import JsonResponse
from django.urls import path

urlpatterns += [
path("health/", lambda request: JsonResponse({"status": "ok"})),
]

Deploy your app again.


Step 4: Update Health Check Path in Elastic Beanstalk

  1. Go to Configuration > Instance traffic and scaling
  2. Edit the default process
  3. Change the health check path from / to /health/
  4. Click Save

Step 5: Update Your Django Settings for IAM Role

In settings.py:

import os

AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_STORAGE_BUCKET_NAME")

AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID", None)
AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY", None)

DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_S3_REGION_NAME = 'us-east-1'
AWS_QUERYSTRING_AUTH = False

if not AWS_ACCESS_KEY_ID or not AWS_SECRET_ACCESS_KEY:
print("Using IAM Role from EC2 instance profile")

If no keys are provided, boto3 will automatically use the IAM Role assigned to the instance.


Final Result

Your Django backend now securely connects to S3:

  • No credentials in code or on disk
  • Automatically rotated credentials
  • Scalable and AWS-compliant
  • Working health check so EB can manage instances properly

Bonus: Custom Policy Example

If you want to limit access to a specific bucket only:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"],
      "Resource": [
        "arn:aws:s3:::your-bucket-name",
        "arn:aws:s3:::your-bucket-name/*"
      ]
    }
  ]
}

What’s Next?

I will also put a tutorial on how to use AWS Secrets Manager to securely store database credentials and other app secrets in production.