Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Running with Kubernetes

A manifest for deploying PostgSail on Kubernetes is available in the kubernetes/ directory of the repository. In addition to the application deployments, the manifest includes Services for connecting to each component and Persistent Volume Claims for retaining data between restarts.

Prerequisites

  • kubectl CLI installed and configured for a running cluster
  • The repository cloned locally: git clone https://github.com/xbgmsharp/postgsail
  • A configured .env file (copy .env.example and edit)

Note

Most PostgSail images are not available in a public registry and must be built locally then imported into your cluster. Only api (PostgREST) and app (Grafana) use official upstream images.

Building and importing images

Build all custom images:

docker compose build

Then import them into your cluster. For MicroK8s:

docker save xbgmsharp/postgsail-db:latest     | microk8s ctr images import -
docker save xbgmsharp/postgsail-migrate:latest | microk8s ctr images import -
docker save xbgmsharp/postgsail-web:latest     | microk8s ctr images import -
docker save xbgmsharp/postgsail-telegram-bot:latest | microk8s ctr images import -

For other clusters (e.g. kind):

kind load docker-image xbgmsharp/postgsail-db:latest
kind load docker-image xbgmsharp/postgsail-migrate:latest
kind load docker-image xbgmsharp/postgsail-web:latest
kind load docker-image xbgmsharp/postgsail-telegram-bot:latest

Verify the images are available:

microk8s ctr images ls | grep postgs

Quick Start

Create the namespace:

kubectl create namespace postgsail --dry-run=client -o yaml | kubectl apply -f -

Create secrets from your .env file:

kubectl -n postgsail create secret generic postgsail-secrets \
  --from-env-file=.env \
  --dry-run=client -o yaml | kubectl apply -f -

Apply the manifest:

kubectl apply -f kubernetes/postgsail-deployment.yaml

Note

The cluster must have a Storage Class enabled, which many Kubernetes implementations do by default.

If you find a pod stuck in a pending state, it may be due to no available storage class. See the Settings and Data Storage section below.

Reaching the Server

Load Balancer

The deployment manifest includes a Service resource for attachment to an external load balancer. Once the cluster has a load balancer with an available IP address, your PostgSail instance becomes externally reachable. Find the assigned IP with:

kubectl describe service postgsail -n postgsail

It may take some time for your load balancer to assign addresses.

MicroK8s

An easy-to-configure load balancer is available in MicroK8s:

microk8s enable metallb

Specify a range of local IPs to dedicate to it (default ranges will often work).

Ingress

For local or single-node clusters, an Ingress resource is the simplest way to access the server without setting up a load balancer.

minikube

This command sets up a tunnel into the postgsail service and attempts to open it in a browser:

minikube service postgsail

Depending on how minikube is deployed, you may need to leave the resulting CLI process running to keep the tunnel available.

MicroK8s

Enable the nginx ingress controller (binds to host port 80):

microk8s enable ingress

Then deploy the ingress resource:

kubectl apply -f kubernetes/postgsail-ingress.yaml

The PostgSail web UI is then available at http://localhost.

Resource Architecture

graph TD
    A[Web Browsers] -- HTTPS :443 --> C
    B[API Clients] -- HTTPS :443 --> C
    C(["Ingress\n(nginx / cloud LB)"])

    C -- "/* HTTP :80" --> D
    C -- "/api/* HTTP :3000" --> E

    subgraph services [Kubernetes Services ClusterIP]
        D["Service: postgsail-web :80"]
        E["Service: postgsail-api :3000"]
        K["Service: postgsail-db :5432"]
    end

    subgraph pods [Kubernetes Pods]
        G["Pod: postgsail-web\nnginx · Vue 3 Frontend"]
        H["Pod: postgsail-api\nPostgREST"]
        F["Pod: postgsail-db\nPostgreSQL + TimescaleDB"]
    end

    D --> G
    E --> H
    H -- "SQL :5432" --> K
    K --> F
    F -- mounts --> J

    subgraph storage [Persistent Storage]
        J["PersistentVolumeClaim: postgres-data (10Gi)"]
        I[("PersistentVolumeClaim: grafana-data (5Gi)")]
    end

Settings and Data Storage

The deployment manifest uses Persistent Volumes to retain data across restarts:

  • postgres-data (10Gi) — PostgreSQL database
  • grafana-data (5Gi) — Grafana dashboards and configuration

Many Kubernetes implementations include a default storage class. If not, pods will remain in a pending state.

MicroK8s

Enable the hostpath storage addon:

microk8s enable hostpath-storage

Then re-apply the deployment manifest.

Checking Deployment Status

# List all pods
kubectl -n postgsail get pods

# Check migration job logs
kubectl -n postgsail logs job/migrate

# Check API logs
kubectl -n postgsail logs deployment/api

# List images (MicroK8s)
microk8s ctr images ls | grep postgs

Uninstall

Remove all resources added to the cluster:

kubectl delete -f kubernetes/postgsail-deployment.yaml

MicroK8s

Disable ingress and storage addons if needed:

microk8s disable ingress
microk8s disable storage