kupe database command reference
kupe database, or kupe db, creates, inspects, resizes and deletes
PostgreSQL databases and binds them into namespaces of your clusters. The CLI
never shows a password. For the model behind these commands, see
Databases. To install the CLI, see
Kupe CLI.
--cpu, --memory and --storage are per instance. A database with high
availability runs two instances, each requesting those resources.
Every command also takes the CLI’s global flags (--tenant, --context,
--config, --token, --api-url, --quiet, --verbose, --no-color).
Commands
Section titled “Commands”| Command | Does |
|---|---|
list |
List databases in the tenant |
get NAME |
Show one database |
create NAME |
Create a database and wait for it to run |
update NAME |
Resize, or change high availability or deletion protection |
delete NAME |
Delete a database and its data |
rotate-credentials NAME |
Rotate one role’s password |
wait NAME |
Block until a database reaches a phase |
binding add DATABASE |
Bind a role into namespaces of a cluster |
binding list DATABASE |
List a database’s bindings |
binding get DATABASE BINDING |
Show one binding |
binding update DATABASE BINDING |
Add or remove a binding’s namespaces |
binding delete DATABASE BINDING |
Remove a binding |
kupe database list
Section titled “kupe database list”kupe database list [--phase PHASE] [-o table|wide|json|yaml|name|go-template=...]NAME ENGINE VERSION RUNNING PHASE HA AGEorders-db postgres 18 18.6 Running off 3dVERSION is the major you asked for and RUNNING the release the database
runs. -o wide adds CPU, MEMORY, STORAGE, INSTANCES (ready/desired)
and PROTECTED.
--phase shows only the databases in one phase: pending, provisioning,
running, upgrading, degraded or terminating, in any case. The CLI
filters the list after the API returns it. Any other value exits 2.
kupe db list --phase degradedkupe database get
Section titled “kupe database get”kupe database get NAME [-o table|json|yaml|go-template=...]Name: orders-dbEngine: postgresVersion: 18 (running 18.6)Phase: RunningHigh Availability: offInstances: 1/1CPU: 500mMemory: 1GiStorage: 10GiDeletion Protection: onCreated: 2026-10-09T10:00:00Z (3d ago)Exits 4 if the database does not exist.
kupe database create
Section titled “kupe database create”kupe database create NAME --cpu CPU --memory MEMORY --storage STORAGE [flags]| Flag | Default | Description |
|---|---|---|
--cpu |
required | CPU per instance, without a unit: 500m, 2, 0.5. |
--memory |
required | Memory per instance, with a unit: 1Gi, 512Mi. |
--storage |
required | Storage per instance, with a unit: 10Gi. Grows later, never shrinks. |
--version |
platform default | Major version, for example 18. |
--engine |
postgres |
The only engine. |
--ha |
off | Two instances with a synchronous standby. |
--no-deletion-protection |
off | Create without deletion protection, which is otherwise on. |
--wait |
true |
Wait for Running. --wait=false returns once the request is accepted. |
--wait-timeout |
30m |
Give up waiting after this long. |
-o, --output |
table | table, json, yaml or go-template=... |
# The smallest databasekupe database create orders-db --cpu 250m --memory 512Mi --storage 5Gi
# Highly available, a specific major, without waitingkupe db create orders-db --version 17 --cpu 1 --memory 2Gi --storage 20Gi --ha --wait=falseThe API enforces the platform minimum and maximum sizes and your plan’s limits, and the CLI prints its message as returned.
| Exit | Meaning |
|---|---|
0 |
The database is Running, or the request was accepted with --wait=false |
1 |
The database entered Degraded |
2 |
Missing or malformed flags, or the API rejected the request |
5 |
The name already exists, or a common refusal such as the plan’s database limit |
7 |
Database creation is paused, the API is temporarily unavailable, or the CLI lost contact with the API while waiting |
8 |
--wait-timeout expired |
130 |
Interrupted while waiting. Creation continues |
kupe database update
Section titled “kupe database update”kupe database update NAME [--cpu CPU] [--memory MEMORY] [--storage STORAGE] [--ha|--ha=false] [--deletion-protection|--deletion-protection=false] [--wait=false] [--wait-timeout DURATION] [-o FORMAT]| Flag | Default | Description |
|---|---|---|
--cpu |
unchanged | New CPU per instance, without a unit. |
--memory |
unchanged | New memory per instance, with a unit. |
--storage |
unchanged | New storage per instance, with a unit. Grows only. |
--ha |
unchanged | --ha turns high availability on and --ha=false turns it off. |
--deletion-protection |
unchanged | --deletion-protection turns it on and --deletion-protection=false turns it off. |
--wait |
true |
Wait until the change is in effect. --wait=false returns once it is accepted. |
--wait-timeout |
30m |
Give up waiting after this long. |
-o, --output |
table | table, json, yaml or go-template=... |
Only the flags you pass are changed, and at least one is required. CPU and memory changes restart the instances one at a time (switching over first with high availability), and storage grows online. The major version cannot be changed.
kupe database update orders-db --cpu 1 --memory 2Gikupe db update orders-db --storage 20Gikupe db update orders-db --hakupe db update orders-db --deletion-protection=falseBy default the command waits until the change is in effect, which is when all three of these hold:
status.observedGenerationhas reached the generation the change produced, so the platform has applied it.- The phase is
Running. - The
Readycondition isTrue, so every instance runs the change. The phase staysRunningwhile the instances roll onto it.
A database with one instance is Degraded while that instance restarts for a
CPU or memory change. The wait rides that out, and exits 1 if the database is
still Degraded 5 minutes later. Degraded counts only once
observedGeneration has caught up, so a status read from before the change
never fails the wait. A database that starts being deleted ends the wait at
once with exit 1, and a change the platform never applies ends in exit 8 at
--wait-timeout.
A change to deletion protection alone is in effect as soon as the API accepts
it, because a delete reads the setting when it arrives, so the command does not
wait for it. Turning protection off on a Degraded database, or on one that
never finished provisioning, returns at once.
A smaller --storage exits 2 with the API’s message, for example
storage can only grow: 5Gi is smaller than the current 10Gi. Any other rejected
change exits 2 with the API’s message, which names the field and the rule it
broke. If someone else changed the database at the same moment, the CLI retries
once, then exits 5.
| Exit | Meaning |
|---|---|
0 |
The change is in effect, or was accepted (with --wait=false, or for deletion protection alone) |
1 |
The database stayed Degraded for 5 minutes after the platform applied the change, or started being deleted |
2 |
Missing or malformed flags (including none at all), or the API rejected the change |
4 |
The database does not exist |
5 |
The database changed again after the retry, or a common refusal |
7 |
The API is temporarily unavailable, or the CLI lost contact with it while waiting |
8 |
--wait-timeout expired. The platform keeps applying the change |
130 |
Interrupted while waiting. The change continues |
kupe database delete
Section titled “kupe database delete”kupe database delete NAME [--yes] [--wait=false] [--wait-timeout DURATION]| Flag | Default | Description |
|---|---|---|
-y, --yes |
off | Skip the confirmation prompt. Required without a terminal. |
--wait |
true |
Wait until the database is gone. --wait=false returns once the delete is accepted. |
--wait-timeout |
15m |
Give up waiting after this long. |
On a terminal you type the database name to confirm. A database with deletion
protection on is refused (exit 5): turn it off first with
kupe database update NAME --deletion-protection=false.
Deleting a database removes it immediately, with its Service and credentials Secrets in every bound namespace. Its storage is kept for 7 days for support-assisted recovery, then permanently deleted. There is no self-service restore.
By default the command waits until the database is gone. It is Terminating
while its bindings are removed from every cluster namespace and its instances
stop, which can take several minutes. Progress and the final
database NAME deleted line go to stderr, and nothing is printed on stdout.
With --wait=false the command prints database/NAME delete requested on
stdout as soon as the delete is accepted. Follow it with
kupe database wait NAME --for deleted.
kupe database delete orders-dbkupe db delete orders-db --yes --wait=false| Exit | Meaning |
|---|---|
0 |
The database is gone, or the delete was accepted with --wait=false |
2 |
Confirmation missing or not matching |
4 |
The database does not exist |
5 |
Deletion protection is on, or the database kept changing during the delete |
7 |
The API is temporarily unavailable, or the CLI lost contact with it while waiting |
8 |
--wait-timeout expired. Deletion continues |
130 |
Interrupted while waiting. Deletion continues |
kupe database rotate-credentials
Section titled “kupe database rotate-credentials”kupe database rotate-credentials NAME --role owner|readwrite|readonly [--yes]Requests a new password for one role and, by default, waits until it is in
effect: the database uses the new password, so the old one no longer works, and
every binding of the role has written it to its credentials Secret in every
namespace it lists. Each role has its own password; rotating one leaves the
others unchanged. If someone
rotates the same role again meanwhile, the command waits for that newer
rotation instead. Applications holding the old password must restart. Prompts
on a terminal, and --yes is required without one. Prints
database/NAME ROLE credentials rotated, or with --wait=false
database/NAME ROLE credentials rotation requested. Exits 5 if the database
is being deleted, and 8 if the rotation is not in effect by --wait-timeout
(default 10m); the platform keeps applying it.
kupe database rotate-credentials orders-db --role readwritekupe db rotate-credentials orders-db --role readonly --yeskupe database wait
Section titled “kupe database wait”kupe database wait NAME [--for PHASE] [--timeout DURATION]--for takes running (the default), pending, provisioning, upgrading,
degraded, terminating or deleted. --timeout defaults to 30m.
| Exit | Meaning |
|---|---|
0 |
The database reached the phase, or is gone with --for deleted |
1 |
The database entered Degraded while waiting for another phase |
7 |
Lost contact with the API while waiting |
8 |
--timeout expired |
130 |
Interrupted while waiting |
kupe database binding add
Section titled “kupe database binding add”kupe database binding add DATABASE --cluster CLUSTER --namespace NAMESPACE... --role owner|readwrite|readonly [flags]| Flag | Default | Description |
|---|---|---|
--cluster |
required | Cluster to bind into. It may not exist yet. |
--namespace |
required | Namespace that receives the Service and Secret. Repeat it for each namespace, up to 10. |
--role |
required | owner (schema changes), readwrite (reads and writes data) or readonly (reads data). |
--secret-name |
DATABASE-ROLE |
Name of the credentials Secret in every namespace. |
--name |
DATABASE-CLUSTER-ROLE |
Binding name. Set it if the default is longer than 63 characters, or for a second binding of the same role into the same cluster. |
--wait |
true |
Wait until the binding is Ready in every namespace. |
--wait-timeout |
5m |
Give up waiting after this long. |
-o, --output |
table | table, json, yaml or go-template=... |
# Migrations with the owner role, the API and its workers with the readwrite rolekupe database binding add orders-db --cluster prod --namespace orders-migrate --role ownerkupe db binding add orders-db --cluster prod --namespace orders-api --namespace orders-worker --role readwrite
# A reporting job with the readonly rolekupe db binding add orders-db --cluster prod --namespace orders-reports --role readonlyThe default name leaves the namespaces out, because they can change. A long
database or cluster name can take it past 63 characters (readwrite is the
longest role name), and then the command needs --name. A second binding of
the same role into the same cluster also needs --name; usually
kupe database binding update ... --add-namespace on the first one is what you
want, and the hint on the refusal says so.
A binding waits in Pending while its database or cluster does not exist yet
or is not running. A wait that runs out then exits 8 with the binding’s
message, for example
binding "orders-db-prod-readwrite" is still Pending: cluster prod does not exist.
A binding is Failed when one of its namespaces already has a Service or
Endpoints object named after the database, or a Secret of the binding’s Secret
name, that the platform did not create. The command stops at once:
Error: binding "orders-db-prod-readwrite" failed: a Secret named "orders-db-readwrite" already exists in namespace "orders-api", not created by the platform; remove it, or bind with another secretNameRename or remove that object and the binding becomes Ready on its own, or
delete the binding and add it again with another --secret-name. When the
binding lists other namespaces without a clash, the hint also gives the
binding update --remove-namespace command that stops binding into the
clashing one.
| Exit | Meaning |
|---|---|
0 |
Ready, or accepted with --wait=false |
1 |
Failed for a reason other than a name clash |
2 |
Missing or malformed flags |
4 |
The database does not exist |
5 |
The binding name exists, or the binding is Failed on a name clash |
7 |
Lost contact with the API while waiting |
8 |
--wait-timeout expired |
130 |
Interrupted while waiting. The binding was created |
kupe database binding list
Section titled “kupe database binding list”kupe database binding list DATABASE [-o table|wide|json|yaml|name|go-template=...]One row per namespace of each binding, with columns NAME, CLUSTER,
NAMESPACE, ROLE, SECRET, PHASE and AGE. -o wide adds the
namespace’s HOST and MESSAGE. PHASE is Pending for a namespace the
platform has not reported yet, and Removing for one taken out of the binding
that is still being withdrawn. -o json and -o yaml print the bindings as the
API returns them. Exits 4 if the database does not exist.
$ kupe db binding list orders-dbNAME CLUSTER NAMESPACE ROLE SECRET PHASE AGEorders-db-prod-owner prod orders-migrate owner orders-db-owner Ready 2morders-db-prod-readwrite prod orders-api readwrite orders-db-readwrite Ready 2morders-db-prod-readwrite prod orders-worker readwrite orders-db-readwrite Pending 2morders-db-prod-readonly prod orders-reports readonly orders-db-readonly Ready 1mkupe database binding get
Section titled “kupe database binding get”kupe database binding get DATABASE BINDING [-o table|json|yaml|go-template=...]Shows the binding and its Secret name, then a table of its namespaces with each
one’s phase, in-cluster host and message. A binding that belongs to another
database exits 4.
kupe database binding update
Section titled “kupe database binding update”kupe database binding update DATABASE BINDING [--add-namespace NAMESPACE]... [--remove-namespace NAMESPACE]... [flags]| Flag | Default | Description |
|---|---|---|
--add-namespace |
none | Namespace to add. Repeat it for each namespace. |
--remove-namespace |
none | Namespace to remove. Repeat it for each namespace. |
--wait |
true |
Wait until every listed namespace is Ready and every removed one is withdrawn. |
--wait-timeout |
5m |
Give up waiting after this long. |
-o, --output |
table | table, json, yaml or go-template=... |
kupe database binding update orders-db orders-db-prod-readwrite --add-namespace orders-workerkupe db binding update orders-db orders-db-prod-readwrite --remove-namespace orders-workerA namespace added gets the Service and the Secret. A namespace removed loses them and its network path to the database, while the other namespaces keep working; the Service and the network path stay if another binding of the same database lists that namespace. The binding’s cluster, role and Secret name cannot change.
The command reads the binding, edits its namespace list and writes it back. If the binding changed in between, it reads it again and retries once. Adding a namespace that is already listed, or removing one that is not, changes nothing: the command then sends no request and says so on stderr.
A binding keeps 1 to 10 namespaces, so a change that would leave none (delete
the binding instead) or more than 10 exits 2 before a request is sent. The
platform checks the namespaces added as it does for binding add, and its
message names the rule a change breaks: the namespace is already bound with the
same role in that cluster, the Secret name is already used there, or the
database’s bindings would list more than 20 namespaces. A change that only
removes namespaces passes those checks.
Removing a namespace does not change the role’s password: rotate it with
kupe database rotate-credentials if that namespace should also lose knowledge
of it.
| Exit | Meaning |
|---|---|
0 |
Ready in every listed namespace and every removed one withdrawn, or accepted with --wait=false |
1 |
A listed namespace is Failed for a reason other than a name clash |
2 |
Missing or contradictory flags, a list that would be empty or over 10, or a change the platform refused |
4 |
The database or the binding does not exist |
5 |
The binding or its database is being deleted, the binding kept changing, or a listed namespace is Failed on a name clash |
7 |
Lost contact with the API while waiting |
8 |
--wait-timeout expired |
130 |
Interrupted while waiting. The change was accepted |
kupe database binding delete
Section titled “kupe database binding delete”kupe database binding delete DATABASE BINDING [--yes]Removes the Secret from every namespace the binding lists, and the Service and
the network path from each one no other binding of the database lists. The
role’s password does not change: rotate it with
kupe database rotate-credentials if those namespaces should also lose
knowledge of it. To remove only some of the namespaces, use
kupe database binding update --remove-namespace. Prompts on a terminal, and --yes is required without one. The removal
happens in the background and the command prints
binding/NAME delete requested.
Refusals every command can get
Section titled “Refusals every command can get”Every kupe database command maps these refusals the same way. The error text
is the API’s message, and the hint under it is the next step.
| Refusal | Exit | Next step |
|---|---|---|
Database creation is paused (creation_paused) |
7 |
Retry later, and check status.kupe.cloud |
The database is being deleted (terminating) |
5 |
kupe database wait NAME --for deleted |
The tenant is being deleted (tenant_terminating) |
5 |
None |
The tenant is suspended (tenant_suspended) |
5 |
Contact support |
The plan’s database limit is reached (plan_limit_reached) |
5 |
Delete a database or upgrade the plan |
The API is temporarily unavailable (HTTP 503) |
7 |
Retry in a few seconds |
The tenant refusals apply to growing a database (more CPU, memory or storage,
or high availability turned on) as well as to creating one. The CLI never
retries a create or an update on its own, so after a 503 the script or the
person running it decides whether to retry.