Skip to main content

External Database

By default, the on-premises control plane bundles its own PostgreSQL database. To use a database managed by your DBA team instead, disable the bundled instance and configure the control plane to connect to your database.

This guide covers the PostgreSQL-backed external database path for the on-premises control plane. The control-plane connection role does not need PostgreSQL superuser privileges. Other database engines are not supported for this deployment path.

The control-plane backend services share one database:

  • cp-api: Control-plane API server. It runs the schema migration on startup.
  • dp-manager: Data-plane manager. It connects to the same database and runs no migration.

PostgreSQL Requirements

  • PostgreSQL 14 or later.
  • 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 prepares the database schema and creates an internal low-privilege role. The connection role needs enough privilege for that bootstrap work and for shared control-plane operations.

Each privilege has a specific purpose:

  • Database ownership lets cp-api create the public and auth schemas, tables, indexes, functions, and Row-Level Security policies. Under default PostgreSQL privileges, the database owner can create objects in public, so a standard cluster needs no separate schema grant.
  • CREATEROLE lets cp-api create and configure cp_api_app, the internal role used for tenant-scoped queries with Row-Level Security. You do not create this role yourself.
  • BYPASSRLS lets shared control-plane operations read rows across organizations when required. These paths include caller-token authentication, billing webhooks, the background budget aggregator, and dp-manager operations.

Tenant-scoped requests still switch to cp_api_app, which has neither SUPERUSER nor BYPASSRLS. The control-plane connection role therefore needs specific PostgreSQL capabilities, but not superuser privileges.

Create the Database and Role

Run this once as a database administrator. Use an account that can create roles, create databases, and grant BYPASSRLS. The control-plane connection role itself does not need to be a 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 because the password is embedded in a PostgreSQL connection URL. Characters such as +, /, and = 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.

If your DBA has hardened the public schema, for example by revoking the default CREATE privilege, also grant schema access explicitly while 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 it during migration and asserts its attributes. Pre-creating it, especially with different attributes, can make the migration fail its safety checks.

Connect the Control Plane to 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 the control-plane services from those values.

For Docker Compose deployments, the services read standard PostgreSQL connection URLs from the environment. Use the same role and database for each service:

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, and by the dashboard.
  • AISIX_DPMGR_DATABASE_URL: Read by dp-manager. If the schema is not ready yet because cp-api is still migrating on a fresh cluster, dp-manager retries for up to about 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 for control-plane tables, such as organizations, environments, models, caller API keys, and budgets, owned by your role;
  • the auth schema for authentication tables, such as users and sessions;
  • the cp_api_app role, with NOSUPERUSER NOBYPASSRLS NOINHERIT NOLOGIN, granted only CRUD on control-plane tables and read/write on a few non-secret identity columns;
  • 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.

Database Privilege Checklist

Use this checklist when reviewing the database role with your DBA team:

RequirementPurpose
LOGINAllows control-plane services to connect as this role.
Database ownershipAllows cp-api to create and alter schemas, tables, indexes, functions, and RLS policies.
CREATEROLEAllows cp-api to create and manage the internal cp_api_app role.
BYPASSRLSAllows cross-organization control-plane paths and dp-manager to read the rows they need on the shared connection.
Not SUPERUSERKeeps the role below PostgreSQL superuser privilege.

Verify the Database 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

Troubleshoot Startup Errors

Use the startup error message to find the missing database capability.

cp-api database role must be SUPERUSER or have BYPASSRLS ...

The connection role is missing BYPASSRLS. Grant it as a database administrator:

ALTER ROLE aisix BYPASSRLS;

permission denied for schema public

The role cannot create objects in the public schema. Confirm database ownership. If the schema is locked down, grant schema access explicitly:

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

permission denied to create role

The connection role is missing CREATEROLE. Grant it as a database administrator:

ALTER ROLE aisix CREATEROLE;

permission denied to set role "cp_api_app"

The migration that grants cp_api_app membership likely did not finish. Check the cp-api startup logs for an earlier migration error.

Next Steps

After the database and role are ready, return to On-Premises Deployment and configure the control plane to use the external database.

For the full set of Docker Compose environment variables and Helm values, see On-Premises Configuration.

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