Connect Django to Amazon RDS With IAM Authentication
Connect Django to Amazon RDS PostgreSQL or Aurora with IAM database authentication instead of a static password: grant rds_iam, create an rds-db:connect policy, generate auth tokens in a custom database backend, and require TLS.
Problem statement
A static database password in an environment variable or Secrets Manager works, but it is a long-lived credential that must be rotated, can leak in logs or dumps, and is shared by every process. RDS IAM authentication replaces it with short-lived tokens generated from the IAM role your EC2 instance, ECS task, or EKS pod already has. Django needs a small extension to request a fresh token when it opens a connection, plus the right IAM policy and database grants.
- Enable IAM database authentication on the RDS instance or Aurora cluster.
- In PostgreSQL:
CREATE USER app; GRANT rds_iam TO app;(no password). - Attach an IAM policy allowing
rds-db:connectonarn:aws:rds-db:<region>:<account>:dbuser:<DbiResourceId>/appto the app's role. - Subclass Django's PostgreSQL backend to generate a token with
boto3inget_connection_params. - Require TLS (
sslmode=verify-fullwith the RDS CA bundle). - Keep
CONN_MAX_AGEmoderate; tokens are only checked at connect time.
Step-by-step solution
1. Enable IAM authentication and create the user
Enable "IAM database authentication" on the instance (console, CLI --enable-iam-database-authentication, or IaC). Then, connected as the master user:
CREATE USER app;
GRANT rds_iam TO app;
GRANT CONNECT ON DATABASE appdb TO app;
\c appdb
GRANT USAGE, CREATE ON SCHEMA public TO app;
A user in rds_iam can only log in with IAM tokens. Keep a separate migration or admin user if your process needs one.
2. Allow the role to connect
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "rds-db:connect",
"Resource": "arn:aws:rds-db:eu-west-1:123456789012:dbuser:db-ABCDEFGHIJKL0123/app"
}]
}
The resource uses the instance's DbiResourceId (shown in the console), not its name. Attach the policy to the EC2 instance role, ECS task role, or the pod's IAM role.
3. Generate tokens in a custom backend
# myproject/db/rds_iam/base.py
import boto3
from django.db.backends.postgresql import base
class DatabaseWrapper(base.DatabaseWrapper):
def get_connection_params(self):
params = super().get_connection_params()
client = boto3.client("rds", region_name=self.settings_dict["OPTIONS"].get("region", "eu-west-1"))
params["password"] = client.generate_db_auth_token(
DBHostname=params["host"], Port=int(params.get("port") or 5432), DBUsername=params["user"],
)
params.pop("region", None)
return params
DATABASES = {
"default": {
"ENGINE": "myproject.db.rds_iam",
"HOST": env("DB_HOST"), "PORT": "5432",
"NAME": "appdb", "USER": "app",
"CONN_MAX_AGE": 300,
"CONN_HEALTH_CHECKS": True,
"OPTIONS": {"sslmode": "verify-full", "sslrootcert": "/etc/ssl/rds/global-bundle.pem", "region": "eu-west-1"},
}
}
generate_db_auth_token signs the request locally with the role's credentials; it does not call AWS, so it is fast. The token is valid for 15 minutes, but only for opening a connection; an open connection stays valid afterwards.
4. Require TLS
IAM authentication requires SSL. Download the RDS CA bundle into the image (global-bundle.pem from AWS) and use verify-full so the client also checks the server's identity.
5. Tune connection reuse
Generating a token is cheap, but creating connections has a cost and RDS limits the rate of new IAM-authenticated connections. Persistent connections (CONN_MAX_AGE of a few minutes) with health checks keep the connect rate low. RDS Proxy also supports IAM authentication from clients and pools connections, which helps with many workers or Lambda.
Verification check: from the app environment, python manage.py dbshell -c "select current_user, ssl_is_used();" returns app and t; removing the policy from the role makes new connections fail with PAM authentication failed, and restoring it fixes them.
Explanation
With IAM authentication, RDS delegates the password check for users in the rds_iam role to AWS IAM. The client builds a presigned token (a SigV4 signature over the hostname, port, and user) with its current AWS credentials and sends it as the password over TLS. RDS validates the signature and the caller's rds-db:connect permission. Because the signature derives from short-lived role credentials, no stored secret exists to leak, and access follows IAM policy changes.
Edge cases / notes
- Migrations can use the same IAM user if it owns the schema, or a separate admin user from Secrets Manager.
- Local development can generate tokens with your own AWS credentials, or use a normal password-based local database.
- Connection storms after deploys can hit IAM connection rate limits; stagger restarts or use RDS Proxy.
- MySQL on RDS supports IAM auth too, with the same approach and the MySQL backend.
Next steps
- Review general managed database settings in Configure Managed PostgreSQL for Django.
- If TLS fails, see Fix Postgres SSL Connection Errors in Django.
FAQ
Can Django use RDS IAM authentication?
Yes, with a small custom backend that sets a fresh token as the password when connecting.
How long is an RDS IAM token valid?
Fifteen minutes for opening connections. Existing connections are not affected when it expires.
Does IAM auth require TLS?
Yes. Use sslmode=verify-full with the RDS CA bundle.
Does generating a token call AWS?
No. It is signed locally with the role's credentials, so it is fast.