We use cookies to help us improve gimlet.ioLearn moreOK

PostgreSQL

On this page you can learn

  • how to set up a centralized PostgreSQL instance in Gimlet that your applications can share
  • and how to provision a logical database for your applicaton inside this shared PostgreSQL instance

Reasons to centralize

Having a centralized PostgreSQL that your applications share means more constraints typically, but you can also delegate the operational responsibilities to the maintainer of the shared instance.

In a small company that maintainer may also be you, but using a centralized instance instead of multiple small ones will ease your maintenance burden.

Operational responsibilities that you can optimize by using a shared instance:

  • backups
  • monitoring, alerting, availability
  • cost

Running a database is an operations heavy task. Providing high-availability and performing backups reliably on a regular cadence is something you should not take lightly. If you can outsource it to your cloud provider, we recommend to consider it.

Use an Existing PostgreSQL

Gimlet allows you to specify the details of an existing PostgreSQL instance. This can be a cloud managed instance or something that you manage outside of Gimlet.

Working with existing PostgreSQL instances are the same as working with a containerized PostgreSQL instances running on Kubernetes. Without the maintenance part naturally. Jump to provision the application credentials.

Deploy a Containerized PostgreSQL on Kubernetes

In this section you will deploy a PostgreSQL version 16 instance that will serve as a centralized instance that your applications can use.

You put these manifests to the gitops-<environment-name>-infra repository by convention.

The manifests configure PostgreSQL in a standalone configuration without replicas.

# helm-repositories/bitnami.yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
  name: bitnami
  namespace: infrastructure
spec:
  interval: 60m
  url: https://charts.bitnami.com/bitnami
# helm-releases/postgresql.yaml
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: redis
  namespace: infrastructure
spec:
  interval: 60m
  releaseName: postgresql
  chart:
    spec:
      chart: postgresql
      version: 15.5.31
      sourceRef:
        kind: HelmRepository
        name: bitnami
      interval: 10m
  values:
    auth:
      existingSecret: postgresql-credentials
    architecture: standalone
    tls:
      enabled: true
      autoGenerated: true
    persistence:
      enabled: true
      size: 8Gi
    persistentVolumeClaimRetentionPolicy:
      enabled: true
      whenScaled: Retain
      whenDeleted: Retain
# manifests/postgresql-credentials.yaml
---
apiVersion: v1
kind: Secret
metadata:
  name: postgresql-credentials
  namespace: infrastructure
type: Opaque
stringData:
  postgres-password: super-secret-password

Verify PostgreSQL

$ kubectl exec -it postgresql-0 -- bash
Defaulted container "postgresql" out of: postgresql, copy-certs (init)
$ I have no name!@postgresql-0:/$ psql -U postgres
Password for user postgres: 
psql (16.4)
Type "help" for help.

postgres=#

Provision Application Credentials

Your application needs a dedicated schema and credentials.

To provision the logical databse with the required credentials, you are going to add manifests to your application's Gimlet manifest.

  • You can find an introduction to the Gimlet Manifest here,
  • and this is the extension point we are using.
  • We are also going to use Helm charts to install Redis. Here is an intro to them. TODO

Terraform workflow to provision logical database with credentials

The bellow addition to your application's Gimlet manifest uses a Terraform module to provision a database and user in the centralized PostgreSQL instance.

Upon deployment, the Terraform module is executed and the database is provisioned in a couple of minutes. Your application will eventually settle in running state.

# .gimlet/staging.yaml
app: myapp
env: staging
namespace: my-team
chart:
  repository: https://chart.onechart.dev
  name: onechart
  version: 0.32.0
values:
  replicas: 1
  image:
    repository: myapp
    tag: 1.1.0
  ingress:
    host: myapp.staging.mycompany.com
    tlsEnabled: true
+dependencies:
+- name: myapp-postgresql
+  namespace: infrastructure
+  kind: terraform
+  spec:
+    module:
+      url: https://github.com/gimlet-io/tfmodules?sha=df0316bfc4d5163edce1ffcf0ed0d667cc263bd0&path=civo/logical-database
+    values:
+      database: myapp
+      user: myapp
+      host: postgresql.infrastructure.svc.cluster.local
+    sealedSecrets:
+      password: <sealed-password-value>
+    existing-secret: postgresql-credentials

Verify PostgreSQL with pgAdmin

The last step is to set the PostgreSQL connection parameters in your application.

The best practice is to use environment variables to configure your application. You can set them in the deployment configuration, and you can also use secrets.

  • Connection URL: postgresql.infrastructure.svc.cluster.local
  • Password: the one you set earlier

Backups

Add the following configuration to helm-releases/postgresql.yaml to enable daily backups of PostgreSQL.

...
spec:
  interval: 60m
  releaseName: postgresql
  chart:
    spec:
      chart: postgresql
      version: 15.5.31
      sourceRef:
        kind: HelmRepository
        name: bitnami
      interval: 10mpo
  values:
+   backup:
+     enabled: true
+     storage:
+       size: 20Gi   
...

This backup will be placed to a different volume than your PostgreSQL data volume. So you are protected from

  • data cossuption due to appl misbehavior
  • and data loss due to PostgreSQL data volume failure.

You may want to ship this backup to an offsite location. Join our Discord if you need help with it.