AWS RDS
Provision Amazon RDS for PostgreSQL, grant Hub a database role that authenticates with AWS IAM, then record the resulting endpoint and region for the install.
IAM authentication is the recommended path for self-hosted Hub on AWS. Rather
than storing a static database password in your cluster, the hub-core Pods
exchange a short-lived RDS auth token per database connection, using credentials
from IAM Roles for Service Accounts (IRSA) or EKS Pod Identity.
If you can't use IAM in your environment, review the password-auth fallback instructions.
Prerequisites
Before you begin, have the following ready:
- An AWS account with permissions to create RDS instances, IAM roles, IAM policies, and (if you don't already have one) an OIDC provider for your EKS cluster.
- A Kubernetes cluster running on AWS with a working AWS workload-identity mechanism: either IAM Roles for Service Accounts on EKS or EKS Pod Identity. Self-managed clusters can use IRSA as long as the API server's service-account-issuer is registered as an IAM OIDC provider.
psql(or another PostgreSQL client) installed locally, reachable to the RDS instance for the one-time bootstrap. A bastion or VPN is fine.
This page assumes you have already read the self-hosted overview and the database overview. It does not repeat generic Postgres requirements covered there.
Provision RDS
Provision the database with whichever IaC tool you already use. This section lists only the settings that matter for Hub.
Read these AWS guides end-to-end before provisioning:
- Creating a PostgreSQL DB instance
- IAM database authentication for MariaDB, MySQL, and PostgreSQL
- VPC security groups for RDS
When you create the instance, set the following:
- Engine: PostgreSQL 18 or newer.
- IAM database authentication: enabled. This setting works as a per-instance flag. You must enable it at the instance level before you can use it for any single database or role.
- Network: place the instance in private subnets
and attach a security group that allows inbound TCP 5432 from the security group attached to the worker
nodes that run
hub-core. - TLS: RDS terminates TLS by default. Note the CA bundle you need to trust, since RDS rotates these on a published schedule.
Record three values once the instance is available:
- the RDS endpoint hostname (for example,
hub.cluster-xxxx.us-east-1.rds.amazonaws.com). - the instance's DbiResourceId (a string starting with
db-…). You can find it in the RDS console under the instance's Configuration tab, or viaaws rds describe-db-instances. An IAM policy you create later targets this ID, not the instance name. - the AWS region (for example,
us-east-1).
Enabling IAM database authentication on an existing instance triggers a reboot, so schedule it during a maintenance window.
Configure IAM authentication
IAM auth for RDS has three sides that must agree:
- An IAM role the
hub-corePod can assume. - A policy on that role granting
rds-db:connectfor the database user Hub logs in as. - A PostgreSQL role with the same name as the IAM user, granted the
rds_iamrole inside the database.
Create the database role
Connect to the instance as the master user and create the role Hub uses. This
role needs the rds_iam grant so RDS accepts IAM-issued tokens for it, as
described in Creating a database account using IAM
authentication. It also needs ownership of the Hub database so
migrations can create and alter objects.
CREATE DATABASE hub;
CREATE USER hub;
GRANT rds_iam TO hub;
GRANT ALL PRIVILEGES ON DATABASE hub TO hub;
\c hub
GRANT ALL ON SCHEMA public TO hub;
Then, as the master user, create the hstore extension in a dedicated
extensions schema, let hub use the schema, and put the schema on the
database's search path. The migrations need all three, and hub can't move
hstore into extensions itself because it isn't a superuser:
\c hub
CREATE SCHEMA IF NOT EXISTS extensions;
GRANT USAGE ON SCHEMA extensions TO hub;
CREATE EXTENSION IF NOT EXISTS hstore WITH SCHEMA extensions;
ALTER DATABASE hub SET search_path TO "$user", public, extensions;
rds_iam and password authentication are mutually exclusive on RDS. A role
granted rds_iam can't also log in with a password. Don't set a password on
hub.
Create the IAM policy
Follow Creating and using an IAM policy for IAM database
access. Hub needs one statement, allowing
rds-db:connect on the hub database role:
| Field | Value for Hub |
|---|---|
| Action | rds-db:connect |
| Resource | arn:aws:rds-db:<region>:<account-id>:dbuser:<dbi-resource-id>/hub |
The final segment of the resource ARN is the database role name, hub, and
<dbi-resource-id> is the db-… string you recorded above, not the instance
name.
Record the resulting policy ARN. You attach it to the role in the next section.
Bind the policy to the hub-core ServiceAccounts
Attach the policy to an IAM role the hub-core Pods can assume. AWS offers two
mechanisms. IRSA works on any EKS cluster with an IAM OIDC provider. Pod Identity
is simpler to manage where it's available.
Whichever you pick, the role identifies Pods by their Kubernetes
ServiceAccount. Two Hub workloads connect to the database with IAM: the
hub-core Deployment and the migration Job that applies the schema. The chart
creates a ServiceAccount for each, so these values are what AWS's procedure
needs:
| What the AWS procedure asks for | Value for Hub |
|---|---|
| Namespace | hub, or the namespace you install the release into |
| ServiceAccount names | hub-core and hub-core-migrate |
IRSA trust policy sub condition | system:serviceaccount:hub:hub-core and system:serviceaccount:hub:hub-core-migrate |
IRSA trust policy aud condition | sts.amazonaws.com |
Option A: IAM roles for service accounts (IRSA)
Follow Assign IAM roles to Kubernetes service accounts, using the subject and audience conditions from the table above. Allow both subjects in the trust policy. If your cluster's OIDC issuer isn't registered with IAM yet, do Create an IAM OIDC provider first.
You then annotate both ServiceAccounts with the role ARN through Helm values, shown in the next section.
Option B: EKS pod identity
Follow Assign an IAM role to a Kubernetes service
account, creating one association against namespace hub
and ServiceAccount hub-core, and a second against ServiceAccount
hub-core-migrate. Create both associations before you install Hub. The
migration Job starts during the install and fails if it can't get credentials.
With Pod Identity the ServiceAccounts need no annotations. EKS keys the
association by namespace and ServiceAccount name, so leave the
eks.amazonaws.com/role-arn annotations out of your Helm values.
Values to record
The IAM-auth values omit any password Secret. hub-core reads the auth mode
and cloud from environment variables emitted by the chart. It then builds an RDS
auth token from the Pod's IAM credentials. It uses that token as the PostgreSQL
password on every new pool connection. The migration Job receives the same
settings and authenticates the same way. TLS is required: in IAM mode the chart
upgrades an unset or disable postgresql.sslmode to require.
Record these values and enter them in the AWS IAM authentication tab in step 4 of the install guide:
| Value | Where it came from |
|---|---|
| RDS endpoint hostname | The provisioned instance |
| AWS region | The provisioned instance |
| Database name and user | hub and hub, from the SQL above |
| IAM role ARN | The role you attached the policy to. IRSA only |
The completed block should look like this:
hub-core:
api:
serviceAccount:
create: true
# Only needed for IRSA. Pod Identity does not use ServiceAccount annotations.
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account-id>:role/hub-core
migration:
serviceAccount:
# Only needed for IRSA. The migration Job runs as hub-core-migrate.
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account-id>:role/hub-core
postgresql:
host: <rds-endpoint>
port: 5432
database: hub
user: hub
sslmode: require
auth:
mode: iam
cloud: aws
aws:
region: <region>
The chart ignores its postgresql.connectionString value when auth.mode=iam.
Use the discrete host, port, database, and user fields.
Troubleshoot IAM auth
Once you've installed Hub, watch the hub-core Pods roll out:
kubectl -n hub rollout status deployment/hub-core
A migration Job (hub-core-migrate-<hash>, running as ServiceAccount
hub-core-migrate) applies the schema. Each hub-core Pod's init-migrate
init container waits until the schema is current before the API server starts.
All of them use the same IAM auth path, so a successful rollout means both the
migration Job and the server authenticated against RDS.
If the rollout stalls, check the migration Job first:
kubectl -n hub get jobs
kubectl -n hub logs job/<hub-core-migrate-job-name>
Look in the Job or Pod logs for one of these:
-
database auth aws region is required.postgresql.auth.aws.regionis empty and the Pod has noAWS_REGIONenvironment variable. Set the value invalues.yaml. -
PAM authentication failed(in the RDS log). The IAM role is missingrds-db:connectfor the resource, the DbiResourceId in the policy is wrong, the role doesn't trust the ServiceAccount that's connecting (hub-coreorhub-core-migrate), or the database role wasn't grantedrds_iam. -
hstore must live in the extensions schema. The extension was created outside theextensionsschema, andhubcan't move it. Have the master user move it, lethubuse the schema, and add the schema to the database's search path:\c hubCREATE SCHEMA IF NOT EXISTS extensions;GRANT USAGE ON SCHEMA extensions TO hub;ALTER EXTENSION hstore SET SCHEMA extensions;ALTER DATABASE hub SET search_path TO "$user", public, extensions;Then run the migration Job again.
Log in to Hub and confirm at least one ControlPlane has been registered or can be created. Schema is in place when the UI lists resources without errors.
Password authentication (fallback)
Use this section only if you can't use IAM authentication, such as when running outside AWS or on a Kubernetes cluster without workload identity. Password mode stores a long-lived credential in a Secret. Rotate it through whatever secret-management tool your organization already uses.
Create the database role with a password rather than the rds_iam
grant:
CREATE USER hub WITH PASSWORD '<your-password>';
GRANT ALL PRIVILEGES ON DATABASE hub TO hub;
Then use the Password authentication tab in step 4 of the install guide instead of the IAM tab, with the RDS endpoint as the host. Step 2 of that guide creates the Secret holding the password you set above. Skip the IAM role, policy, and ServiceAccount setup on this page entirely.
Everything else (TLS, security groups, schema bootstrap) works the same as the IAM path.
Next step
Return to the self-hosted install guide to finish wiring Hub against the database you just provisioned.