Skip to content
This repository was archived by the owner on Jun 15, 2026. It is now read-only.

Commit 3b103a2

Browse files
authored
feat: add scheduled backups (#48)
* feat: add scheduled backups * Rename Kubernetes cron job resource for tasknote DB
1 parent c4cd5cc commit 3b103a2

3 files changed

Lines changed: 161 additions & 0 deletions

File tree

.github/workflows/cd-main.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ jobs:
109109
-var="db_name=${{ secrets.DB_NAME }}" \
110110
-var="security_key=${{ secrets.JWT_SECURITY_KEY }}" \
111111
-var="mailgun_apikey=${{ secrets.MAILGUN_API_KEY }}" \
112+
-var="r2_access_key=${{ secrets.R2_ACCESS_KEY_ID }}" \
113+
-var="r2_secret_key=${{ secrets.R2_SECRET_ACCESS_KEY }}" \
112114
-var="backend_image=${{ steps.deploy-vars.outputs.backend_image }}" \
113115
-var="frontend_image=${{ steps.deploy-vars.outputs.frontend_image }}"
114116
terraform show -json tfplan > tfplan.json

terraform/.terraform.lock.hcl

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

terraform/main.tf

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,26 @@ variable "mailgun_apikey" {
4848
sensitive = true
4949
}
5050

51+
variable "r2_access_key" {
52+
type = string
53+
sensitive = true
54+
}
55+
56+
variable "r2_secret_key" {
57+
type = string
58+
sensitive = true
59+
}
60+
61+
variable "r2_bucket_name" {
62+
type = string
63+
default = "tasknote-backups"
64+
}
65+
66+
variable "r2_endpoint" {
67+
type = string
68+
default = "https://d17eb09b6bce2f90e16e800bb2a6baf9.r2.cloudflarestorage.com"
69+
}
70+
5171
variable "cors_allowed_origins" {
5272
type = string
5373
default = "https://tasknote.darkroasted.vps-kinghost.net"
@@ -368,3 +388,120 @@ resource "kubernetes_ingress_v1" "tasknote_ingress" {
368388
}
369389
}
370390
}
391+
392+
resource "kubernetes_secret_v1" "r2_backup_secrets" {
393+
metadata {
394+
name = "r2-backup-secrets"
395+
namespace = kubernetes_namespace_v1.tasknote.metadata[0].name
396+
}
397+
398+
data = {
399+
access_key = var.r2_access_key
400+
secret_key = var.r2_secret_key
401+
}
402+
}
403+
404+
resource "kubernetes_cron_job_v1" "tasknote_db_backup" {
405+
metadata {
406+
name = "tasknote-db-backup"
407+
namespace = kubernetes_namespace_v1.tasknote.metadata[0].name
408+
}
409+
spec {
410+
schedule = "0 0,12 * * *"
411+
job_template {
412+
metadata {
413+
labels = {
414+
app = "taknote-db-backup"
415+
}
416+
}
417+
spec {
418+
template {
419+
metadata {
420+
labels = {
421+
app = "tasknote-db-backup"
422+
}
423+
}
424+
spec {
425+
container {
426+
name = "backup"
427+
image = "postgres:15.8-alpine"
428+
command = ["/bin/sh", "-c"]
429+
args = [
430+
<<-EOT
431+
apk add --no-cache aws-cli
432+
export PGPASSWORD=$POSTGRES_PASSWORD
433+
FILENAME="backup-$(date +%Y%m%d%H%M%S).sql.gz"
434+
echo "Starting backup of $POSTGRES_DB to $FILENAME..."
435+
pg_dump -h $DB_HOST -U $POSTGRES_USER $POSTGRES_DB | gzip > /tmp/$FILENAME
436+
echo "Uploading to R2..."
437+
AWS_ACCESS_KEY_ID=$R2_ACCESS_KEY AWS_SECRET_ACCESS_KEY=$R2_SECRET_KEY \
438+
aws s3 cp /tmp/$FILENAME s3://$R2_BUCKET/ --endpoint-url $R2_ENDPOINT
439+
echo "Backup completed successfully."
440+
EOT
441+
]
442+
env {
443+
name = "DB_HOST"
444+
value = "tasknote-db-svc"
445+
}
446+
env {
447+
name = "POSTGRES_USER"
448+
value_from {
449+
secret_key_ref {
450+
name = kubernetes_secret_v1.tasknote_secrets.metadata[0].name
451+
key = "postgres_user"
452+
}
453+
}
454+
}
455+
env {
456+
name = "POSTGRES_PASSWORD"
457+
value_from {
458+
secret_key_ref {
459+
name = kubernetes_secret_v1.tasknote_secrets.metadata[0].name
460+
key = "postgres_password"
461+
}
462+
}
463+
}
464+
env {
465+
name = "POSTGRES_DB"
466+
value_from {
467+
secret_key_ref {
468+
name = kubernetes_secret_v1.tasknote_secrets.metadata[0].name
469+
key = "postgres_db"
470+
}
471+
}
472+
}
473+
env {
474+
name = "R2_ACCESS_KEY"
475+
value_from {
476+
secret_key_ref {
477+
name = kubernetes_secret_v1.r2_backup_secrets.metadata[0].name
478+
key = "access_key"
479+
}
480+
}
481+
}
482+
env {
483+
name = "R2_SECRET_KEY"
484+
value_from {
485+
secret_key_ref {
486+
name = kubernetes_secret_v1.r2_backup_secrets.metadata[0].name
487+
key = "secret_key"
488+
}
489+
}
490+
}
491+
env {
492+
name = "R2_BUCKET"
493+
value = var.r2_bucket_name
494+
}
495+
env {
496+
name = "R2_ENDPOINT"
497+
value = var.r2_endpoint
498+
}
499+
}
500+
restart_policy = "OnFailure"
501+
}
502+
}
503+
}
504+
}
505+
}
506+
}
507+

0 commit comments

Comments
 (0)