Skip to content

Managed Postgres Providers

A managed Postgres service withholds the superuser, reserves some roles and schemas for itself, installs extensions of its own, and lets a customer create only the extensions on its list. sql.provider tells the dialect which service a project runs on.

export default {
lexicons: ["sql"],
sql: {
dialect: "postgres",
provider: "rds",
profiles: {
preview: { url: "postgres://ep-example.neon.tech/app", provider: "neon" },
},
},
} satisfies ChantConfig;

The values are rds, aurora, cloud-sql, azure, neon and supabase; anything else fails the config check. sql.profiles.<env>.provider overrides sql.provider for one environment. Leave both out for a self-hosted server.

WhereWhat it does
SQLPG004 (lint)reports an extension template that creates an extension the provider does not allow. Lint has no environment, so it reads sql.provider, else the one provider every profile that names one agrees on, and stays silent when there is none or they disagree
chant import --from <env>leaves out the provider’s reserved schemas and everything in them, and the extensions it installs itself
chant lifecycle diff --live, describeResourcesreports those objects foreign
chant sql plan, postgresApplynever proposes or makes a drop of them; the applier reports a declared one filtered

The provider’s data is also exported from @intentius/chant-lexicon-sql/postgres for a tool of your own: providerData(provider) returns it, providerAllowsExtension(provider, name) answers true, false, or undefined when the provider’s list is partial and does not name the extension, isProviderOwned(provider, { kind, name }) says whether a role, schema or extension is the provider’s, and refusedStatement(provider, sql) matches a statement against the provider’s refused statements. Nothing in the build, plan or apply checks statements against that last list today, and the dialect declares no roles, so the reserved roles are read only by isProviderOwned.

Each provider’s data is one file under lexicons/sql/src/postgres/providers/, read from its documentation on 2026-10-03 and citing each page in providerData(provider).sources. A provider changes its lists without announcing it, so a list is a snapshot. An extension the provider added since is reported by SQLPG004 until the file is refreshed; disable the rule on that line meanwhile.

ProviderAllowed extensionsInstalls itselfReserved schemasRefuses beyond the common three
rds (Amazon RDS for PostgreSQL)83, the major 18 tableaws_commons, aws_lambda, aws_s3, rds_tools, log_fdw, pg_transportnoneCREATE TABLESPACE ... LOCATION
aurora (Aurora PostgreSQL)94, the Aurora 18 tableapg_plan_mgmt, aurora_stat_utils, aws_commons, aws_lambda, aws_ml, aws_s3, log_fdw, pg_ad_mapping, pg_columnmask, pgdam, rds_activity_stream, rds_toolsnoneCREATE TABLESPACE ... LOCATION
cloud-sql (Cloud SQL for PostgreSQL)79google_ml_integration, google_read_only_sessionnone
azure (Azure Database for PostgreSQL flexible server)80, the entries with a major 18 versionazure_ai, azure_storage, pg_diskannnonealtering azure_pg_admin, granting to it
neon76, the PG18 columnneon, neon_utils, pg_session_jwt, lakebase_text, lakebase_tokenizer, lakebase_vectornoneCREATE TABLESPACE, altering neon_superuser
supabase23, partialpg_graphql, pg_net, pgmq, index_advisorauth, storage, etlnone beyond the two it states

The common three, which need the superuser every provider withholds, are ALTER SYSTEM, COPY ... PROGRAM, and a role created or altered SUPERUSER. Supabase’s page states only the last two.

Supabase’s extension list is rendered by a script and could not be read whole, so its list is marked partial (extensionListComplete: false) and SQLPG004 does not report an extension outside it.

A profile’s password can name a token source in place of an environment variable. The token is minted when a connection needs it, from the identity the job already has (an OIDC-federated role in CI, a workload identity, a developer’s login), so CI holds no long-lived database password:

profiles: {
prod: {
url: "postgres://shop.abc123.eu-west-1.rds.amazonaws.com:5432/shop",
user: { env: "PG_USER" },
password: { token: "rds-iam" },
},
},
SourceWhat it runsLifetime
{ token: "rds-iam", region? }aws rds generate-db-auth-token for the URL’s host and port and the profile’s user, in region, else AWS_REGION, else the region in the RDS host name. RDS and Aurora.900 s
{ token: "cloud-sql-iam" }gcloud sql generate-login-token3600 s
{ token: "entra" }az account get-access-token --resource-type oss-rdbms (Azure Database for PostgreSQL)3600 s
{ token: "command", command: ["./mint.sh"] }the program, without a shell; its standard output, trimmed, is the password300 s

Each takes ttlSeconds to override its lifetime. The cloud sources run that cloud’s own command line, which reads the job’s credentials the way it always does, so the line must be installed where chant runs. A token is reused until 80% of its lifetime has passed and minted again after, so a long apply never sends an expired one: each Postgres connection mints, if needed, when it opens, and each ClickHouse request does the same. A token the server refuses is forgotten, and the next connection mints a new one. A source that cannot mint (the program is missing, exits non-zero or prints nothing) reports the environment no-credentials, naming the program; no token is ever written to a message or an outcome.

The cloud sources are for Postgres. A ClickHouse profile takes the command source.

Every file lists in toCheck what its pages did not confirm, each with the page to check. As read on 2026-10-03:

ProviderNot verified
RDSthe reserved roles beyond rds_superuser were not read from a page; the refused statements beyond the common three come from the documented master-user model, not a statement list; per-major differences in the extension list.
Auroraper-release differences inside major 18 and the other majors’ tables; pgdam and pg_columnmask were listed with no description read; the same role and statement gaps as RDS.
Cloud SQLanon (the page lists postgresql_anonymizer, and the CREATE EXTENSION name was not read); the list is the newest major’s, and older majors drop some entries; no page read names a schema Cloud SQL owns.
Azurethe azure.extensions allowlist parameter and the extensions that need shared_preload_libraries (the page rendered no body); no reserved schema read from a page; wal2json and pg_partman_bgw may not take CREATE EXTENSION; older majors’ lists.
Neonno reserved schema read from a page; pg_repack needs a paid plan and Neon Support to enable, and pg_ivm is not available for new installs; older majors’ columns.
Supabasethe full extension list; reserved schemas beyond auth, storage and etl (extensions, realtime, graphql, graphql_public, vault, net, supabase_functions and pgbouncer were not read from a page, so they import and plan as the project’s); refused statements beyond the two the page lists; the installed extensions are inferred from the extension pages’ names, not from a statement of ownership.

Every list is for the newest major. A project on an older major can be told an extension is allowed that the provider does not offer there.