Managed authentication lets the Atatus Infrastructure Agent connect to your RDS or Aurora PostgreSQL database using a short-lived IAM token instead of a password. Nothing long-lived is written to the agent host, and you revoke access by editing an IAM policy rather than by rotating a password that every host holds a copy of. This page covers PostgreSQL on Amazon RDS and Amazon Aurora. If you are using a password today, you can move to managed authentication without changing anything else about your setup.

Before you begin

Check these requirements first, because a mismatch here fails later in a way that is hard to read:
Managed authentication requires agent 4.3.0 or higher. An older agent does not report an error when it sees the aws: block. It ignores the block, falls back to password authentication, finds no password, and then fails to authenticate on every attempt. Check your version with atatus-infra-agent version before you start.

How it works

The agent signs a connection token locally using the IAM credentials it already holds, then presents that token to PostgreSQL as the password. The token is valid for 15 minutes and the agent mints a new one for each connection. Two consequences are worth knowing up front:
  • Token minting is not logged in CloudTrail. The token is signed on the client side, so no AWS API call is made and no event is recorded. You can audit the connection landing in your PostgreSQL logs instead.
  • The IAM policy controls who, not where. IAM database authentication ignores aws:SourceIp, aws:SourceVpc and aws:SourceVpce in a policy condition. Use security groups to restrict the network path.

Step 1: Turn on IAM database authentication

Your database has to accept IAM tokens before any of the following matters. Without this, the token is signed correctly and then rejected at login. In the AWS console, go to Modify, then Database authentication, and choose Password and IAM database authentication. For a DB instance, you can also use the CLI:
For an Aurora cluster, the setting lives on the cluster:
--apply-immediately applies every pending modification on that database, not only this one. Anything queued for the next maintenance window is applied now too, and some of those changes reboot the instance. Check what is pending before you run it:
Turning on IAM database authentication is itself a dynamic change and needs no reboot. Leave --apply-immediately off to have it applied in the next maintenance window instead.

Step 2: Create the database role

Connect as your master user and create the atatus role. Run this once per cluster:
GRANT rds_iam is a one-way switch for the role it is applied to. Once a role holds it, that role can never authenticate with a password again, and the only way back is to drop and recreate the role. Never grant it to your master user: every application and operator that connects with the master password is locked out immediately, and you cannot use the master user to undo it. Recovery from that means restoring a snapshot.
pg_monitor is the built-in read-only monitoring role. Without it the agent sees only its own session in pg_stat_activity, and the query text of every other session is redacted to <insufficient privilege>. The agent opens a connection per database it collects from. Roles inherit CONNECT from PUBLIC by default. If you have revoked that, grant it back for each database you want monitored:
The username in the agent config, the username in the IAM policy, and the PostgreSQL role name must match exactly, including their case. The username is signed into the token, so a case mismatch fails as an authentication error with no hint about the cause.

Step 3: Allow the agent to connect

The identity the agent runs as needs permission to open an IAM-authenticated connection as the atatus role. Attach this policy to the agent’s EC2 instance role, ECS task role, or EKS pod role:
<RESOURCE_ID> is the database resource id, not the database name. Find it under Configuration in the RDS console:
A restored or cross-region-copied database gets a new resource id. A policy that still names the old id keeps applying without error and silently stops authorizing anything, so the agent starts failing to connect after a restore. Check the resource id whenever you restore from a snapshot.
On ECS Fargate, attach this to the task role, not the execution role. The execution role is used by ECS before your container starts, to pull the image and read secrets. The process inside the container picks up the task role, so that is the identity that signs the token.

Step 4: Configure the agent

Edit /etc/atatus-infra-agent/conf.d/postgresql.d/postgresql.yml and add an aws: block:
These are the fields that matter:
Remove the password: line when you turn this on. An IAM-authenticated role has no password, and the agent rejects a config carrying both at startup rather than preferring one silently.
Set instance_endpoint explicitly whenever hosts: is not the real RDS endpoint, such as a CNAME, an IP address, or a connection pooler. AWS rejects a token presented to a host it was not signed for. Restart the agent to pick up the change:
This restarts the agent on the host, not your database. It is safe to run at any time: the only effect is a gap in collection of a few seconds, and your databases keep serving throughout. The commands that restart a database are called out separately, and none of them are required by the agent.

TLS

TLS is mandatory under managed authentication and defaults to verify-full. The token is a bearer credential, so a connection that can fall back to plaintext would hand it to anyone on the network path. The agent does not let you turn TLS off here. You do not need to install a certificate. The agent ships the Amazon RDS certificate bundle and trusts it alongside the system store, which matters because the RDS authorities are a private PKI that is in no operating system trust store. verify-ca and require are also accepted. Point ssl_root_cert at a PEM file only if you terminate TLS with your own certificate authority:
verify-ca checks the certificate chain but not the server hostname, so it is only as strong as its trust store. Pinned to the Amazon RDS authorities, it still trusts any RDS instance in any AWS account. Prefer verify-full, and use verify-ca only with your own ssl_root_cert.

Monitor several databases with one agent

