Skip to content
SYS.DOCS // DOCS

PostgreSQL

Edka provisions PostgreSQL on your cluster with CloudNativePG. Each database is a CloudNativePG Cluster in a namespace you choose. Its volumes and Secrets stay in your cluster, and its backups go to a bucket you own.

A PostgreSQL database has Overview, Insights, Logs, Users & DB, Backups, and Settings tabs. A Pooler tab appears when the connection pooler is on.

  • An Edka cluster. For 3 or 5 instances, use at least as many schedulable nodes as instances so each instance runs on its own node. With fewer, the General tab shows a Not enough nodes warning.
  • For backups, a bucket on Amazon S3, an S3 compatible service, or Google Cloud Storage, with credentials that can write to it. Azure Blob Storage is not a PostgreSQL backup target. See Object Storage.
  • For a Tailscale endpoint, the Tailscale add-on installed on the cluster. For a private load balancer, private networking enabled on the cluster.
  1. Open the cluster and select Databases under Workloads. Select Create Database, then pick PostgreSQL.
  2. On General, enter the Database name and the Namespace. Both are fixed after creation.
  3. Pick the PostgreSQL version. The default is 18.6. The overview lists every version.
  4. Set Instance count. 1 instance runs a single primary. 3 instances or 5 instances adds replicas with synchronous replication and automatic failover.
  5. Set Storage size (Gi), the data volume of each instance, and the Storage class. The default size is 10. The storage class is fixed after creation.
  6. Under Credentials, set the Database, the Application user that owns it, and the Superuser. All three names are fixed after creation. The form fills in a 24 character Application password and Superuser password. Replace them with your own of at least 8 characters, or keep them.
  7. Review the other tabs. Each one has working defaults.
  8. Select Create PostgreSQL.
TabSets
ImportAn empty database, a clone, or an import from another server
ResourcesCPU and memory requests and limits for each instance
ExtensionsExtensions to install in each database
AdvancedShared preload libraries and PostgreSQL parameters
PlacementThe node pool and taint toleration
AccessExternal access, its network, and its port
PoolingThe PgBouncer connection pooler
BackupWAL archiving and base backups to object storage

The database is ready when its status reads Ready and Runtime Status on Overview lists every instance as ready with one primary. Creation fails if the instances are not ready within 20 minutes.

On Resources, each instance requests 100 m of CPU and 256 Mi of memory by default. CPU limit (m) and Memory limit (Mi) are optional, and empty means no cap. PostgreSQL is killed if it exceeds the memory limit. Keep Memory request (Mi) at least twice shared_buffers. The form rejects a smaller request. Saving a resource change restarts the instances one at a time.

On Placement, pick a Node pool or keep Default cluster placement. Keep Tolerate node pool taints on when the pool is tainted. Otherwise the pods stay Pending.

Storage size (Gi) can grow later on a storage class that allows volume expansion. It cannot shrink.

The WAL volume switch on the General tab puts pg_wal on its own volume, which keeps write-ahead log I/O off the data volume. Set WAL storage size (Gi) and WAL storage class. You can add the WAL volume after creation. Once it exists you can grow it, but you cannot remove it or change its storage class.

On Pooling, turn on the Connection Pooling switch to put PgBouncer between clients and the primary.

FieldDefaultMeaning
Pool modeTransactionSession keeps one server connection per client
Max client connections1000Concurrent client connections PgBouncer accepts
Default pool size10Server connections per user and database pair

Edka runs one pooler pod for a single instance and three for 3 or 5 instances. The three pods must run on separate nodes, so a cluster with fewer than three schedulable nodes leaves some of them Pending.

With the pooler on, the connection details on Overview point at the pooler service, <name>-pooler-rw, and external access reaches PostgreSQL through the pooler. The Pooler tab charts queries, transactions, active and waiting clients, CPU, and memory for the pooler pods.

On Overview, the Connection Details card builds the connection for you. Pick the network and the user, then copy from Parameters or Connection string. Unlock the card to load passwords. Edka asks for step-up verification first.

Inside the cluster, clients connect to <name>-rw.<namespace>.svc.cluster.local:5432, or to <name>-pooler-rw.<namespace>.svc.cluster.local with the pooler on. Both follow the primary, so a failover does not change the address.

