pgAgroal Enterprise · Tutorial
Install with Helm
Install the Kubernetes operator, create your first Pgagroal resource, and confirm the managed pooler pod is running and hardened.
The pgagroal-enterprise Helm chart installs the Elevarq pgAgroal Enterprise control layer: the Pgagroal custom resource definition (CRD), the Kubernetes operator (RBAC plus a hardened deployment), and an optional fleet control plane. The chart deploys the operator — you create poolers by applying Pgagroal resources, which the operator reconciles into standard, unmodified upstream pgagroal pods. For background, see the pgAgroal Enterprise overview.
Prerequisites
- A Kubernetes cluster and
kubectlconfigured against it, with permission to create CRDs, ClusterRoles, and namespaces. helmv3 installed locally.- A reachable PostgreSQL backend (a Service hostname or external endpoint) for the pooler to front.
- A released operator image version to pin — production installs should pin by tag or, ideally, by digest.
Get the chart
The chart is published to an OCI Helm registry and is cosign-signed on release, so you install it directly with helm— no Git checkout is required. The CRD ships inside the chart’s crds/ directory and installs automatically with the release; you do not apply it separately.
charts/pgagroal-enterprise — substitute that local path for the oci://… reference in the commands below.Choose your image registry
The operator and pooler images come from a different registry depending on how you obtained the product, because AWS Marketplace entitlement resolves images through a Marketplace-managed registry rather than the public one. The registry is a Helm value, so the same chart works either way:
- Direct / open users —
ghcr.io/elevarq. These Enterprise images and the chart are private: you authenticate to GHCR with a GitHub token that hasread:packages, and the pods pull them with an image pull secret (next section). - AWS Marketplace customers — the Marketplace-managed ECR path tied to your subscription:
TODO(frank): exact Marketplace ECR registry string.
The examples below use the direct GHCR registry. Marketplace customers replace the registry portion of operator.image.repository and the pooler image with their entitled ECR path (and authenticate to ECR instead of GHCR).
Authenticate to the registry
The chart and images are private, so log in to GHCR for Helm and create a pull secret the pods use to pull the images. Use a GitHub token (classic, scope read:packages; or a fine-grained token with package read) as the password:
# Helm (pulls the chart)
helm registry login ghcr.io -u <github-username> -p <token>
# Pull secret in the install namespace (the pods use it to pull images)
kubectl create namespace pgagroal-system
kubectl create secret docker-registry ghcr-pull \
--docker-server=ghcr.io \
--docker-username=<github-username> \
--docker-password=<token> \
--namespace pgagroal-systemghcr-pull here) is referenced from the Helm install (imagePullSecrets) and from the Pgagroal resource (spec.imagePullSecrets) below. Marketplace customers create an ECR pull secret instead.Install the operator
Install the published chart into a dedicated namespace, pinning the operator image to a released version. This command needs only helm and a kubeconfig — no repository checkout:
helm install pgagroal-enterprise \
oci://ghcr.io/elevarq/charts/pgagroal-enterprise --version <released-version> \
--namespace pgagroal-system \
--set operator.image.repository=ghcr.io/elevarq/pgagroal-enterprise-operator \
--set operator.image.tag=<released-version> \
--set 'imagePullSecrets[0].name=ghcr-pull'<released-version> with the latest release tag without the leading v (for example 1.0.0-beta.5) — see the releases page. The single-quotes around the imagePullSecrets[0].name set are needed so zsh does not glob the brackets.This deploys the operator (one replica by default), creates its ServiceAccount, and installs the ClusterRole and binding it needs to reconcile Pgagroal resources cluster-wide. Pin a released tag — or, ideally, a digest — in production rather than tracking a moving tag.
values.yaml. The chart is built for that pattern.Pgagroal CRD from crds/ on first install, but Helm does not upgrade CRDs on helm upgrade — so a later chart can add fields the live CRD schema still rejects. After upgrading the chart, apply the new CRD explicitly:helm pull oci://ghcr.io/elevarq/charts/pgagroal-enterprise \
--version <released-version> --untar -d /tmp/pgae
kubectl apply -f /tmp/pgae/pgagroal-enterprise/crds/pgagroals.yamlGitOps tools should manage the CRD with a replace/apply strategy (Argo CD Replace=true, Flux force) for the same reason.Optional: the fleet control plane
The chart can also deploy the fleet control plane alongside the operator. It is off by default because it needs a PostgreSQL DSN and operator tokens, which you supply through existing Secrets. Enable it only when you are ready to wire those in:
helm install pgagroal-enterprise \
oci://ghcr.io/elevarq/charts/pgagroal-enterprise --version <released-version> \
--namespace pgagroal-system \
--set operator.image.repository=ghcr.io/elevarq/pgagroal-enterprise-operator \
--set operator.image.tag=<released-version> \
--set 'imagePullSecrets[0].name=ghcr-pull' \
--set controlPlane.enabled=trueReference the control plane’s DSN and token Secrets via controlPlane.env. For the full list of tunables, see the Helm values reference.
Create a Pgagroal resource
With the operator running, create a pooler by applying a Pgagroal custom resource. Point backendHost at your PostgreSQL Service:
apiVersion: pgagroal.elevarq.com/v1alpha1
kind: Pgagroal
metadata:
name: pg1
spec:
image: ghcr.io/elevarq/pgagroal-enterprise:<released-version>
backendHost: my-postgres.default.svc.cluster.local
replicas: 3
# the managed pooler pods pull the private image with this secret
imagePullSecrets:
- ghcr-pullimage: uses the same registry choice as the operator. Marketplace customers set it to their entitled ECR path (see Choose your image registry).Save this as pg1.yaml and apply it:
kubectl apply -f pg1.yamlThe operator reconciles the resource into a managed pooler deployment. It hardens every managed pod — non-root, dropped capabilities, seccomp, and resource limits — and creates a PodDisruptionBudget plus an ingress NetworkPolicy.
Verify the pooler is running and hardened
Confirm the Pgagroal resource and the pods the operator created for it:
# The custom resource the operator is reconciling
kubectl get pgagroal pg1
# The managed pooler pods
kubectl get pods -l app.kubernetes.io/instance=pg1Check that the operator applied the hardened pod settings. The managed pods run with these defaults:
- Non-root —
runAsNonRoot: true - No privilege escalation —
allowPrivilegeEscalation: false - All capabilities dropped —
capabilities.drop: [ALL] - Read-only root filesystem —
readOnlyRootFilesystem: true - Seccomp —
RuntimeDefaultprofile
Inspect a managed pod’s security context to confirm:
kubectl get pod -l app.kubernetes.io/instance=pg1 \
-o jsonpath='{.items[0].spec.securityContext}{"\n"}'Connect through the pooler and run a query
Now see the payoff: send real traffic. Applications connect to the operator-created Service named after the resource (pg1) on the pooler port 6432 — not to PostgreSQL on 5432 directly. The in-cluster connection target is pg1.<namespace>.svc.cluster.local:6432; the pooler forwards to the backend you set in backendHost.
Run a one-shot psql pod in the same namespace as pg1 and execute a trivial query (use a database role that exists on your backend):
kubectl run psql-check --rm -it --restart=Never \
--image=postgres:17-alpine \
--env="PGPASSWORD=<password>" -- \
psql "host=pg1 port=6432 user=<user> dbname=<db>" -c 'SELECT version();'On success you see the backend’s version row, returned through the pooler:
version
--------------------------------------------------------------------------------
PostgreSQL 17.4 on x86_64-pc-linux-gnu, compiled by gcc ...
(1 row)
pod "psql-check" deletedbackendHost, or credentials the backend rejects. Look at the managed pod’s logs, which name the backend it tried to reach:kubectl logs -l app.kubernetes.io/instance=pg1 --tail=50Verify the chart supply chain
The chart is helm lint and helm template validated in CI and cosign-signed on release. Verify the signature with cosign verify before installing in environments where provenance matters — this supports your audit and compliance readiness.
Next steps
To manage Pgagroal resources declaratively from a Git repository, follow the GitOps tutorial. For the full set of chart values, see the Helm values reference. For pgagroal’s own configuration and operation, see the pgagroal manual.