Skip to content

How to Create a Postgres Database#

Learn how to provision a PostgreSQL database in your project.

Jordan, a backend engineer at ACME Corp, needs a managed PostgreSQL database for her application. She can provision one, scale it for high availability, and connect to it from a workload in minutes.

Prerequisites#

  • A Project already created
  • The postgres.database.cloud.stakater.com API available
  • kubectl configured with your project kubeconfig
  • To read the credentials: access to your organisation's Vault from a device enrolled on the Mesh

What Gets Created#

When you create a Postgres claim, the platform provisions:

  • A PostgreSQL database sized to your requested number of instances and disk
  • Credentials in your organisation's Vault, at the path published in the claim's status
  • Optionally, an externally-reachable endpoint when exposeLoadBalancer: true

Step 1: Define a Postgres Claim#

Create a file named db.yaml:

apiVersion: database.cloud.stakater.com/v1
kind: Postgres
metadata:
  name: my-db
spec:
  parameters: {}

Optional Parameters#

Parameter Default Description
parameters.instances 1 Number of PostgreSQL instances (replicas). Use 3 for HA.
parameters.storage.size 10Gi Disk size per instance
parameters.exposeLoadBalancer false When true, the database is reachable from outside the project

Step 2: Apply the Claim#

kubectl apply -f db.yaml

Step 3: Verify the Database#

Check the claim status:

kubectl get postgres my-db

Wait for it to become ready:

NAME    SYNCED   READY   AGE
my-db   True     True    2m

Inspect the endpoint metadata and the credentials location:

kubectl get postgres my-db -o jsonpath='{.status.connection}'
{"host":"my-db-rw.projects.svc.cluster.local","port":"5432","database":"my-db","credentialsRef":{"vault":"https://bao.acme.mesh.example.stakater.cloud","mount":"services","path":"my-project/postgres/my-db"}}

Step 4: Read the Credentials#

The platform stores the credentials in your organisation's Vault at status.connection.credentialsRef. Open the Vault URL in a browser (from a Mesh-enrolled device) and navigate to the services secret engine, or use the CLI:

export BAO_ADDR=$(kubectl get postgres my-db -o jsonpath='{.status.connection.credentialsRef.vault}')
bao login -method=oidc
bao kv get -mount=services $(kubectl get postgres my-db -o jsonpath='{.status.connection.credentialsRef.path}')

Keys cover the common client conventions:

Key Description
username / user Application user (both keys carry the same value)
password Password for the application user
host Read-write service hostname
port Service port
dbname Database name
uri PostgreSQL connection URI
jdbc-uri JDBC URL
fqdn-uri, fqdn-jdbc-uri URI / JDBC URL with the host's fully-qualified cluster DNS name
pgpass A line for ~/.pgpass

Mount the credentials into a workload#

Create a Secret next to your workload from the Vault values, then reference it with envFrom:

bao kv get -mount=services -format=json my-project/postgres/my-db \
  | jq -r '.data.data' > creds.json
kubectl create secret generic my-db --from-env-file=<(jq -r 'to_entries[] | "\(.key)=\(.value)"' creds.json)
rm creds.json
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: app
          image: my-app:latest
          envFrom:
            - secretRef:
                name: my-db

The container will see DATABASE_URL-equivalent values through the standard keys (host, port, username, password, dbname, uri).

Test from your workstation#

For ad-hoc testing — psql from your laptop, a local migration runner, a BI tool — set parameters.exposeLoadBalancer: true on the claim. The platform provisions an externally-routable endpoint and surfaces its hostname / IP under status.connection.externalHost:

apiVersion: database.cloud.stakater.com/v1
kind: Postgres
metadata:
  name: my-db
spec:
  parameters:
    exposeLoadBalancer: true

Connect using the credentials from the Vault — no port-forward needed:

PG_HOST=$(kubectl get postgres my-db -o jsonpath='{.status.connection.externalHost}')
CREDS=$(bao kv get -mount=services -format=json my-project/postgres/my-db | jq -r '.data.data')
export PGUSER=$(echo "$CREDS" | jq -r '.username')
export PGPASSWORD=$(echo "$CREDS" | jq -r '.password')
export PGDATABASE=$(echo "$CREDS" | jq -r '.dbname')

psql -h "$PG_HOST" -p 5432 -c "SELECT version();"

If you left exposeLoadBalancer off, the database is only reachable from inside the project — a one-off connection from your laptop is still possible by port-forwarding:

kubectl port-forward svc/my-db-rw 5432:5432 &
psql -h 127.0.0.1 -p 5432 -c "SELECT version();"

Step 5: Scale the Database (Optional)#

High availability with 3 instances#

apiVersion: database.cloud.stakater.com/v1
kind: Postgres
metadata:
  name: app-db
spec:
  parameters:
    instances: 3
    storage:
      size: 50Gi

Step 6: Delete the Database#

kubectl delete postgres my-db

The platform tears down the database and removes the credentials from your organisation's Vault. If you copied the credentials into Secrets next to your workloads, remember to delete those too:

kubectl delete secret my-db

Warning

All data is deleted when the claim is removed. Back up any database you need to keep before deletion.

What's Next?#