Databases
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
| Option | Description | Example |
|---|---|---|
instances | Number of PostgreSQL replicas. Use 1 for development, 2 or 3 for high availability in production. | 2 |
size | Persistent volume size for each instance. | 10Gi |
version | PostgreSQL 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.
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.