In the last post we’ve created a very simple three node PostgreSQL cluster using CloudNativePG and this was really easy to do. While this is great to get started quickly, you usually want to have more control on how PostgreSQL is initialized and configured. In this post we’ll look at bootstrapping the cluster and what options you have for controlling it, when you want to start with an empty cluster.

What we got in the last post was mostly a default configuration. Lets start from scratch by deleting the existing cluster:

minicube@micro-minicube:~> kubectl get cluster -A
NAMESPACE   NAME              AGE   INSTANCES   READY   STATUS                     PRIMARY
default     cluster-example   26h   3           3       Cluster in healthy state   cluster-example-1
minicube@micro-minicube:~> kubectl delete cluster cluster-example
cluster.postgresql.cnpg.io "cluster-example" deleted
minicube@micro-minicube:~> kubectl get cluster -A
No resources found
minicube@micro-minicube:~> kubectl get pods
No resources found in default namespace.
minicube@micro-minicube:~>

Instead of using the minimal configuration from the last post, we’ll now use this one to describe our cluster:

minicube@micro-minicube:~> cat pg.yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: my-pg-cluster
spec:
  instances: 3

  bootstrap:
    initdb:
      database: db1
      owner: db1
      dataChecksums: true
      walSegmentSize: 32
  storage:
    size: 1Gi

This is what changed (all under the “initdb” section):

  • The name of the cluster
  • We want a new database “db1” to be created and this database should be owned by a new user called “db1”
  • We want data checksum to be enabled
  • Finally we want to change the WAL segment size from the default of 16MB to 32MB

Once we apply this, we of course get the same picture as before for the pods and the services:

minicube@micro-minicube:~> kubectl apply -f pg.yaml
cluster.postgresql.cnpg.io/my-pg-cluster created
minicube@micro-minicube:~> kubectl get pods
NAME              READY   STATUS    RESTARTS   AGE
my-pg-cluster-1   1/1     Running   0          119s
my-pg-cluster-2   1/1     Running   0          97s
my-pg-cluster-3   1/1     Running   0          78s

Lets take one of these pods, start a shell and check what was created:

minicube@micro-minicube:~> kubectl exec my-pg-cluster-2 -i -t -- /bin/bash
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
postgres@my-pg-cluster-2:/$ psql
psql (16.2 (Debian 16.2-1.pgdg110+2))
Type "help" for help.

postgres=# \l
                                                  List of databases
   Name    |  Owner   | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules |   Access privileges
-----------+----------+----------+-----------------+---------+-------+------------+-----------+-----------------------
 db1       | db1      | UTF8     | libc            | C       | C     |            |           |
 postgres  | postgres | UTF8     | libc            | C       | C     |            |           |
 template0 | postgres | UTF8     | libc            | C       | C     |            |           | =c/postgres          +
           |          |          |                 |         |       |            |           | postgres=CTc/postgres
 template1 | postgres | UTF8     | libc            | C       | C     |            |           | =c/postgres          +
           |          |          |                 |         |       |            |           | postgres=CTc/postgres
(4 rows)

postgres=#

We got the database and the database is owned by the new user. What we also can see is, the we got “C” for the “Collate” and “Ctype”. This is probably not what you want, so you also need to change this in the initdb section of the cluster definition:

minicube@micro-minicube:~> cat pg.yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: my-pg-cluster
spec:
  instances: 3

  bootstrap:
    initdb:
      database: db1
      owner: db1
      dataChecksums: true
      walSegmentSize: 32
      localeCollate: 'en_US.utf8'
      localeCType: 'en_US.utf8'
  storage:
    size: 1Gi

Destroy, re-create and check again:

