{"id":44989487,"url":"https://github.com/pascalinthecloud/terraform-module-cnpg-database","last_synced_at":"2026-02-18T21:03:23.613Z","repository":{"id":332446482,"uuid":"1133787940","full_name":"pascalinthecloud/terraform-module-cnpg-database","owner":"pascalinthecloud","description":"Terraform module for declarative PostgreSQL database creation in CloudNative-PG with automatic user and secret management.","archived":false,"fork":false,"pushed_at":"2026-02-05T20:58:46.000Z","size":59,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-06T01:04:29.157Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pascalinthecloud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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-01-13T20:23:23.000Z","updated_at":"2026-02-05T20:58:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pascalinthecloud/terraform-module-cnpg-database","commit_stats":null,"previous_names":["pascalinthecloud/terraform-module-cnpg-database"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/pascalinthecloud/terraform-module-cnpg-database","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pascalinthecloud%2Fterraform-module-cnpg-database","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pascalinthecloud%2Fterraform-module-cnpg-database/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pascalinthecloud%2Fterraform-module-cnpg-database/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pascalinthecloud%2Fterraform-module-cnpg-database/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pascalinthecloud","download_url":"https://codeload.github.com/pascalinthecloud/terraform-module-cnpg-database/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pascalinthecloud%2Fterraform-module-cnpg-database/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29596127,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-18T20:59:56.587Z","status":"ssl_error","status_checked_at":"2026-02-18T20:58:41.434Z","response_time":162,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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-02-18T21:03:22.947Z","updated_at":"2026-02-18T21:03:23.600Z","avatar_url":"https://github.com/pascalinthecloud.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Terraform Module: CloudNative-PG Database\n\nTerraform module for creating PostgreSQL databases in CloudNative-PG clusters with automatic user and secret management.\n\n## Features\n\n- 🔐 Automatic password secret creation with hot-reload support\n- 📦 Database and user provisioned declaratively\n- 🔌 Connection details secret for easy application integration\n- 🏷️ Customizable labels for all resources\n- ✅ Input validation for security best practices\n- 💾 S3-based backup configuration with CloudNativePG/Barman\n- 📅 Automated scheduled backups with retention policies\n- 🔄 Point-in-Time Recovery (PITR) support\n\n## Prerequisites\n\n- CloudNative-PG operator installed in your Kubernetes cluster\n\n## Usage\n\n### Basic Example\n\n```hcl\nmodule \"my_app_database\" {\n  source = \"github.com/pascalinthecloud/terraform-module-cnpg-database\"\n\n  databases = [{\n    name     = \"my-app\"\n    owner    = \"my_app_user\"\n    password = var.database_password # Use a secure variable or secret manager\n  }]\n\n  cluster = {\n    name      = \"shared-postgres-prod\"\n    namespace = \"databases-prod\"\n  }\n}\n```\n\n### Complete Example with Labels\n\n```hcl\nmodule \"my_app_database\" {\n  source = \"github.com/pascalinthecloud/terraform-module-cnpg-database\"\n\n  databases = [{\n    name     = \"my-app\"\n    owner    = \"my_app_user\"\n    password = var.database_password\n  }]\n\n  cluster = {\n    name      = \"shared-postgres-prod\"\n    namespace = \"databases-prod\"\n  }\n\n  labels = {\n    app         = \"my-app\"\n    environment = \"production\"\n    managed-by  = \"terraform\"\n  }\n}\n```\n\n### Custom PostgreSQL Database Name\n\n```hcl\nmodule \"my_app_database\" {\n  source = \"github.com/pascalinthecloud/terraform-module-cnpg-database\"\n\n  databases = [{\n    name             = \"my-app\"\n    pg_database_name = \"myapp_production\" # Custom PostgreSQL database name\n    owner            = \"my_app_user\"\n    password         = var.database_password\n  }]\n\n  cluster = {\n    name      = \"shared-postgres-prod\"\n    namespace = \"databases-prod\"\n  }\n}\n```\n\n### Connection Secret in Different Namespace\n\n```hcl\nmodule \"my_app_database\" {\n  source = \"github.com/pascalinthecloud/terraform-module-cnpg-database\"\n\n  databases = [{\n    name                        = \"my-app\"\n    owner                       = \"my_app_user\"\n    password                    = var.database_password\n    connection_secret_namespace = \"my-app-namespace\" # Deploy connection secret to app namespace\n  }]\n\n  cluster = {\n    name      = \"shared-postgres-prod\"\n    namespace = \"databases-prod\"\n  }\n}\n```\n\n### With S3 Backups Enabled\n\n```hcl\nmodule \"my_app_database\" {\n  source = \"github.com/pascalinthecloud/terraform-module-cnpg-database\"\n\n  databases = [{\n    name     = \"my-app\"\n    owner    = \"my_app_user\"\n    password = var.database_password\n  }]\n\n  cluster = {\n    name      = \"shared-postgres-prod\"\n    namespace = \"databases-prod\"\n  }\n\n  # Enable S3 backups\n  backup = {\n    enabled              = true\n    s3_endpoint_url      = \"https://s3.amazonaws.com\"  # Or your S3-compatible endpoint\n    s3_bucket_name       = \"my-postgres-backups\"\n    s3_access_key_id     = var.s3_access_key_id\n    s3_secret_access_key = var.s3_secret_access_key\n    retention_policy     = \"30d\"\n    schedule             = \"0 2 * * *\"  # Daily at 2 AM UTC\n    target               = \"prefer-standby\"\n    wal_compression      = \"gzip\"\n    data_compression     = \"gzip\"\n  }\n\n  labels = {\n    app         = \"my-app\"\n    environment = \"production\"\n  }\n}\n```\n\n\u003c!-- BEGIN_TF_DOCS --\u003e\n## Requirements\n\n| Name | Version |\n|------|---------|\n| \u003ca name=\"requirement_terraform\"\u003e\u003c/a\u003e [terraform](#requirement\\_terraform) | \u003e= 1.9.0 |\n| \u003ca name=\"requirement_kubernetes\"\u003e\u003c/a\u003e [kubernetes](#requirement\\_kubernetes) | \u003e= 2.0 |\n\n## Providers\n\n| Name | Version |\n|------|---------|\n| \u003ca name=\"provider_kubernetes\"\u003e\u003c/a\u003e [kubernetes](#provider\\_kubernetes) | \u003e= 2.0 |\n\n## Modules\n\nNo modules.\n\n## Resources\n\n| Name | Type |\n|------|------|\n| [kubernetes_manifest.cluster](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource |\n| [kubernetes_manifest.database](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource |\n| [kubernetes_manifest.scheduled_backup](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource |\n| [kubernetes_role_binding_v1.backup_secret_reader](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role_binding_v1) | resource |\n| [kubernetes_role_v1.backup_secret_reader](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role_v1) | resource |\n| [kubernetes_secret_v1.backup_credentials](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret_v1) | resource |\n| [kubernetes_secret_v1.connection](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret_v1) | resource |\n| [kubernetes_secret_v1.database_password](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret_v1) | resource |\n\n## Inputs\n\n| Name | Description | Type | Default | Required |\n|------|-------------|------|---------|:--------:|\n| \u003ca name=\"input_backup\"\u003e\u003c/a\u003e [backup](#input\\_backup) | Backup configuration for S3-based backups using Barman | \u003cpre\u003eobject({\u003cbr/\u003e    enabled                 = optional(bool, false)\u003cbr/\u003e    s3_endpoint_url         = optional(string, \"\")\u003cbr/\u003e    s3_bucket_name          = optional(string, \"\")\u003cbr/\u003e    s3_access_key_id        = optional(string, \"\")\u003cbr/\u003e    s3_secret_access_key    = optional(string, \"\")\u003cbr/\u003e    retention_policy        = optional(string, \"30d\")\u003cbr/\u003e    schedule                = optional(string, \"0 2 * * *\")\u003cbr/\u003e    wal_compression         = optional(string, \"gzip\")\u003cbr/\u003e    data_compression        = optional(string, \"gzip\")\u003cbr/\u003e    jobs                    = optional(number, 2)\u003cbr/\u003e    target                  = optional(string, \"prefer-standby\")\u003cbr/\u003e    create_scheduled_backup = optional(bool, true)\u003cbr/\u003e    immediate               = optional(bool, false)\u003cbr/\u003e  })\u003c/pre\u003e | `{}` | no |\n| \u003ca name=\"input_cluster\"\u003e\u003c/a\u003e [cluster](#input\\_cluster) | CloudNative-PG cluster configuration object | \u003cpre\u003eobject({\u003cbr/\u003e    name                                    = optional(string, \"default-cluster\")\u003cbr/\u003e    namespace                               = optional(string, \"default\")\u003cbr/\u003e    instances                               = optional(number, 1)\u003cbr/\u003e    storage_class                           = optional(string, \"longhorn\") # Override with your cluster's available storage class\u003cbr/\u003e    storage_size                            = optional(string, \"10Gi\")\u003cbr/\u003e    inherited_labels                        = optional(map(string), {})\u003cbr/\u003e    inherited_annotations                   = optional(map(string), {})\u003cbr/\u003e    postgresql_max_connections              = optional(string, \"100\")\u003cbr/\u003e    postgresql_shared_buffers               = optional(string, \"256MB\")\u003cbr/\u003e    postgresql_effective_cache_size         = optional(string, \"1GB\")\u003cbr/\u003e    postgresql_maintenance_work_mem         = optional(string, \"64MB\")\u003cbr/\u003e    postgresql_checkpoint_completion_target = optional(string, \"0.9\")\u003cbr/\u003e    postgresql_wal_buffers                  = optional(string, \"16MB\")\u003cbr/\u003e    postgresql_default_statistics_target    = optional(string, \"100\")\u003cbr/\u003e    postgresql_random_page_cost             = optional(string, \"1.1\")\u003cbr/\u003e    postgresql_effective_io_concurrency     = optional(string, \"200\")\u003cbr/\u003e    postgresql_work_mem                     = optional(string, \"2621kB\")\u003cbr/\u003e    postgresql_min_wal_size                 = optional(string, \"512MB\")\u003cbr/\u003e    postgresql_max_wal_size                 = optional(string, \"2GB\")\u003cbr/\u003e    bootstrap_database                      = optional(string, \"postgres\")\u003cbr/\u003e    bootstrap_owner                         = optional(string, \"postgres\")\u003cbr/\u003e    enable_pod_monitor                      = optional(bool, true)\u003cbr/\u003e    resources = optional(object({\u003cbr/\u003e      requests = optional(object({\u003cbr/\u003e        memory = optional(string, \"512Mi\")\u003cbr/\u003e        cpu    = optional(string, \"250m\")\u003cbr/\u003e      }), {})\u003cbr/\u003e      limits = optional(object({\u003cbr/\u003e        memory = optional(string)\u003cbr/\u003e        cpu    = optional(string)\u003cbr/\u003e      }), null)\u003cbr/\u003e    }), {})\u003cbr/\u003e  })\u003c/pre\u003e | `{}` | no |\n| \u003ca name=\"input_databases\"\u003e\u003c/a\u003e [databases](#input\\_databases) | List of databases to create. Each object must have name, owner, password, and database\\_reclaim\\_policy.\u003cbr/\u003eIf the list is empty, the cluster will be created with no managed database users.\u003cbr/\u003eUsers can manually add roles to the cluster or add databases through this module later. | \u003cpre\u003elist(object({\u003cbr/\u003e    name                        = string\u003cbr/\u003e    owner                       = string\u003cbr/\u003e    password                    = string\u003cbr/\u003e    database_reclaim_policy     = optional(string, \"retain\")\u003cbr/\u003e    pg_database_name            = optional(string, \"\")\u003cbr/\u003e    create_connection_secret    = optional(bool, true)\u003cbr/\u003e    connection_secret_namespace = optional(string, \"\")\u003cbr/\u003e  }))\u003c/pre\u003e | `[]` | no |\n| \u003ca name=\"input_labels\"\u003e\u003c/a\u003e [labels](#input\\_labels) | Additional labels to add to all resources | `map(string)` | `{}` | no |\n\n## Outputs\n\n| Name | Description |\n|------|-------------|\n| \u003ca name=\"output_backup_destination_path\"\u003e\u003c/a\u003e [backup\\_destination\\_path](#output\\_backup\\_destination\\_path) | S3 destination path for backups |\n| \u003ca name=\"output_backup_enabled\"\u003e\u003c/a\u003e [backup\\_enabled](#output\\_backup\\_enabled) | Whether backups are configured for this cluster |\n| \u003ca name=\"output_backup_secret_name\"\u003e\u003c/a\u003e [backup\\_secret\\_name](#output\\_backup\\_secret\\_name) | Name of the Kubernetes secret containing backup credentials |\n| \u003ca name=\"output_connection_host\"\u003e\u003c/a\u003e [connection\\_host](#output\\_connection\\_host) | Database connection hostname |\n| \u003ca name=\"output_connection_port\"\u003e\u003c/a\u003e [connection\\_port](#output\\_connection\\_port) | Database connection port |\n| \u003ca name=\"output_connection_secret_names\"\u003e\u003c/a\u003e [connection\\_secret\\_names](#output\\_connection\\_secret\\_names) | Names of the Kubernetes secrets containing connection details |\n| \u003ca name=\"output_connection_uris\"\u003e\u003c/a\u003e [connection\\_uris](#output\\_connection\\_uris) | Full PostgreSQL connection URIs for each database |\n| \u003ca name=\"output_database_names\"\u003e\u003c/a\u003e [database\\_names](#output\\_database\\_names) | Names of the created databases in PostgreSQL |\n| \u003ca name=\"output_owner_usernames\"\u003e\u003c/a\u003e [owner\\_usernames](#output\\_owner\\_usernames) | Usernames of the database owners |\n| \u003ca name=\"output_password_secret_names\"\u003e\u003c/a\u003e [password\\_secret\\_names](#output\\_password\\_secret\\_names) | Names of the Kubernetes secrets containing the database passwords |\n| \u003ca name=\"output_scheduled_backup_name\"\u003e\u003c/a\u003e [scheduled\\_backup\\_name](#output\\_scheduled\\_backup\\_name) | Name of the ScheduledBackup resource |\n\u003c!-- END_TF_DOCS --\u003e\n\n## Backup Configuration\n\nThis module supports S3-based backups using CloudNativePG's Barman integration.\n\n### Prerequisites for Backups\n\n1. **S3-Compatible Storage**: AWS S3, MinIO, or other S3-compatible storage\n2. **S3 Bucket**: Pre-created bucket for storing backups\n3. **S3 Credentials**: Access key ID and secret access key\n\n### Backup Features\n\n- **Continuous WAL Archiving**: Write-Ahead Logs continuously archived to S3\n- **Scheduled Backups**: Automated backups based on cron schedule\n- **Retention Policies**: Automatic cleanup of old backups\n- **Compression**: Configurable compression (gzip, bzip2, snappy)\n- **PITR**: Point-in-Time Recovery support\n\n### Backup Configuration\n\nThe `backup` object accepts the following properties:\n\n| Property | Description | Default | Required |\n|----------|-------------|---------|----------|\n| `enabled` | Enable S3 backups | `false` | No |\n| `s3_endpoint_url` | S3 endpoint URL (empty for AWS S3) | `\"\"` | When enabled |\n| `s3_bucket_name` | S3 bucket name | `\"\"` | When enabled |\n| `s3_access_key_id` | S3 access key ID | `\"\"` | When enabled |\n| `s3_secret_access_key` | S3 secret access key | `\"\"` | When enabled |\n| `retention_policy` | Retention policy (e.g., \"30d\") | `\"30d\"` | No |\n| `schedule` | Cron schedule | `\"0 2 * * *\"` | No |\n| `wal_compression` | WAL compression algorithm | `\"gzip\"` | No |\n| `data_compression` | Data compression algorithm | `\"gzip\"` | No |\n| `jobs` | Parallel backup jobs | `2` | No |\n| `target` | Backup target instance | `\"prefer-standby\"` | No |\n| `create_scheduled_backup` | Create ScheduledBackup resource | `true` | No |\n| `immediate` | Take immediate backup on creation | `false` | No |\n\n### What Gets Created for Backups\n\nWhen `backup.enabled = true`, the module creates:\n\n1. **Kubernetes Secret**: Stores S3 credentials securely\n2. **RBAC Role**: Allows cluster service account to read backup credentials\n3. **RBAC RoleBinding**: Binds the role to the cluster's service account\n4. **Cluster Backup Config**: Configures Barman object store in cluster spec\n5. **ScheduledBackup Resource**: Creates automated backup schedule (if `create_scheduled_backup = true`)\n\n### Backup Schedule Examples\n\n```hcl\n# Daily at 2 AM UTC\nbackup = {\n  schedule = \"0 2 * * *\"\n}\n\n# Every 6 hours\nbackup = {\n  schedule = \"0 */6 * * *\"\n}\n\n# Weekly on Sunday at 3 AM\nbackup = {\n  schedule = \"0 3 * * 0\"\n}\n\n# Monthly on the 1st at 1 AM\nbackup = {\n  schedule = \"0 1 1 * *\"\n}\n```\n\n### Monitoring Backups\n\nCheck backup status:\n\n```bash\n# List all backups\nkubectl get backups -n \u003cnamespace\u003e\n\n# Check scheduled backup\nkubectl get scheduledbackup -n \u003cnamespace\u003e\n\n# Describe backup details\nkubectl describe backup \u003cbackup-name\u003e -n \u003cnamespace\u003e\n\n# View cluster backup status\nkubectl describe cluster \u003ccluster-name\u003e -n \u003cnamespace\u003e\n```\n\n### Point-in-Time Recovery (PITR)\n\nTo restore from backups, create a new cluster with recovery configuration. See the [CloudNativePG documentation](https://cloudnative-pg.io/documentation/current/recovery/) for details.\n\n## Connection Details Secret\n\nWhen `create_connection_secret = true` (default), the module creates a secret with the following keys:\n\n- `host` - Database hostname\n- `port` - Database port (5432)\n- `database` - Database name\n- `username` - Database username\n- `password` - Database password\n- `uri` - Full connection URI\n\nExample usage in a pod:\n\n```yaml\nenv:\n  - name: DATABASE_HOST\n    valueFrom:\n      secretKeyRef:\n        name: my-app-db-connection\n        key: host\n  - name: DATABASE_URL\n    valueFrom:\n      secretKeyRef:\n        name: my-app-db-connection\n        key: uri\n```\n\n## How It Works\n\n1. **Password Secret**: Creates a `kubernetes.io/basic-auth` secret with the `cnpg.io/reload` label for hot password updates\n2. **Database CRD**: Creates a CloudNative-PG Database resource that triggers database creation\n3. **Connection Secret**: Optionally creates a connection details secret for application use\n\nThe CloudNative-PG operator reconciles the managed role in the cluster and creates the database with the specified owner.\n\n## Security Best Practices\n\n- Always use a secure method to provide passwords (e.g., Terraform variables with encryption, secret managers)\n- Never commit passwords to version control\n- Use strong passwords (minimum 8 characters enforced by validation)\n- Consider using Kubernetes RBAC to restrict access to password secrets\n\n## License\n\nMIT\n\n## Author\n\nPascal Toepke\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpascalinthecloud%2Fterraform-module-cnpg-database","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpascalinthecloud%2Fterraform-module-cnpg-database","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpascalinthecloud%2Fterraform-module-cnpg-database/lists"}