Automated PostgreSQL cluster deployment on K3s using Terraform with CloudNativePG operator and PgBouncer connection pooling.
- K3s Kubernetes cluster installed and running on target server
- SSH access to the target server with sudo privileges
- GitHub account with access to container registries (ghcr.io)
- PostgreSQL cluster using CloudNativePG operator
- PgBouncer connection pooler
- NodePort services for external database access
- High availability with configurable replicas
- Persistent storage
Copy and edit the configuration file:
cp terraform.tfvars.example terraform.tfvarsEdit terraform.tfvars:
github = {
username = "your-github-username"
token = "ghp_your_github_personal_access_token"
}
server = {
ssh_server = "192.168.1.100"
ssh_port = 22
ssh_username = "ubuntu"
ssh_password = ""
ssh_private_key_path = "~/.ssh/id_rsa"
}
postgres = {
username = "postgres"
password = "secure_password_here"
replication_username = "replicator"
replication_password = "replication_password"
}GitHub: Username and personal access token for container registry access
Server: SSH connection details (IP, port, username, key path)
PostgreSQL: Database credentials (username, password, replication credentials)
PgBouncer (optional): Pool mode (transaction/session/statement), max connections, pool size
Storage (optional): Size (default: 8Gi), storage class (default: local-path)
High Availability (optional): Enable HA (default: true), replica count (default: 2)
Data Preservation (optional): Preserve data on destroy (default: true), preserve PVCs (default: true)
See terraform.tfvars.example for all available options.
# Initialize Terraform
terraform init
# Preview changes
terraform plan
# Deploy PostgreSQL cluster
terraform apply
# View connection info
terraform output
# Destroy (respects preservation settings)
terraform destroyExternal access from DBMS tools (DBeaver, pgAdmin, etc.):
- PgBouncer (recommended):
<server-ip>:30500 - PostgreSQL Direct:
<server-ip>:30501 - PostgreSQL Alternative:
<server-ip>:30502
Using psql:
psql -h <server-ip> -p 30500 -U postgres -d postgresConnection string:
postgresql://postgres:your_password@<server-ip>:30500/postgresDBMS tools (DBeaver/pgAdmin/DataGrip):
- Host: Your server IP
- Port: 30500
- Database: postgres
- Username: postgres
- Password: Your configured password
# Via PgBouncer
pgbouncer-postgres.postgres.svc.cluster.local:5432
# Direct PostgreSQL
postgresql-postgres.postgres.svc.cluster.local:5432- System prerequisites (Git, curl, Docker)
- CloudNativePG operator
- PostgreSQL cluster in
postgresnamespace - PgBouncer connection pooler
- NodePort services for external access
- GitHub container registry credentials
When enabled, deploys PostgreSQL with primary + replica instances (default: 3 total). Automatic failover managed by CloudNativePG operator.
By default, all data is preserved when running terraform destroy. To permanently delete data, set these to false in terraform.tfvars:
preserve_postgres_data_on_destroy = false
preserve_postgres_pvcs = falseInstall K3s first:
curl -sfL https://get.k3s.io | sh -Monitor cluster status:
kubectl get cluster postgresql-postgres -n postgres
kubectl get pods -n postgresCheck PgBouncer status:
kubectl get pods -n postgres -l app.kubernetes.io/name=pgbouncer
kubectl logs -n postgres -l app.kubernetes.io/name=pgbouncerCheck services and firewall:
kubectl get svc -n postgres
sudo ufw allow 30500/tcp- Change default passwords in production
- Never commit
terraform.tfvarsto version control - Use SSH key-based authentication
- Configure firewall rules to restrict NodePort access
- Use PgBouncer for connection pooling
- main.tf - Main Terraform configuration
- variables.tf - Variable definitions
- postgresql-cluster.yaml - PostgreSQL cluster template
- pgbouncer-manifests.yaml - PgBouncer deployment
- ingress.yaml - NodePort services
- terraform.tfvars.example - Example configuration