Skip to main content

External Database

By default the on-premises control plane bundles its own PostgreSQL. To use a database your own DBA team manages instead, you disable the bundled instance (postgresql.builtin=false) and point the control plane at an external database through the externalDatabase.* values — see On-Premises Configuration.

This page covers what that external database must provide: the role and privileges the control plane needs, and a setup that does not require a PostgreSQL superuser.

The control plane has two services that share one database:

  • cp-api — the control-plane API server. It runs the schema migration on startup.
  • dp-manager — the data-plane manager. It connects to the same database and runs no migration.

Requirements at a Glance

  • PostgreSQL 14 or later. 16 is the tested and bundled version.
  • One empty database, owned by the role the control plane connects as.
  • One login role with CREATEROLE and BYPASSRLS — but not SUPERUSER.
  • No PostgreSQL extensions. UUIDs use the built-in gen_random_uuid().

Why the Control Plane Needs These Privileges

On its first start, cp-api builds its own schema and a dedicated low-privilege role. That bootstrap is why the connection role needs a specific, bounded set of privileges — and why it does not need a superuser:

  • Ownership of the database lets cp-api create the public and auth schemas and every table, index, function, and Row-Level Security (RLS) policy. A database owner implicitly has CREATE on the public schema under default privileges, so no separate schema grant is required on a standard cluster.
  • CREATEROLE lets cp-api create and configure its internal application role, cp_api_app, on first boot. Every per-request tenant query runs as this role, and RLS policies keyed on it isolate each organization's rows. You do not create this role yourself.
  • BYPASSRLS is needed because a few control-plane operations are intentionally cross-tenant — caller-token authentication, billing webhooks, the background budget aggregator, and everything dp-manager does — and run on a shared connection that must see every organization's rows. Per-request tenant traffic still drops to cp_api_app, which has neither SUPERUSER nor BYPASSRLS, so tenant isolation is preserved where it matters.

None of these is a superuser privilege.

Create the Database and Role

Run this once, as a database administrator. On a self-managed server this is the postgres superuser. On a managed service (Amazon RDS, Google Cloud SQL, Alibaba Cloud RDS), it is the instance's master/admin user — which can grant CREATEROLE and BYPASSRLS without being a true superuser.

-- 1) A dedicated login role for the control plane. Not a superuser.
CREATE ROLE aisix LOGIN PASSWORD 'change-me-to-a-strong-password'
NOSUPERUSER CREATEROLE BYPASSRLS;

-- 2) A dedicated database owned by that role.
CREATE DATABASE aisix_cloud OWNER aisix;

Choose your own role name, password, and database name. Ownership is what lets aisix create objects in public and add the auth schema on first boot.

Use a URL-safe password

The password is embedded in a postgres:// connection URL, so characters such as +, /, and = (common in openssl rand -base64 output) can break the DSN unless they are percent-encoded. Generate a URL-safe value instead, for example with openssl rand -hex 24. See On-Premises Deployment.

Hardened public schema

The setup above assumes the database's public schema keeps its default privileges, where the owner can create objects. If your DBA has hardened public (for example revoked the default CREATE), also grant it explicitly to the role, connected to the new database:

\c aisix_cloud
GRANT USAGE, CREATE ON SCHEMA public TO aisix;
Do not pre-create the cp_api_app role

The control plane creates and configures cp_api_app during migration and asserts its attributes. Pre-creating it — especially with different attributes — can make the migration fail its safety checks.

Point the Control Plane at the Database

With Helm, set the externalDatabase.* values (and postgresql.builtin=false) as described in On-Premises Configuration. The chart builds the connection URLs for both services from those values.

Under the hood — and for a Docker Compose deployment — the two services read a standard PostgreSQL connection URL from the environment. Use the same role and database for both:

AISIX_CLOUD_DATABASE_URL="postgres://aisix:<url-encoded-password>@db.internal.example.com:5432/aisix_cloud?sslmode=require"
AISIX_DPMGR_DATABASE_URL="postgres://aisix:<url-encoded-password>@db.internal.example.com:5432/aisix_cloud?sslmode=require"
  • AISIX_CLOUD_DATABASE_URL — read by cp-api, which runs the migration on startup.
  • AISIX_DPMGR_DATABASE_URL — read by dp-manager. If the schema is not ready yet (cp-api is still migrating on a fresh cluster), dp-manager retries for up to ~2 minutes.

Set sslmode=require (or stricter, such as verify-full) whenever the database is reached over a network you do not fully control.

What the Control Plane Creates

On the first successful start, cp-api builds the full schema in the database you provisioned, and re-runs the same steps idempotently on every later start:

  • the public schema — all control-plane tables (organizations, environments, models, caller API keys, budgets, and so on), owned by your role;
  • the auth schema — authentication tables (users, sessions);
  • the cp_api_app role — NOSUPERUSER NOBYPASSRLS NOLOGIN, granted only CRUD on control-plane tables and read/write on a few non-secret identity columns; it is the role every tenant request drops to;
  • Row-Level Security policies that scope each tenant table to a single organization.

The connection role is automatically made a member of cp_api_app so it can switch to that role per request — you do not grant this yourself.

Privileges the Connection Role Holds

For review by your DBA, the full set the control plane relies on:

CapabilityWhy it is needed
LOGINthe services connect as this role
owns the databasecreate and alter the schemas, tables, indexes, functions, and RLS policies
CREATEROLEcreate and manage the internal cp_api_app role
BYPASSRLScross-tenant control-plane paths and dp-manager read every organization's rows on the shared connection
not SUPERUSERthe control plane never needs it

Verify the Setup

After the control plane starts, connect as an administrator and confirm the bootstrap looks right.

The internal role exists and is correctly de-privileged:

SELECT rolname, rolsuper, rolbypassrls, rolcanlogin
FROM pg_roles WHERE rolname = 'cp_api_app';
-- expect: cp_api_app | f | f | f

Both schemas were created:

SELECT nspname FROM pg_namespace WHERE nspname IN ('public', 'auth');
-- expect: two rows

Troubleshooting

permission denied to alter role ... Only roles with the SUPERUSER attribute may change the SUPERUSER attribute The control-plane image predates non-superuser support. Upgrade to a version that supports a non-superuser role, or, as a stop-gap, connect as a superuser.

cp-api database role must be SUPERUSER or have BYPASSRLS ... The connection role is missing BYPASSRLS. Grant it as a privileged admin:

ALTER ROLE aisix BYPASSRLS;

permission denied for schema public (on CREATE TABLE during migration) The role cannot create objects in public. Confirm it owns the database, and on a hardened cluster grant the schema privilege explicitly:

\c aisix_cloud
GRANT USAGE, CREATE ON SCHEMA public TO aisix;

permission denied to create role The connection role lacks CREATEROLE:

ALTER ROLE aisix CREATEROLE;

permission denied to set role "cp_api_app" The migration grants this membership automatically, so this points to a migration that did not finish — check the cp-api startup logs for an earlier error and resolve that first.

Next Steps

API7.ai Logo

The digital world is connected by APIs,
API7.ai exists to make APIs more efficient, reliable, and secure.

Sign up for API7 newsletter

Product

API7 Gateway

SOC2 Type IIISO 27001HIPAAGDPRRed Herring

Copyright © APISEVEN PTE. LTD 2019 – 2026. Apache, Apache APISIX, APISIX, and associated open source project names are trademarks of the Apache Software Foundation