Bash · 1631 bytes Raw Blame History
1 #!/usr/bin/env bash
2 # SPDX-License-Identifier: AGPL-3.0-or-later
3 #
4 # Daily logical backup. Run from cron (or a systemd timer) as the
5 # postgres user. We take a custom-format pg_dump of the shithub DB,
6 # stream it to Spaces, and keep one local copy in /var/backups for
7 # the operator to grab in a hurry. Lifecycle on the bucket prunes
8 # anything older than 30 days; PITR rolls forward from the WAL
9 # archive (see archive_command.sh).
10 #
11 # Exit non-zero on any failure so the systemd timer surfaces it
12 # (OnFailure= → alertmanager).
13
14 set -euo pipefail
15
16 DB="${SHITHUB_DB:-shithub}"
17 BUCKET="${SHITHUB_BACKUP_BUCKET:-spaces-prod:shithub-backups}"
18 LOCAL_DIR="${SHITHUB_BACKUP_LOCAL:-/var/backups/shithub}"
19 STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
20 NAME="${DB}-${STAMP}.dump"
21
22 mkdir -p "$LOCAL_DIR"
23
24 # pg_dump as the postgres user via local-socket peer auth.
25 # Cron runs this script as root; sudo handles the user switch.
26 sudo -u postgres pg_dump --format=custom --compress=9 --no-owner --no-privileges \
27 --file="$LOCAL_DIR/$NAME" "$DB"
28
29 # Verify the dump is structurally sound before we ship it.
30 pg_restore --list "$LOCAL_DIR/$NAME" >/dev/null
31
32 # --s3-no-check-bucket: skip the GetBucketLocation pre-check that
33 # requires a permission our scoped-RW Spaces key doesn't grant.
34 # The actual PUT works fine on a key with bucket-level readwrite.
35 rclone --config /etc/rclone-shithub.conf --s3-no-check-bucket \
36 copyto "$LOCAL_DIR/$NAME" "$BUCKET/daily/$(date -u +%Y/%m/%d)/$NAME"
37
38 # Local retention: keep the last 7 dumps; bucket lifecycle handles
39 # the long tail.
40 ls -1t "$LOCAL_DIR"/*.dump 2>/dev/null | tail -n +8 | xargs -r rm -f