# postgres-backup.yaml apiVersion: v1 kind: ConfigMap metadata: name: postgres-backup-script namespace: gitea data: backup.sh: | #!/bin/bash set -euo pipefail BACKUP_DIR="/backup/postgres" DATE=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="postgres_${DATE}.sql.gz" RETENTION_DAYS=30 echo "[$(date)] Starting PostgreSQL backup..." # Create backup directory mkdir -p ${BACKUP_DIR} # Perform backup if pg_dump -h ${POSTGRES_HOST} -U ${POSTGRES_USER} ${POSTGRES_DB} | gzip > ${BACKUP_DIR}/${BACKUP_FILE}; then echo "[$(date)] ✓ Backup successful: ${BACKUP_FILE}" # Verify backup file exists and is not empty if [ -s ${BACKUP_DIR}/${BACKUP_FILE} ]; then SIZE=$(du -h ${BACKUP_DIR}/${BACKUP_FILE} | cut -f1) echo "[$(date)] Backup size: ${SIZE}" else echo "[$(date)] ✗ ERROR: Backup file is empty!" exit 1 fi else echo "[$(date)] ✗ Backup failed!" exit 1 fi # Cleanup old backups echo "[$(date)] Cleaning up backups older than ${RETENTION_DAYS} days..." find ${BACKUP_DIR} -name "postgres_*.sql.gz" -mtime +${RETENTION_DAYS} -delete # List recent backups echo "[$(date)] Recent backups:" ls -lh ${BACKUP_DIR} | tail -5 echo "[$(date)] Backup completed successfully" --- apiVersion: batch/v1 kind: CronJob metadata: name: postgres-backup namespace: gitea spec: schedule: "0 2 * * *" # Daily at 2 AM successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 3 jobTemplate: spec: template: spec: containers: - name: postgres-backup image: postgres:17 # Match your PostgreSQL version command: ["/bin/bash", "/scripts/backup.sh"] env: - name: POSTGRES_HOST value: "gitea-postgresql" # Adjust to your service name - name: POSTGRES_USER value: "gitea" - name: POSTGRES_DB value: "gitea" - name: PGPASSWORD valueFrom: secretKeyRef: name: gitea-postgresql # Adjust to your secret name key: password volumeMounts: - name: backup-script mountPath: /scripts - name: backup-storage mountPath: /backup volumes: - name: backup-script configMap: name: postgres-backup-script defaultMode: 0755 - name: backup-storage hostPath: path: /mnt/backup/k8s-backups type: DirectoryOrCreate restartPolicy: OnFailure