{"id":47664779,"url":"https://github.com/hops-ops/psql-stack","last_synced_at":"2026-05-25T05:01:31.611Z","repository":{"id":340636190,"uuid":"1166191216","full_name":"hops-ops/psql-stack","owner":"hops-ops","description":"Create scalable Postgres clusters and manage their schemas and migrations declaratively.","archived":false,"fork":false,"pushed_at":"2026-05-25T03:04:19.000Z","size":241,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-25T04:26:03.611Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"KCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hops-ops.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-25T01:00:04.000Z","updated_at":"2026-05-25T02:39:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/hops-ops/psql-stack","commit_stats":null,"previous_names":["hops-ops/stack-psql","hops-ops/psql-stack"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/hops-ops/psql-stack","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fpsql-stack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fpsql-stack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fpsql-stack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fpsql-stack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hops-ops","download_url":"https://codeload.github.com/hops-ops/psql-stack/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fpsql-stack/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33461090,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-25T02:24:28.008Z","status":"ssl_error","status_checked_at":"2026-05-25T02:23:23.339Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-02T11:52:32.016Z","updated_at":"2026-05-25T05:01:31.605Z","avatar_url":"https://github.com/hops-ops.png","language":"KCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# psql-stack\n\nPostgreSQL platform layer on top of [CloudNativePG](https://cloudnative-pg.io/). Composes the CNPG operator, the [`cnpg-i-scale-to-zero` plugin](https://github.com/xataio/cnpg-i-scale-to-zero), the [Atlas Operator](https://atlasgo.io/integrations/kubernetes/operator) (declarative schema migrations), a paired `psql` `StorageClass` + `VolumeSnapshotClass` that PSQLClusters and PSQLBranches default to.\n\nThis is the **platform layer** — it does not create any serving Postgres clusters. Per-app DBs live in [`PSQLCluster`](../../psql-cluster/), ephemeral forks in [`PSQLBranch`](../../psql-branch/).\n\n## Design\n\n- **Paired StorageClass + VolumeSnapshotClass, both named `psql`.** Snapshots only work when the snapshotter driver matches the underlying StorageClass provisioner, so the stack composes both with the same CSI driver value. PSQLClusters and PSQLBranches default `spec.storage.class: psql` (XRD default), so consumer manifests don't have to know the driver. Default driver/provisioner is `ebs.csi.eks.amazonaws.com` (EKS Auto Mode); override `storageClass.provisioner` + `snapshotClass.driver` together for non-EKS targets (kind, self-managed Longhorn, etc.).\n- **No NodePool / node-prep.** Components run wherever the cluster's scheduler puts them. Auto Mode handles node provisioning end-to-end.\n\nIf you need replicated CoW storage (true block-level branches with delta-only economics), that's a separate concern — see `aws-storage-stack` for self-managed nodes that can host Longhorn or similar. The default psql-stack stays on the AWS-blessed Auto Mode path.\n\n## Components\n\n| Component | Default | Purpose |\n|---|---|---|\n| **CNPG operator** | always-on | The CNCF Postgres operator. CRDs include `Cluster`, `Backup`, `Pooler`, `ScheduledBackup`. |\n| **cnpg-i-scale-to-zero plugin** | on (`spec.scaleToZeroPlugin.enabled: true`) | Auto-hibernates idle CNPG `Cluster`s. **Requires cert-manager** (provided by [`aws-cert-stack`](../../aws/cert/)). |\n| **Atlas operator** | always-on | Declarative schema migrations via `AtlasMigration` / `AtlasSchema` CRDs. |\n| **StorageClass** | on (`spec.storageClass.enabled: true`) | Named `psql` by default. Provisioner: `ebs.csi.eks.amazonaws.com` with `type: gp3`. PSQLCluster + PSQLBranch reference it as their default `spec.storage.class`. |\n| **VolumeSnapshotClass** | on (`spec.snapshotClass.enabled: true`) | Named `psql` by default. Driver matches `storageClass.provisioner`. PSQLBranch references it for snapshot/fork. |\n| **HA mode** | off (`spec.ha.enabled: false`) | When enabled: 3 replicas + `topologySpreadConstraints` by zone on CNPG, Atlas, S2Z plugin. |\n\n## Prerequisites\n\n- **A working CSI driver** on the cluster matching `storageClass.provisioner`. EKS Auto Mode provides `ebs.csi.eks.amazonaws.com` automatically. For other targets, override `storageClass.provisioner` (and the matching `snapshotClass.driver`) to whatever the target cluster has — e.g. `hostpath.csi.k8s.io` for kind, `driver.longhorn.io` for self-managed Longhorn.\n- **VolumeSnapshot CRDs + snapshot-controller** (`snapshot.storage.k8s.io`). EKS Auto Mode ships the snapshot CRDs but **not** the cluster-wide snapshot-controller. Without one, the composed VolumeSnapshotClass is inert and PSQLBranch snapshots will never reach `ReadyToUse`. Install [`volume-snapshot-stack`](../volume-snapshot/) (also in this org) — it composes the upstream snapshot-controller via the piraeus-charts Helm chart and is the canonical CRD installer for the cluster.\n- **cert-manager** (only when `scaleToZeroPlugin.enabled` — the plugin uses cert-manager Issuer+Certificate for its gRPC TLS). Provided by [`aws-cert-stack`](../../aws/cert/).\n\n## Stages\n\n### Stage 1: Default install\n\nDeploy with all defaults. CNPG + Atlas + S2Z + a `psql` VolumeSnapshotClass for EBS.\n\n```yaml\napiVersion: hops.ops.com.ai/v1alpha1\nkind: PSQLStack\nmetadata:\n  name: psql\n  namespace: default\nspec:\n  clusterName: my-cluster\n```\n\n### Stage 2: Production posture\n\nHA on; per-component value tweaks; team labels for cost allocation.\n\n```yaml\napiVersion: hops.ops.com.ai/v1alpha1\nkind: PSQLStack\nmetadata:\n  name: psql\n  namespace: default\nspec:\n  clusterName: production-cluster\n  namespace: cnpg-system\n  labels:\n    team: platform\n  ha:\n    enabled: true\n    replicas: 3\n    topologySpreadByZone: true\n  atlasOperator:\n    values:\n      prewarmDevDB: true\n```\n\n### Stage 3: Non-EKS cluster\n\nOverride the SC + VSC driver together (they must match for snapshots to work). Example: self-managed cluster running Longhorn.\n\n```yaml\napiVersion: hops.ops.com.ai/v1alpha1\nkind: PSQLStack\nmetadata:\n  name: psql\n  namespace: default\nspec:\n  clusterName: edge\n  helmProviderConfigRef:\n    name: default\n  storageClass:\n    provisioner: driver.longhorn.io\n    parameters:\n      numberOfReplicas: \"3\"\n  snapshotClass:\n    driver: driver.longhorn.io\n```\n\n### Stage 4: Opt out of the composed StorageClass\n\nIf the cluster already ships a suitable default StorageClass, disable composition and have PSQLCluster/PSQLBranch consumers set `spec.storage.class` explicitly.\n\n```yaml\napiVersion: hops.ops.com.ai/v1alpha1\nkind: PSQLStack\nmetadata:\n  name: psql\n  namespace: default\nspec:\n  clusterName: shared-cluster\n  helmProviderConfigRef:\n    name: default\n  storageClass:\n    enabled: false\n```\n\n### Stage 5: Local / no-snapshot cluster\n\nFor dev clusters without a snapshot-controller, disable the VSC composition. PSQLBranch won't function (it needs the VSC), but PSQLCluster still works.\n\n```yaml\napiVersion: hops.ops.com.ai/v1alpha1\nkind: PSQLStack\nmetadata:\n  name: psql\n  namespace: default\nspec:\n  clusterName: local\n  helmProviderConfigRef:\n    name: default\n  snapshotClass:\n    enabled: false\n  scaleToZeroPlugin:\n    enabled: false\n```\n\n## Spec Reference\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `clusterName` | string | _required_ | Target cluster name; default for `helmProviderConfigRef.name`, `kubernetesProviderConfigRef.name`, and label values |\n| `namespace` | string | `cnpg-system` | Shared namespace for CNPG, S2Z plugin, and Atlas |\n| `labels` | object | — | Custom labels merged with stack defaults |\n| `managementPolicies` | string[] | `[\"*\"]` | Crossplane management policies |\n| `helmProviderConfigRef.name` | string | `clusterName` | Helm ProviderConfig name |\n| `helmProviderConfigRef.kind` | enum | `ProviderConfig` | `ProviderConfig` or `ClusterProviderConfig` |\n| `kubernetesProviderConfigRef.name` | string | `clusterName` | Kubernetes ProviderConfig name |\n| `kubernetesProviderConfigRef.kind` | enum | `ProviderConfig` | Same as above |\n| **HA mode** | | | |\n| `ha.enabled` | bool | `false` | Stack-wide HA toggle |\n| `ha.replicas` | int | `3` | Replica count for HA-able platform components |\n| `ha.topologySpreadByZone` | bool | `true` | Add `topologySpreadConstraint` with `topologyKey=topology.kubernetes.io/zone`, `maxSkew=1`, `whenUnsatisfiable=ScheduleAnyway` |\n| **CNPG operator** | | | |\n| `cnpg.name` | string | `cloudnative-pg` | Helm release name |\n| `cnpg.chartVersion` | string | `0.27.1` | CNPG Helm chart version (tracks CNPG 1.29.x) |\n| `cnpg.values` | object | — | Helm values merged with chart defaults |\n| `cnpg.overrideAllValues` | object | — | Helm values that replace all defaults |\n| **Scale-to-zero plugin** | | | |\n| `scaleToZeroPlugin.enabled` | bool | `true` | Install the plugin (zero-cost when no `Cluster` opts in) |\n| `scaleToZeroPlugin.version` | string | `v0.1.7` | Plugin release tag |\n| `scaleToZeroPlugin.namespace` | string | shared `namespace` | Override |\n| **Atlas operator** | | | |\n| `atlasOperator.name` | string | `atlas-operator` | Helm release name |\n| `atlasOperator.namespace` | string | shared `namespace` | Override |\n| `atlasOperator.values` | object | — | Helm values merged with chart defaults |\n| `atlasOperator.overrideAllValues` | object | — | Helm values that replace all defaults |\n| **Storage class** | | | |\n| `storageClass.enabled` | bool | `true` | Compose the StorageClass |\n| `storageClass.name` | string | `psql` | StorageClass name (PSQLCluster + PSQLBranch reference this) |\n| `storageClass.provisioner` | string | `ebs.csi.eks.amazonaws.com` | CSI driver name. Must match `snapshotClass.driver` |\n| `storageClass.reclaimPolicy` | enum | `Delete` | `Delete` or `Retain` |\n| `storageClass.volumeBindingMode` | enum | `WaitForFirstConsumer` | `Immediate` or `WaitForFirstConsumer` |\n| `storageClass.allowVolumeExpansion` | bool | `true` | Online PVC expansion (CNPG resizes via the same field on its `Cluster` CR) |\n| `storageClass.parameters` | object | `{type: gp3}` | Provisioner-specific parameters |\n| **Snapshot class** | | | |\n| `snapshotClass.enabled` | bool | `true` | Compose the VolumeSnapshotClass |\n| `snapshotClass.name` | string | `psql` | VolumeSnapshotClass name (PSQLBranch references this) |\n| `snapshotClass.driver` | string | `ebs.csi.eks.amazonaws.com` | CSI driver. Must match `storageClass.provisioner` |\n| `snapshotClass.deletionPolicy` | enum | `Delete` | `Delete` or `Retain` |\n| `snapshotClass.parameters` | object | — | Driver-specific parameters |\n\n## Composed Resources\n\n| Resource | Kind | When |\n|---|---|---|\n| `cloudnative-pg` | `helm.m.crossplane.io/Release` | always |\n| `atlas-operator` | `helm.m.crossplane.io/Release` | always |\n| 9× `\u003cname\u003e-s2z-*` | `kubernetes.m.crossplane.io/Object` | `scaleToZeroPlugin.enabled: true` |\n| `\u003cname\u003e-storageclass` | `kubernetes.m.crossplane.io/Object` | `storageClass.enabled: true` |\n| `\u003cname\u003e-volumesnapshotclass` | `kubernetes.m.crossplane.io/Object` | `snapshotClass.enabled: true` |\n| Various `Usage` | `protection.crossplane.io/Usage` | when both ends Ready (deletion ordering) |\n\n## Dependencies\n\n| Kind | Package | Version |\n|---|---|---|\n| Function | `crossplane-contrib/function-auto-ready` | `\u003e=v0.6.2` |\n| Provider | `crossplane-contrib/provider-helm` | `\u003e=v1` |\n| Provider | `crossplane-contrib/provider-kubernetes` | `\u003e=v1` |\n\n## Development\n\n```bash\nmake render             # Render all examples\nmake validate           # Validate against Crossplane schemas\nmake test               # KCL render tests (assert composed resource shapes)\nmake build              # Build the package\nmake render:standard    # Render a single example\n```\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhops-ops%2Fpsql-stack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhops-ops%2Fpsql-stack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhops-ops%2Fpsql-stack/lists"}