Credentials are in Kubernetes Secrets in the database namespace, each with username and password keys:

  • <name>-app for the application user
  • <name>-superuser for the superuser
  • <name>-role-<user> for each user added on Users & DB

To change a password, open Settings, then General, and enter a new value under Passwords.

On the Access tab, turn on External Access, pick a Network, and set the Port. The default port is 5432. The networks, hostname, and certificate work the same for every engine. See External access.

Require TLS rejects connections without TLS on every network. It is on by default for new databases. While it is on, clients inside the cluster connect with sslmode=require. With the pooler on, the external endpoint is the pooler, which still accepts sessions without TLS.

Once the certificate is issued, Verified connection on Overview shows the client command:

Terminal window
psql "host=<hostname> port=<port> dbname=<database> user=<user> sslmode=verify-full sslrootcert=system"

sslrootcert=system needs a PostgreSQL 16 or later client.

The Users & DB tab lists the users and databases Edka provisions beside the application user and its database.

  • Add User takes a Username and a Password of at least 8 characters. Superuser and Create databases grant those privileges.
  • Add Database takes a Database name and an Owner, which is the application user, the superuser, or a user from this tab.

Save the page to apply the changes. A saved user or database cannot be renamed or removed from this tab.

Extensions are on Settings, under Extensions. Add Extension takes:

  • Catalog or Extension name. The catalog lists extensions shipped with the PostgreSQL image, such as vector, pg_stat_statements, pg_trgm, and pgcrypto. Extension name is the name used in CREATE EXTENSION.
  • Target database, the database to install it in.
  • Ensure. Present installs the extension and Absent drops it.
  • Version and Schema, both optional.

The Advanced tab sets server configuration:

  • Shared preload libraries, one per line. Changing them restarts the instances. Libraries that a parameter needs, such as pg_stat_statements, are added for you.
  • Parameter Catalog, a searchable list of 35 common parameters, including max_connections, shared_buffers, work_mem, and the autovacuum_* family.
  • Custom Parameters for anything not in the catalog.

Memory parameters such as shared_buffers and work_mem need a unit, for example 512MB. shared_buffers can be at most half of the memory request. Parameters that CloudNativePG controls, such as archive_command, listen_addresses, port, and the ssl settings, are rejected.

New databases set pg_stat_statements.max = 10000 and pg_stat_statements.track = all.

Backups use the Barman Cloud plugin. It archives WAL continuously and takes base backups, which together allow point in time recovery.

  1. Open Settings, then Backup. The same tab is in the create form.
  2. Turn on the Backups switch. On an existing database, turning backups on or off restarts the instances when you save.
  3. Set Retention from 1 to 35 days. The default is 7. Base backups and WAL archives older than this are deleted, so this is also how far back a restore can go.
  4. Set the Path prefix, the folder inside the bucket. The default is postgres_backup. Backups land in <prefix>/<database-id>/. The database ID follows /databases/ in the database page URL.
  5. Pick the storage. With Use a saved object storage integration on, select an Integration. With it off, pick a Provider:
    • Amazon S3 / S3-Compatible takes the S3 bucket, Access key ID, and Secret access key. For MinIO, Ceph, Wasabi, or Hetzner Object Storage, also turn on S3-compatible endpoint and enter the S3 endpoint URL, for example https://fsn1.your-objectstorage.com.
    • Google Cloud Storage takes the GCS bucket and the Service account JSON.
  6. Save.

The first base backup starts as soon as backups are on. After that a base backup runs every day at 00:00 UTC. Backups are gzip compressed. On Amazon S3 and Google Cloud Storage they use AES256 server side encryption.

The Backups tab lists each backup under Backup Snapshots with its name, status, method, completion time, and instance. The line above the list reads “Last backup … Recoverable from …” once a base backup completes. Backup now takes an extra backup. It is available when backups are on, the database is ready, and no other backup is running.

A restore always creates a new database in the same Kubernetes cluster and leaves the source unchanged. The source database must still exist. After you delete a database, its backups stay in the bucket but Edka can no longer restore them.

  1. Open the Backups tab of the source database.
  2. Under Restore Method, pick one:
    • Latest WAL replays every archived change up to the most recent recovery point.
    • Specific timestamp restores to a moment between Earliest Point and Latest Safe Target. Enter the time in your local timezone.
    • Backup snapshot restores a completed base backup without replaying later changes.
  3. Under Target, enter the New Database Name, which defaults to <source>-restored, and the Target Namespace.
  4. Select Start Restore.