One agent can collect from any number of PostgreSQL databases. You do not need an agent per database, and the databases do not have to share a region, an account, or a set of credentials. There are three ways to arrange it. They can be mixed in the same setup.

One entry per database

metrics is a list, so add an entry for each database. This is the clearest option and the one to reach for by default, because every setting is scoped to its own database:
Because each entry carries its own aws block, the databases can sit in different regions, and with role_arn they can sit in different AWS accounts. Each entry can also use a different authentication method, so one database can use a password while another uses IAM tokens.
Set reported_hostname and labels on every entry. Without them, several databases collected by one agent are harder to tell apart in the UI, since they all arrive from the same host.

Several hosts in one entry

When databases share the same credentials and region, list them under a single hosts entry:
Everything outside hosts is shared, so this works only when the username, the region and the authentication method are the same for all of them.
Leave instance_endpoint out when you list several hosts under managed authentication. It is a single value that applies to the whole entry, so setting it makes the agent sign every token for that one endpoint. AWS then rejects the token at every other host, and TLS hostname verification fails as well. With instance_endpoint unset, the agent signs each token for the host it is connecting to, which is what you want here.
Each host still needs its own rds-db:connect permission, because the resource id differs per database. Grant them in one policy:

One file per database

The agent reads every .yml file under conf.d/postgresql.d/, not only postgresql.yml. Give each database its own file when you want them managed separately, for example by different teams or by a configuration management tool:
Each file holds its own metrics list and is loaded independently, so a mistake in one file is easier to find than a mistake in a long combined file.
Only files ending in .yml are read. postgresql.yml.template is ignored because of its suffix, which is why the shipped template does not start collecting on its own. Renaming a file to orders.yml.disabled is a quick way to turn one database off without deleting its config.

Things to watch

Keep these in mind as the number of databases grows:
  • Every database role has to be set up separately. Steps 2 and 3 above are per database, so create the atatus role and grant rds_iam on each one.
  • The agent opens a connection per database it collects from. Check max_connections on your smaller instances before you add many databases to one agent.
  • Security groups are per database. The agent host needs a path to each one on port 5432.
  • Restart once, after all the edits. The agent reads the whole conf.d tree at startup.

Monitoring a database in another AWS account

RDS has no resource-based policy, so the only workable shape is a role in the database’s account that the agent assumes. The agent’s own credentials are used only to call sts:AssumeRole. The token is then signed with the temporary credentials that role returns. Add role_arn to the managed_authentication block:
Set external_id only if the trust policy requires one. In the database owner’s CloudTrail, the agent appears with the role session name atatus-infra-agent. Confirm the assume works from the agent host before you restart the agent:

Running the agent outside AWS

With no credential fields set, the agent uses the standard AWS credential chain: environment variables, then the shared config file, then the EC2, ECS or EKS role. That is the best setup inside AWS, because no key is written to disk. On a host outside AWS, supply credentials explicitly. They are used only to sign the token, or to assume the role above:

Set it up with Terraform

Everything on this page can be provisioned instead of configured by hand: an EC2 host running the agent, the rds-db:connect policy with the resource id resolved at apply time, the security group rule, and optionally the database role itself created at first boot. That has its own page, including the DocumentDB half and how to run both engines from one host: The working Terraform is at atatus-database-monitoring-sample.

Troubleshooting

Work through these in order. Each step rules out one layer, so you do not have to guess which one is at fault.

Check the agent version

Managed authentication needs 4.3.0 or higher. An older agent ignores the aws: block without complaining:

Check the agent is running and loaded your config

If more than one agent process is running, the second one exits with a lock error and the first keeps running with whatever config it started with. Stop everything and start once:

Turn up the log level

The default log level is warning, which hides the messages that show the integration starting up. Set log_level: info in /etc/atatus-infra-agent/atatus.yml, restart, and read the log:
To filter for the PostgreSQL integration:

Prove the token works on its own

This is the single most useful check, because it splits the problem cleanly in half. Run it on the agent host, so it uses the same identity the agent uses:
Read the result like this:

Query metrics are empty

If the agent connects but you see no queries, pg_stat_statements is most likely not loaded. It is a shared library that has to be loaded when the server starts, so CREATE EXTENSION on its own is not enough:
The output has to list pg_stat_statements. If it does not, set shared_preload_libraries = pg_stat_statements in your parameter group and reboot the instance. This is a static parameter, so applying the parameter group without a reboot does nothing, CREATE EXTENSION still succeeds, and query metrics stay empty with no error.
That reboot restarts your database, not the agent. Every open connection is dropped and the database is unavailable while it comes back, usually for a minute or two. Do it in a maintenance window.On a Multi AZ instance the reboot fails over to the standby unless you pass --no-force-failover, which changes which availability zone your writer is in. On Aurora, reboot the writer instance, and be aware that rebooting the writer triggers a failover.
Nothing about the Atatus agent requires this reboot. It is PostgreSQL’s own rule for a static parameter, so if you cannot take the downtime now, everything except query metrics still works.
On Aurora, shared_preload_libraries is set in the DB cluster parameter group, not the DB parameter group. Recent Aurora PostgreSQL versions already include pg_stat_statements in their default cluster parameter group, so check the current value before you create a custom group.

Common error messages