Connecting GKE to Cloud SQL and Cloud Storage with Terraform

Table of Contents
Introduction
Part 1 covered VPC + GKE with Terraform modules and Terragrunt live config: remote state, subnet secondary ranges, and a small node pool. This post adds the data plane most small platforms need next: Cloud SQL (PostgreSQL or MySQL) and Cloud Storage (GCS) buckets for object data, uploads, or static assets.
Read the foundation first: Provisioning a Small GKE Cluster with Terraform and Terragrunt.
The GKE storage docs explain when to use Persistent Disks, Filestore, or object storage in Kubernetes. Here we focus on provisioning SQL and buckets in GCP and hooking them up to workloads running on GKE.
Stack Layout: DB and Bucket Alongside GKE
In the same repo style as part 1, you typically have Terragrunt children:
environments/<env>/vpc/environments/<env>/gke/environments/<env>/db/environments/<env>/bucket/
Cloud SQL must attach to a VPC for private IP access, so db/terragrunt.hcl almost always dependencys the VPC stack and passes network_id and the VPC self link into the module.
A GCS application bucket may not need VPC outputs if the module only creates the bucket and IAM; still, keep it in the same environment tree so inputs (project, region, naming suffix) stay consistent.
Cloud SQL: Private IP and Service Networking
Why private IP
Exposing Cloud SQL on a public IPv4 address simplifies demos but is a security liability for production. A common Terraform pattern sets:
ip_configuration {
ipv4_enabled = false
private_network = var.vpc_self_link
}
That forces traffic over private networking inside your VPC (clients still need a path from GKE nodes or a proxy).
Service networking connection
Private Cloud SQL on GCP uses VPC peering to the Google-managed service producer network. Terraform usually creates:
- A
google_compute_global_addresswithpurpose = "VPC_PEERING"and an internal prefix (e.g. /24) - A
google_service_networking_connectiontoservicenetworking.googleapis.comusing that reserved range
Without this, private IP instances fail to allocate addresses correctly.
Instance and databases
The module may define:
google_sql_database_instanceper environment key (or a single instance)backup_configuration, optional PITR, disk size, tier, availability_type (zonal vs regional)- Primary and extra databases/users via
for_eachmaps
Passwords are often generated with random_password and lifecycle { ignore_changes = [password] } after the first apply so Terraform does not rotate secrets on every run. Store the value in Secret Manager or your secret store; avoid committing secrets to Git.
Connecting from GKE
From pods, typical options:
- Cloud SQL Auth Proxy or Cloud SQL connector libraries (recommended for IAM DB auth and TLS)
- Direct private IP if network routing and firewall rules allow node → SQL traffic
Hardening topics (Workload Identity to IAM DB auth, least-privilege service accounts) belong with GKE security — implement them explicitly; a minimal Terraform repo may only create the instance and network, not the full auth story.
GCS: Buckets and IAM for Workloads
Bucket resource
A small application bucket module might:
- Name buckets with environment + suffix (globally unique)
- Set location (e.g. multi-region or EU)
- Enable uniform bucket-level access
- Turn on versioning when you need rollback or auditability
Service account and binding
One pattern creates a dedicated storage service account and grants it roles/storage.objectViewer (read-only) on that bucket via google_storage_bucket_iam_binding. Write-heavy apps need objectCreator / objectAdmin or fine-grained IAM Conditions — adjust to least privilege.
Getting credentials into the cluster
At this layer Terraform often stops at GCP IAM. Inside Kubernetes you still choose how the pod authenticates:
- Workload Identity (bind a Kubernetes SA to a Google SA) — preferred on GKE; see GKE security
- Mounted JSON key — works but harder to rotate and easier to leak
If your Terraform repo only creates the Google SA and bucket IAM, plan a follow-up (Helm/Kustomize, Config Connector, or a small manifest repo) to wire Workload Identity or Workload Identity Federation patterns.
Terragrunt: DB Depends on VPC
The db stack mirrors gke: include parent config, depend on VPC, map outputs to module variables:
terraform {
source = "../../../modules/db"
}
include {
path = find_in_parent_folders()
}
dependency "vpc" {
config_path = "../vpc"
mock_outputs = {
network_id = "network-00000001"
vpc_self_link = "https://www.googleapis.com/compute/v1/projects/my-project/global/networks/my-vpc"
}
}
inputs = {
network_id = dependency.vpc.outputs.network_id
vpc_self_link = dependency.vpc.outputs.vpc_self_link
}
Shared inputs (project, region, db_envs maps, etc.) flow from the environment root the same way as for GKE.
When to Use Cloud SQL / GCS vs Kubernetes Volumes
| Need | Typical choice |
|---|---|
| Relational transactional data | Cloud SQL (or AlloyDB for larger Postgres) |
| Large objects, static assets, user uploads | GCS |
| Block storage for a database inside the cluster | Persistent Disk + StatefulSet — see GKE storage |
| Shared file semantics (NFS-like) | Filestore or a CSI driver |
Avoid running production Postgres only on a single PD unless you accept operational risk; managed SQL trades cost for backups, HA, and patching.
Apply Order
Within one environment:
- VPC (subnets + secondary ranges)
- Cloud SQL module (depends on VPC for private IP + peering)
- GKE (depends on VPC)
- Bucket (can run in parallel with GKE once project inputs exist)
Use terragrunt run-all apply from the env folder so dependency order is respected.
Cross-Linking for SEO and Readers
- Foundation: Provisioning a Small GKE Cluster with Terraform and Terragrunt
- Deep dives: GKE cluster setup, GKE networking, GKE storage, GKE security
Summary
- Cloud SQL in this pattern uses private IP + service networking; the
dbTerragrunt unit depends onvpc. - GCS modules provision buckets and IAM; pod auth should move toward Workload Identity, not long-lived keys.
- Keep Terraform focused on GCP resources; Kubernetes manifests and GitOps layers sit above this base.
Together with part 1, you get a small but coherent GCP stack: network, cluster, SQL, and object storage—ready to layer applications and platform tooling on top.