Wire GCS backup to neuron-db-backup-prod bucket (neuron-494301)

Bucket created, SA key stored in Vault at secret/gcs.
CronJob ExternalSecret updated to pull from secret/gcs.
Hourly restic backup now runs to both R2 and GCS.
This commit is contained in:
Will Anderson
2026-04-25 17:51:57 -05:00
parent 8d97bbd802
commit 7eeff54a11
2 changed files with 10 additions and 69 deletions
+7 -66
View File
@@ -1,70 +1,11 @@
# Google Cloud Storage — neuron backup bucket + dedicated service account
# Paired with: k8s/neuron-technologies/prod/backup-cronjob.yaml (restic → GCS)
# k8s/backup/cronjob.yaml (restic → GCS for Gitea/Postgres)
# Google Cloud Storage — neuron backup bucket (neuron-494301 / neurontechnologies)
# Bucket: gs://neuron-db-backup-prod (created 2026-04-25, US multi-region)
# SA: neuron-agent@neuron-494301.iam.gserviceaccount.com (Storage Admin)
# Key: stored in Vault at secret/gcs.neuron_backup_sa_key
# CronJob: k8s/neuron-technologies/prod/backup-cronjob.yaml
variable "gcp_project_id" {
description = "Google Cloud project ID"
description = "Google Cloud project ID for neurontechnologies"
type = string
default = "neuron-785695"
}
provider "google" {
project = var.gcp_project_id
credentials = fileexists("${path.module}/vault-gcp-sa.json") ? file("${path.module}/vault-gcp-sa.json") : null
}
# ── Neuron prod DB backup bucket ──────────────────────────────────────────────
resource "google_storage_bucket" "neuron_backup" {
name = "neuron-backup-${var.gcp_project_id}"
location = "US"
force_destroy = false
uniform_bucket_level_access = true
versioning {
enabled = false
}
lifecycle_rule {
condition { age = 90 }
action { type = "Delete" }
}
labels = {
managed-by = "terraform"
service = "neuron"
purpose = "backup"
tier = "prod"
}
}
# ── Dedicated service account for backup writes ───────────────────────────────
resource "google_service_account" "neuron_backup" {
account_id = "neuron-backup"
display_name = "Neuron Backup"
description = "Used by k8s restic CronJobs to write hourly backups to GCS"
project = var.gcp_project_id
}
resource "google_storage_bucket_iam_member" "neuron_backup_writer" {
bucket = google_storage_bucket.neuron_backup.name
role = "roles/storage.objectAdmin"
member = "serviceAccount:${google_service_account.neuron_backup.email}"
}
resource "google_service_account_key" "neuron_backup" {
service_account_id = google_service_account.neuron_backup.name
# After first apply: copy the base64 key into Vault:
# vault kv put secret/data/gcs neuron_backup_sa_key="$(terraform output -raw neuron_backup_sa_key_base64 | base64 -d)"
}
output "neuron_backup_gcs_bucket" {
value = google_storage_bucket.neuron_backup.name
description = "GCS bucket name for use in RESTIC_REPOSITORY_GCS"
}
output "neuron_backup_sa_key_base64" {
value = google_service_account_key.neuron_backup.private_key
sensitive = true
description = "Base64-encoded SA key JSON — store in Vault: secret/data/gcs.neuron_backup_sa_key"
default = "neuron-494301"
}
@@ -23,8 +23,8 @@ spec:
# ── GCS (secondary) ──
RESTIC_PASSWORD_GCS: "{{ .restic_password_gcs }}"
GCS_SA_KEY_JSON: "{{ .gcs_neuron_backup_sa_key }}"
GOOGLE_PROJECT_ID: "neuron-785695"
RESTIC_REPOSITORY_GCS: "gs:neuron-backup-neuron-785695:/"
GOOGLE_PROJECT_ID: "neuron-494301"
RESTIC_REPOSITORY_GCS: "gs:neuron-db-backup-prod:/"
data:
- secretKey: restic_password
remoteRef:
@@ -59,7 +59,7 @@ spec:
schedule: "0 * * * *"
concurrencyPolicy: Forbid
failedJobsHistoryLimit: 3
successfulJobsHisticsLimit: 3
successfulJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2