diff --git a/templates/backup-cronjob.yaml b/templates/backup-cronjob.yaml new file mode 100644 index 0000000..000bf42 --- /dev/null +++ b/templates/backup-cronjob.yaml @@ -0,0 +1,90 @@ +{{- if .Values.backups.enabled -}} +# Daily dump job. Same image as the Postgres pod, so pg_dump is +# version-matched. Output goes to the dedicated backup PVC; the same +# job script prunes older dumps to honor `backups.retain`. +# +# Tower's "Backup Now" feature creates a one-off Job from this same +# template at request time — see backend/cmd/api/backups.go. +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ include "instance.fullname" . }}-backup + labels: + {{- include "instance.labels" . | nindent 4 }} + odoosky.io/role: backup +spec: + schedule: {{ .Values.backups.schedule | quote }} + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 5 + failedJobsHistoryLimit: 3 + jobTemplate: + metadata: + labels: + {{- include "instance.labels" . | nindent 8 }} + odoosky.io/role: backup + spec: + backoffLimit: 1 + template: + metadata: + labels: + {{- include "instance.labels" . | nindent 12 }} + odoosky.io/role: backup + spec: + restartPolicy: Never + containers: + - name: pgdump + image: "{{ .Values.postgres.image }}:{{ .Values.postgres.tag }}" + imagePullPolicy: IfNotPresent + env: + - name: PGHOST + value: {{ include "instance.fullname" . }}-pg + - name: PGUSER + valueFrom: + secretKeyRef: + name: {{ include "instance.fullname" . }}-pg + key: POSTGRES_USER + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: {{ include "instance.fullname" . }}-pg + key: POSTGRES_PASSWORD + - name: PGDATABASE + valueFrom: + secretKeyRef: + name: {{ include "instance.fullname" . }}-pg + key: POSTGRES_DB + - name: RETAIN + value: {{ .Values.backups.retain | quote }} + command: + - /bin/sh + - -c + - | + set -e + TS=$(date -u +%Y%m%dT%H%M%SZ) + OUT=/backups/${TS}.sql.gz + mkdir -p /backups + echo ">>> pg_dump → $OUT" + pg_dump --format=plain --clean --if-exists --no-owner --no-acl \ + | gzip -9 > "$OUT" + echo ">>> wrote $(du -h "$OUT" | cut -f1)" + # Rotate: keep only the newest $RETAIN dumps. + cd /backups + ls -1t *.sql.gz 2>/dev/null \ + | awk -v n=$RETAIN 'NR > n' \ + | xargs -r rm -v + ls -lh /backups + volumeMounts: + - name: backups + mountPath: /backups + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: "1" + memory: 1Gi + volumes: + - name: backups + persistentVolumeClaim: + claimName: {{ include "instance.fullname" . }}-backups +{{- end }} diff --git a/templates/backup-pvc.yaml b/templates/backup-pvc.yaml new file mode 100644 index 0000000..0760b53 --- /dev/null +++ b/templates/backup-pvc.yaml @@ -0,0 +1,14 @@ +{{- if .Values.backups.enabled -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "instance.fullname" . }}-backups + labels: + {{- include "instance.labels" . | nindent 4 }} + odoosky.io/role: backups +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: {{ .Values.backups.storage | quote }} +{{- end }} diff --git a/values.yaml b/values.yaml index 8a8190a..44c7c02 100644 --- a/values.yaml +++ b/values.yaml @@ -73,6 +73,17 @@ postgres: password: "" storage: 10Gi +backups: + enabled: true + # Cron schedule for the automatic backup job. Default 03:00 UTC + # daily — quiet hour for most timezones, non-business in EU/US/AS. + schedule: "0 3 * * *" + # PVC size for retained dumps. Holds ~7 days of dumps for a small + # instance; scale up via overlay if the instance has a large DB. + storage: 10Gi + # How many dumps to retain. Older ones are pruned by the same Job. + retain: 7 + ingress: # Traefik entrypoint name (set on the Traefik install in the # `traefik` namespace).