Appearance
Database credentials
Where the password lives
Keel provisions RDS with manage_master_user_password = true. That means AWS — not Keel — generates the master password, stores it in Secrets Manager, and rotates it on a schedule.
The consequence worth internalising: Keel never holds the password. It is not in keel.yml, not in OpenTofu state, not in ~/.keel, and not in any log Keel writes. Nothing you can grep will produce it, because nothing on your machine has ever had it.
The running tasks do not hold it either. Each service's task definition names the secret by ARN and ECS injects the values at container start:
| Variable | Source |
|---|---|
DATABASE_USER | the secret's username key |
DATABASE_PASSWORD | the secret's password key |
DATABASE_CREDENTIALS | the whole JSON payload, for a client that wants it |
DATABASE_HOST | the endpoint, from the RDS resource |
DATABASE_PORT | the port, from the RDS resource |
DATABASE_NAME | the database name |
DATABASE_SCHEME | postgres or mysql2, per the engine |
So an application never needs the admin credentials, and should not use them: it already has what it needs, and it keeps working across a rotation.
Note there is no DATABASE_URL: the parts are injected separately and an app assembles its own. (The cache does get CACHE_URL and REDIS_URL. The asymmetry is finding F5 in docs/findings/rails-app-deployment.md, half-implemented.)
Seeing that it exists
The config views list it alongside the variables someone set, because a service's environment carries both:
$ keel config list
NAME TYPE VERSION
SECRET_KEY_BASE SecureString 3
Managed by AWS — injected into every service, not editable here:
@database credentials (active)
DATABASE_USER ← username the master username
DATABASE_PASSWORD ← password generated by AWS, rotated on a schedule
DATABASE_CREDENTIALS the whole JSON secret, for a client that wants it
AWS Secrets Manager, generated and rotated by RDS
rotated by AWS every 7 days — do not copy the value
secret arn:aws:secretsmanager:us-west-2:123456789012:secret:rds!db-1a2b3c-AbCdEf
values keel db credentialsThe dashboard's Config tab shows the same thing, in a block below the variables table and outside the cursor's range — the selection there drives unset, and none of this can be unset.
No value is read to produce that listing: it comes from DescribeSecret, which returns metadata only. So a viewer or a developer sees the secret exists, what it injects and how often it rotates, without holding any permission that could read it.
keel config set and keel config unset refuse these names rather than reporting a missing SSM parameter:
$ keel config unset DATABASE_PASSWORD
Error: DATABASE_PASSWORD is not a configuration variable and cannot be unset: it comes
from @database credentials, which AWS generates and rotates. Read the values with
'keel db credentials'.Reading them as a human
For the cases an application does not cover — connecting with psql, running a one-off query, creating a second database user — there are two commands. Both need admin access, because both call secretsmanager:GetSecretValue.
$ keel db credentials
acme (production) — postgres
host acme-production-db.abc123.us-west-2.rds.amazonaws.com
port 5432
database acme
username keeladmin
password •••••••••••••••• (--show to reveal)
secret arn:aws:secretsmanager:us-west-2:123456789012:secret:rds!db-1a2b3c-AbCdEfThe password is masked by default: checking an endpoint or the admin username is the common case, and it should not put a credential into a shared screen or a scrollback buffer. --show prints it.
For a connection string:
$ psql "$(keel db url)"keel db url is the only Keel command that emits a password, and it emits it only when asked. Do not store what it prints — the password rotates, and a saved URL stops working.
Reaching the database at all
The database sits in private subnets, and its security group only admits the service security groups on the engine's port. It is not reachable from a laptop, whatever credentials you hold.
The route in is a task inside the VPC:
$ keel exec web -- sh -lc \
'psql "postgres://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOST:$DATABASE_PORT/$DATABASE_NAME"'That uses the service's own injected values, needs only developer access, and does not involve the admin password at all. It is the right answer for almost every "I need to look at the database" moment. keel db credentials is for the cases where you genuinely need the master user — creating roles, granting to a new user, an out-of-band migration.
Rotation
AWS rotates the master password on the schedule the secret carries. Two things follow:
- Copies expire. Anything you paste into a password manager, a
.env, or a CI variable will stop working, silently, at a time nobody chose. Read the secret each time instead. - Nothing needs redeploying when it rotates. The task definitions reference the secret by ARN, so ECS picks up the new value the next time a task starts.
If keel db credentials says the output is missing
A stack applied before Keel emitted the database_secret_arn output has no record of where the secret is. Re-applying adds the output:
$ keel up --env productionNothing is recreated — an output is not a resource.