Latest Safe Target trails the last archived WAL segment by about five minutes. Restore is unavailable until the first base backup completes.

The new database copies the full configuration of the source: instance count, storage, resources, placement, pooler, external access, parameters, users, extensions, and backup settings. It also keeps the source passwords. It runs the PostgreSQL major version that wrote the backup, and its own backups go to a new folder under the same path prefix.

The Import tab of the create form sets where the first data comes from. The choice is fixed after creation.

  • Empty database starts with only the database and user from General.
  • Clone an existing cluster copies a CloudNativePG cluster running in the same Kubernetes cluster and namespace with pg_basebackup. The version, database, and application user follow the Source cluster.
  • Import from an external PostgreSQL runs pg_dump and pg_restore against any PostgreSQL server the cluster can reach.

For an import, set:

  • Import type. Microservice copies one Source database into the database and user from General. Monolith keeps the source database and role names. List them under Source databases and Source roles, one per line, or enter * alone for all of them.
  • Source host, Source port, Connection database (usually postgres), Source user, and Source password. The user needs read access to everything being imported. The password is stored in the <name>-import-source Secret.
  • SSL mode. The default is Prefer. Use Require or stricter for a server reached over the internet.
  • pg_dump and pg_restore options, one flag per line, for example --no-owner.

The import runs while the database is being created, and Logs shows its progress. Creation fails if the import has not finished after 24 hours. With backups on, the first base backup starts when the import finishes.

Open Settings, then General, and pick a newer PostgreSQL version. Save the version change on its own, without other edits. Downgrades are rejected.

  • Minor upgrade. Select Apply Upgrade. With replicas, CloudNativePG updates the replicas first and then switches the primary over to an updated replica. A single instance restarts in place.
  • Major upgrade. The database goes offline while pg_upgrade runs, and the replicas are cloned again afterwards. Turn on Acknowledge major upgrade, then select Start Major Upgrade. With backups on, Edka takes a backup before the upgrade and cancels the upgrade if that backup fails. It takes another backup afterwards and refreshes planner statistics with vacuumdb --all --analyze-in-stages. With backups off, take your own backup first.

Insights charts transactions, connections, CPU, and memory, and reports connection capacity, cache hit ratio, database size, and replication lag. History needs VictoriaMetrics installed on the cluster. Without it the charts cover the current session only.

Current Connections lists client sessions with their query and duration. Query Statistics shows the top statements from pg_stat_statements. Install that extension under Settings, then Extensions, to collect them.

With VictoriaMetrics installed, Edka installs the Postgres alert pack when a PostgreSQL database becomes ready. Its ten rules cover unreachable instances, a missing primary, a required manual switchover, stalled WAL archiving, exceeded WAL retention, failed backups, connection saturation, waiting backends, long transactions, and replication lag. See Alert packs.

Open Settings, then General, and use the delete section at the bottom. Type delete me to confirm. Deleting removes the database, its volumes, and its Kubernetes Secrets from the cluster. Backups stay in the bucket. Remove them there when you no longer need them. Apps that still use the database block the delete.

  • Instance or pooler pods stay Pending. The selected node pool has too few schedulable nodes, or Tolerate node pool taints is off for a tainted pool. The three pooler pods of a 3 or 5 instance database each need their own node.

  • The Backups tab says “Continuous archiving is on. No completed backup yet.” long after the database is ready. List the backups and read the error on the failed one. Wrong credentials, a missing bucket, or a wrong endpoint URL show up there.

    Terminal window
    kubectl get backups.postgresql.cnpg.io -n <namespace>
    kubectl describe backups.postgresql.cnpg.io <backup-name> -n <namespace>
  • Backup now is disabled. It needs backups on, the database in the Ready state, and no backup already running.

  • The Integration list is empty. Only Amazon S3, S3 compatible, and Google Cloud Storage integrations qualify.

  • An import is still running. Follow it on Logs, or list its pods:

    Terminal window
    kubectl get pods -n <namespace> -l cnpg.io/cluster=<name>,cnpg.io/jobRole=import

For kubectl, the resources of a database named <name> are the Cluster <name>, the Pooler <name>-pooler-rw, the ScheduledBackup <name>-backup, and the ObjectStore <name>-backup-store.