Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions product_docs/docs/postgres_for_kubernetes/1/backup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,13 @@ The schedule `"0 0 0 * * *"` triggers a backup every day at midnight
(00:00:00). In Kubernetes CronJobs, the equivalent expression would be `0 0 * * *`,
since seconds are not supported.

!!!warning

The `spec.cluster` field is immutable after creation. To schedule backups
for a different `Cluster`, create a new `ScheduledBackup` resource instead
of updating an existing one.
!!!

### Backup Frequency and RTO

!!!tip Hint
Expand Down
21 changes: 16 additions & 5 deletions product_docs/docs/postgres_for_kubernetes/1/bootstrap.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,17 @@ cluster. An error in any of those queries will interrupt the bootstrap phase,
leaving the cluster incomplete and requiring manual intervention.
!!!

!!!note

These queries run with the standard `"$user", public` `search_path`, even
though operator-issued connections otherwise pin a fixed `search_path` for
security reasons.
See [Schema resolution and `search_path` hardening](security.md#schema-resolution-and-search_path-hardening)
for details.
Schema-qualify object references if you need them to be independent of the
`search_path`.
!!!

!!!info Important
Ensure the existence of entries inside the ConfigMaps or Secrets specified
in `postInitSQLRefs`, `postInitTemplateSQLRefs`, and
Expand Down Expand Up @@ -647,7 +658,7 @@ file on the source PostgreSQL instance:
host replication streaming_replica all md5
```

The following manifest creates a new PostgreSQL 18.3 cluster,
The following manifest creates a new PostgreSQL 18.4 cluster,
called `target-db`, using the `pg_basebackup` bootstrap method
to clone an external PostgreSQL cluster defined as `source-db`
(in the `externalClusters` array). As you can see, the `source-db`
Expand All @@ -662,7 +673,7 @@ metadata:
name: target-db
spec:
instances: 3
imageName: docker.enterprisedb.com/k8s/postgresql:18.3-standard-ubi9
imageName: docker.enterprisedb.com/k8s/postgresql:18.4-standard-ubi9

bootstrap:
pg_basebackup:
Expand All @@ -682,7 +693,7 @@ spec:
```

All the requirements must be met for the clone operation to work, including
the same PostgreSQL version (in our case 18.3).
the same PostgreSQL version (in our case 18.4).

#### TLS certificate authentication

Expand All @@ -698,7 +709,7 @@ This example can be easily adapted to cover an instance that resides
outside the Kubernetes cluster.
!!!

The manifest defines a new PostgreSQL 18.3 cluster called `cluster-clone-tls`,
The manifest defines a new PostgreSQL 18.4 cluster called `cluster-clone-tls`,
which is bootstrapped using the `pg_basebackup` method from the `cluster-example`
external cluster. The host is identified by the read/write service
in the same cluster, while the `streaming_replica` user is authenticated
Expand All @@ -713,7 +724,7 @@ metadata:
name: cluster-clone-tls
spec:
instances: 3
imageName: docker.enterprisedb.com/k8s/postgresql:18.3-standard-ubi9
imageName: docker.enterprisedb.com/k8s/postgresql:18.4-standard-ubi9

bootstrap:
pg_basebackup:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ names in user-provided certificates for the `<cluster>-rw` service used for
communication within the cluster.
!!!

!!!note

Beyond the CA-managed certificates listed above, the operator also uses a
self-signed client certificate, generated in memory, to authenticate to the
instance manager's status port. This certificate is not signed by the client
CA: the instance manager trusts it by pinning its public-key fingerprint. See
[Operator-to-instance authentication](security.md#operator-to-instance-authentication).
!!!

## Operator-Managed Mode

By default, the operator automatically generates a single Certificate Authority
Expand Down
6 changes: 6 additions & 0 deletions product_docs/docs/postgres_for_kubernetes/1/cnpg_i.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ TCP gRPC endpoint behind a Service, with **mTLS** for secure communication.
Standalone plugins are discovered dynamically by watching for Services with the
required labels and annotations — no operator restart is needed.

When a standalone plugin's pods are rolled (for example, after upgrading its
image), the operator detects the change through the EndpointSlices backing the
plugin Service and reconciles every cluster using that plugin, so they start
interacting with the new pods promptly instead of waiting for the periodic
resync.

Example Deployment:

```yaml
Expand Down
211 changes: 192 additions & 19 deletions product_docs/docs/postgres_for_kubernetes/1/connection_pooling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ spec:
The pooler name can't be the same as any cluster name in the same namespace.
!!!

!!!warning

The `spec.cluster` field is immutable after creation. To point a pooler at
a different `Cluster`, create a new `Pooler` resource instead of updating
an existing one.
!!!

This example creates a `Pooler` resource called `pooler-example-rw`
that's strictly associated with the Postgres `Cluster` resource called
`cluster-example`. It points to the primary, identified by the read/write
Expand Down Expand Up @@ -190,15 +197,27 @@ GRANT CONNECT ON DATABASE postgres TO cnp_pooler_pgbouncer;

Create the lookup function for password verification. This function is created
in the `postgres` database with `SECURITY DEFINER` privileges and is used by
PgBouncer’s `auth_query` option:
PgBouncer’s `auth_query` option. Because it runs as the function owner, its
`search_path` is pinned to `pg_catalog, pg_temp` so that the function body
cannot resolve operators or objects through a caller- or
tenant-controlled `search_path`:

```sql
CREATE OR REPLACE FUNCTION public.user_search(uname TEXT)
RETURNS TABLE (usename name, passwd text)
LANGUAGE sql SECURITY DEFINER AS
LANGUAGE sql SECURITY DEFINER
SET search_path = pg_catalog, pg_temp AS
'SELECT usename, passwd FROM pg_catalog.pg_shadow WHERE usename=$1;';
```

!!!note

Clusters created with an earlier version of {{name.ln}} carry a
`user_search` function without the pinned `search_path`. The operator
recreates the function with the `SET search_path` clause automatically
during reconciliation when the cluster is upgraded.
!!!

Restrict and grant permissions on the lookup function:

```sql
Expand Down Expand Up @@ -280,10 +299,89 @@ spec:
topologyKey: "kubernetes.io/hostname"
```

#### Custom image and resource limits
#### Resource limits

You can define resource requests and limits by adding a container named
`pgbouncer` to the `template` section:

```yaml
# ...
template:
metadata:
# ...
spec:
containers:
# This name MUST be "pgbouncer"
- name: pgbouncer
resources:
requests:
cpu: "0.1"
memory: 100Mi
limits:
cpu: "0.5"
memory: 500Mi
```

## PgBouncer image

By default, {{name.ln}} deploys the
[latest stable PgBouncer image](https://docker.enterprisedb.com/k8s/pgbouncer)
the operator was built against. You can override that default in three ways.
When more than one is set, the sources are evaluated top-down — the first
match in the list below is used; if none match, the operator's built-in
default applies:

1. An explicit image set on the `pgbouncer` container inside
`spec.template.spec.containers` (escape hatch — see
[Pod-template override](#pod-template-override) below).
2. `spec.pgbouncer.image` — an image reference set directly on the `Pooler`.
3. `spec.pgbouncer.imageCatalogRef` — a reference to an entry in an
`ImageCatalog` or `ClusterImageCatalog`.

`spec.pgbouncer.image` and `spec.pgbouncer.imageCatalogRef` are mutually
exclusive — set at most one.

!!!warning Policy gating

If you enforce admission policies that restrict which PgBouncer images
may run, those policies **must gate all three** image sources:
`spec.pgbouncer.image`, `spec.pgbouncer.imageCatalogRef`, and the
`image` field on a `pgbouncer` container inside
`spec.template.spec.containers`. A policy that covers only the first
two leaves the pod-template override as an unguarded escape hatch.
The same consideration applies to `Cluster.spec.imageName` and
`Cluster.spec.imageCatalogRef`.
!!!

### Setting an explicit image

Use `spec.pgbouncer.image` to pin a specific PgBouncer version or pull from
a private registry:

```yaml
apiVersion: postgresql.k8s.enterprisedb.io/v1
kind: Pooler
metadata:
name: pooler-example-rw
spec:
cluster:
name: cluster-example
instances: 3
type: rw
pgbouncer:
poolMode: session
image: docker.enterprisedb.com/k8s/pgbouncer:1.25.1-ubi9
```

### Using an image catalog

You can specify a custom image and define resource requests/limits. Note that
the container name is explicitly set to `pgbouncer`.
The `Pooler` resource can manage the PgBouncer container image centrally via
an `ImageCatalog` or `ClusterImageCatalog`, following the same pattern as
`Cluster` resources (see [Image Catalog](image_catalog.md)). The catalog
entry is selected by the `key` defined in the catalog's `componentImages`
list.

Reference a catalog entry with `spec.pgbouncer.imageCatalogRef`:

```yaml
apiVersion: postgresql.k8s.enterprisedb.io/v1
Expand All @@ -295,29 +393,68 @@ spec:
name: cluster-example
instances: 3
type: rw
pgbouncer:
poolMode: session
imageCatalogRef:
apiGroup: postgresql.k8s.enterprisedb.io
kind: ImageCatalog
name: my-catalog
key: pgbouncer
```

To use a cluster-wide catalog instead, set `kind: ClusterImageCatalog` and
point `name` at the corresponding resource — the rest of the spec is
identical.

When a catalog entry is updated, the operator automatically reconciles all
poolers referencing it and rolls out the new image without any change to the
`Pooler` spec.

### Pod-template override

The pod template can also carry an `image` on the `pgbouncer` container, in
which case it overrides every other source (including `spec.pgbouncer.image`
and `spec.pgbouncer.imageCatalogRef`). Treat this as an **escape hatch** —
use it only when you need to customize other container-level settings
(resources, environment, security context) and happen to want to pin the
image in the same place. For routine image changes, prefer
`spec.pgbouncer.image` or an image catalog: those fields are validated,
mutually exclusive, and visible to admission policies that gate the
PgBouncer image (see [Pod templates](#pod-templates) for the broader
template mechanics):

```yaml
# ...
template:
metadata:
labels:
app: pooler
spec:
containers:
# This name MUST be "pgbouncer"
- name: pgbouncer
image: my-pgbouncer:latest
resources:
requests:
cpu: "0.1"
memory: 100Mi
limits:
cpu: "0.5"
memory: 500Mi
```

### Monitoring the resolved image

The operator stores the resolved image in `status.image` and reflects the
outcome in `status.phase`, one of `active`, `paused`, `inactive`, or
`failed`. On `failed`, `status.phaseReason` describes the cause (for
example, if the catalog or key does not exist). You can inspect the
current state with:

```shell
kubectl get pooler pooler-example-rw -o jsonpath='{.status.image}'
```

!!!note API reference

For details, see [`PgBouncerSpec`](pg4k.v1.md#pgbouncerspec)
in the API reference.
!!!

## Service Template

Sometimes, your pooler will require some different labels, annotations, or even change
the type of the service, you can achieve that by using the `serviceTemplate` field:
Sometimes, your pooler will require some different labels, annotations, or
even a different Service type. You can achieve that by using the
`serviceTemplate` field:

```yaml
apiVersion: postgresql.k8s.enterprisedb.io/v1
Expand Down Expand Up @@ -668,6 +805,42 @@ spec:
- port: metrics
```

### TLS for the Metrics Endpoint

Set `.spec.monitoring.tls.enabled: true` to serve the metrics endpoint over
HTTPS. By default, the cluster's server certificate is being used.
The certificate is reloaded on every TLS handshake, so rotations are
picked up without restarting the pod.

```yaml
spec:
monitoring:
tls:
enabled: true
```

When `.spec.pgbouncer.clientTLSSecret` is set, the metrics server presents
that certificate instead.

```yaml
spec:
pgbouncer:
clientTLSSecret:
name: <CLIENT_TLS_SECRET>
monitoring:
tls:
enabled: true
```

The generated `PodMonitor` scrapes with `insecureSkipVerify=true` because
Prometheus scrapes pods by IP and the certificate's SANs do not generally
cover the pod IP.

If you need strict verification, set `.spec.monitoring.enablePodMonitor: false`
and manage the `PodMonitor` yourself: the operator-generated one is hardcoded
to `insecureSkipVerify=true` and overwrites its spec on every reconcile, so a
manual patch on the generated `PodMonitor` would not survive.

### Deprecation of Automatic `PodMonitor` Creation

!!!warning Feature Deprecation Notice
Expand Down Expand Up @@ -704,7 +877,7 @@ following example:
## Pausing connections

The `Pooler` specification allows you to take advantage of PgBouncer's `PAUSE`
and `RESUME` commands, using only declarative configuration. You can ado this
and `RESUME` commands, using only declarative configuration. You can do this
using the `paused` option, which by default is set to `false`. When set to
`true`, the operator internally invokes the `PAUSE` command in PgBouncer,
which:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ As a result, the instructions in this section are suitable for both:
- importing one or more databases from an existing PostgreSQL instance, even
outside Kubernetes
- importing the database from any PostgreSQL version to one that is either the
same or newer, enabling *major upgrades* of PostgreSQL (e.g. from version 13.x
to version 17.x)
same or newer, enabling *major upgrades* of PostgreSQL (e.g. from version 14.x
to version 18.x)

!!!warning
When performing major upgrades of PostgreSQL you are responsible for making
Expand Down
Loading