minicube@micro-minicube:~> kubectl delete -f pg.yaml
cluster.postgresql.cnpg.io "my-pg-cluster" deleted
minicube@micro-minicube:~> kubectl apply -f pg.yaml
cluster.postgresql.cnpg.io/my-pg-cluster created
minicube@micro-minicube:~> kubectl get pods
NAME              READY   STATUS    RESTARTS   AGE
my-pg-cluster-1   1/1     Running   0          55m
my-pg-cluster-2   1/1     Running   0          54m
my-pg-cluster-3   1/1     Running   0          54m
minicube@micro-minicube:~> kubectl exec my-pg-cluster-1 -i -t -- /bin/bash
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
postgres@my-pg-cluster-1:/$ psql
psql (16.2 (Debian 16.2-1.pgdg110+2))
Type "help" for help.

postgres=# \l
                                                      List of databases
   Name    |  Owner   | Encoding | Locale Provider |  Collate   |   Ctype    | ICU Locale | ICU Rules |   Access privileges   
-----------+----------+----------+-----------------+------------+------------+------------+-----------+-----------------------
 db1       | db1      | UTF8     | libc            | en_US.utf8 | en_US.utf8 |            |           | 
 postgres  | postgres | UTF8     | libc            | en_US.utf8 | en_US.utf8 |            |           | 
 template0 | postgres | UTF8     | libc            | en_US.utf8 | en_US.utf8 |            |           | =c/postgres          +
           |          |          |                 |            |            |            |           | postgres=CTc/postgres
 template1 | postgres | UTF8     | libc            | en_US.utf8 | en_US.utf8 |            |           | =c/postgres          +
           |          |          |                 |            |            |            |           | postgres=CTc/postgres
(4 rows)

postgres=# 

Now we are fine and the size of the WAL segments is as well as we wanted to have it:

postgres=# show wal_segment_size;
 wal_segment_size 
------------------
 32MB
(1 row)

Data checksum are enabled:

postgres=# \! pg_controldata | grep checksum
Data page checksum version:           1

We could have changed the encoding, but as the default anyway is “UTF8”, there is no reason to touch this usually.

Another option you have, is to execute SQL commands or whole scripts (via ConfigMaps). A simple example is this one:

minicube@micro-minicube:~> cat pg.yaml 
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: my-pg-cluster
spec:
  instances: 3

  bootstrap:
    initdb:
      database: db1
      owner: db1
      dataChecksums: true
      walSegmentSize: 32
      localeCollate: 'en_US.utf8'
      localeCType: 'en_US.utf8'
      postInitSQL:
      - create user db2
      - create database db2 with owner = db2
  storage:
    size: 1Gi

minicube@micro-minicube:~> kubectl delete -f pg.yaml 
cluster.postgresql.cnpg.io "my-pg-cluster" deleted
minicube@micro-minicube:~> kubectl apply -f pg.yaml
cluster.postgresql.cnpg.io/my-pg-cluster created
minicube@micro-minicube:~> kubectl exec my-pg-cluster-1 -i -t -- /bin/bash
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
postgres@my-pg-cluster-1:/$ psql
psql (16.2 (Debian 16.2-1.pgdg110+2))
Type "help" for help.

postgres=# \l
                                                      List of databases
   Name    |  Owner   | Encoding | Locale Provider |  Collate   |   Ctype    | ICU Locale | ICU Rules |   Access privileges   
-----------+----------+----------+-----------------+------------+------------+------------+-----------+-----------------------
 db1       | db1      | UTF8     | libc            | en_US.utf8 | en_US.utf8 |            |           | 
 db2       | db2      | UTF8     | libc            | en_US.utf8 | en_US.utf8 |            |           | 
 postgres  | postgres | UTF8     | libc            | en_US.utf8 | en_US.utf8 |            |           | 
 template0 | postgres | UTF8     | libc            | en_US.utf8 | en_US.utf8 |            |           | =c/postgres          +
           |          |          |                 |            |            |            |           | postgres=CTc/postgres
 template1 | postgres | UTF8     | libc            | en_US.utf8 | en_US.utf8 |            |           | =c/postgres          +
           |          |          |                 |            |            |            |           | postgres=CTc/postgres
(5 rows)

postgres=# 

All this is documented, of course. In the next post we’ll look at how we can configure PostgreSQL, because bootstrapping is one task, but configuring PostgreSQL for your needs is another task you definitely should look at.