Infrastructure

Databases

Provision PostgreSQL databases with CloudNativePG

Lucity provisions PostgreSQL databases using CloudNativePG (CNPG), the leading Kubernetes operator for PostgreSQL and a CNCF project. Each database is a CNPG Cluster custom resource: production-grade, with automated failover, replication, and point-in-time recovery built in.

No managed database service, no external dependency, no surprise bills. Just PostgreSQL running where your application runs.

Add a PostgreSQL Database

Create a database through the GraphQL API:

mutation {
  createDatabase(input: {
    projectId: "myapp"
    name: "main"
    version: "16"
    instances: 1
    size: "10Gi"
  }) {
    name
    version
    instances
    size
  }
}

Lucity creates a CNPG Cluster with the specified configuration. The operator handles the rest: initialization, user creation, connection pooling, health checks. Under the hood, this adds a database definition to databases.postgres in your GitOps repo's values.yaml.

Configuration Options

OptionDescriptionExample
instancesNumber of PostgreSQL replicas. Use 1 for development, 2 or 3 for high availability in production.2
sizePersistent volume size for each instance.10Gi
versionPostgreSQL major version."16"

For production environments, you'll typically override instances in your environment-specific values:

# environments/production/values.yaml
databases:
  postgres:
    main:
      instances: 3
      size: 50Gi

Connect From Your Application

CNPG automatically creates a Kubernetes Secret containing your database credentials. The secret includes the full connection URI, individual fields (host, port, user, password, database name), and a pgpass file.

Your application reads the connection string from environment variables. In your service's config, reference the secret:

DATABASE_URL=postgresql://user:password@main-rw.myapp-development.svc.cluster.local:5432/app

The namespace follows the {project}-{environment} convention, so adjust for your target environment.

The -rw suffix routes to the primary instance (read-write). CNPG also creates -ro (read-only replicas) and -r (any replica) services for read scaling.

What Happens on Eject

You get a standard CNPG Cluster manifest. It works with any CNPG operator installation. No Lucity-specific configuration, no proprietary annotations. Install the CNPG operator on your cluster, apply the manifest, and your database runs exactly as it did before.

The ejected manifest describes the database, not the archive it was backed up to. That bucket belongs to the platform. Point the CNPG barman-cloud plugin at object storage of your own and you get the same continuous backups on your own cluster. See the CNPG documentation.

Backups

Every database is backed up, without you doing anything. A full base backup runs weekly, and every write in between is archived continuously as it happens. The two together mean recovery is not limited to the moments a backup ran: you can restore to any point in the last 30 days, down to the second.

Open the Backups tab on a database to see the window you can reach, when the last backup ran, and the base backups behind it.

Restoring

Pick a moment, name a new database, and Lucity brings up a second database holding your data exactly as it was then. The original never stops serving traffic.

Restoring alongside rather than in place is deliberate, and it is what PostgreSQL recovery does natively. It also means a restore is never a second outage: nothing is overwritten, so if you picked the wrong moment you simply restore again. Once you can see the recovered data you decide what to do with it, whether that is copying a few rows back, pointing your service's DATABASE_URL at the new database, or dropping it.

The restored database is an ordinary Lucity database, with the same compute, disk and replicas as the one it came from, and backed up from the moment it starts.