Skip to content

render cli v2.20.0 pg_dump postgres instance delete and api auth issues

Needed to delete an abandoned Render Postgres instance and pg_dump it first, from a machine where the Render CLI (v2.20.0) was already logged in but no Render API key was available. render services --confirm listed everything fine, so I assumed the CLI's stored credential would work as a REST bearer token: read the single token field out of ~/.render/cli.yaml and called GET https://api.render.com/v1/services?limit=1 with Authorization: Bearer <token>, which returned urllib.error.HTTPError: HTTP Error 401: Unauthorized. Also could not find any delete verb: render services --help lists only create, instances, and update subcommands, and neither the top-level help nor render services mentions datastore deletion at all, which made it look like destructive datastore ops were API-only. Separately, the instance I wanted to inspect did not appear in render services -o json output at all until much later, and render psql <id> -c "select pg_size_pretty(pg_database_size(current_database()))" reported a 7766 kB database for what should have been multi-gigabyte production data, so I briefly concluded I was connected to the wrong instance.

1 solution
ranked by outcome — not votes
Accepted

Four separate Render CLI v2.20.0 behaviors, all undocumented in the top-level help:

1. The CLI credential is not an API key. ~/.render/cli.yaml stores a field named refreshtoken, which the CLI exchanges for short-lived tokens internally. It is not accepted as Authorization: Bearer by api.render.com/v1 — you get a bare 401 with no hint about the credential type. There is no render command that prints a usable API key either. If you need REST access, create a key in the dashboard (Account Settings → API Keys); do not try to borrow the CLI's session.

2. Destructive datastore commands live under render ea (early access), not the main surface.

render ea pg --help      # create, delete, get, list, resume, suspend, update
render ea kv --help
render ea objects --help
render ea pg delete dpg-0123456789 --confirm -o json

So the CLI can delete a Postgres instance; it is just not reachable from render services. Services (web/worker/cron) still have no CLI delete — that remains REST-only (DELETE /v1/services/{id}).

3. Connection strings need an opt-in flag and land in a sibling top-level key.

render ea pg get dpg-0123456789 --include-sensitive-connection-info -o json --confirm

The JSON is {"postgres": {...}, "connectionInfo": {...}}. The credentials are not inside the postgres object where the rest of the metadata lives — connectionInfo is a peer key holding externalConnectionString, internalConnectionString, password, and psqlCommand. Code that does data["postgres"].get("externalConnectionString") silently gets None.

To dump without leaking the URI into ps output, parse it and use libpq environment variables instead of -d <uri>:

u = urllib.parse.urlparse(info["connectionInfo"]["externalConnectionString"])
env = dict(os.environ, PGHOST=u.hostname, PGPORT=str(u.port or 5432),
           PGUSER=urllib.parse.unquote(u.username),
           PGPASSWORD=urllib.parse.unquote(u.password),
           PGDATABASE="mydb", PGSSLMODE="require")
subprocess.run(["pg_dump", "-Fc", "-Z6", "-f", out_path], env=env, check=True)

4. Two listing gotchas that mimic "wrong instance" symptoms. render services hides preview resources unless you pass --include-previews (same as includePreviews=true on the REST endpoint), so PR-preview instances are invisible by default. And current_database() on a Render Postgres connection is the instance's default database, which is usually an empty ~7766 kB shell — your application data lives in a separately named database on the same instance. Query the catalog instead of trusting the default:

select datname, pg_size_pretty(pg_database_size(datname)) sz,
       (select count(*) from pg_stat_activity a where a.datname = d.datname) conns
  from pg_database d order by pg_database_size(datname) desc limit 5;

That query is also the reliable way to tell a live instance from an abandoned clone: identical database names, but the live one shows a much larger size and nonzero connection count.