From ffce751a3989a702cdeeb59496f52d3cebc2b7e0 Mon Sep 17 00:00:00 2001 From: fmar Date: Mon, 31 Aug 2026 15:58:27 +0200 Subject: [PATCH 1/3] fix(postgres-backup): stream dumps to S3 --- postgres-backup-s3/backup.sh | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/postgres-backup-s3/backup.sh b/postgres-backup-s3/backup.sh index ce7ba9e..d4155c7 100644 --- a/postgres-backup-s3/backup.sh +++ b/postgres-backup-s3/backup.sh @@ -78,16 +78,19 @@ echo "Starting backup at $(date -Iseconds)" if [ "${POSTGRES_BACKUP_ALL}" == "true" ]; then echo "Creating dump of all databases from ${POSTGRES_HOST}..." - - pg_dumpall -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER | gzip | gpg -c --batch --passphrase $GPG_PASSWORD -o ${DUMP_PATH}dump.sql.gz.gpg - echo "Uploading dump to $S3_BUCKET" - cat ${DUMP_PATH}dump.sql.gz.gp | aws $AWS_ARGS s3 cp - "s3://${S3_BUCKET}${S3_PREFIX}all_$(date +"%Y-%m-%dT%H:%M:%SZ").sql.gz.gpg" || exit 2 + BACKUP_URI="s3://${S3_BUCKET}${S3_PREFIX}all_$(date +"%Y-%m-%dT%H:%M:%SZ").sql.gz.gpg" + if ! pg_dumpall -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" \ + | gzip \ + | gpg -c --batch --pinentry-mode loopback --passphrase "$GPG_PASSWORD" --output - \ + | aws $AWS_ARGS s3 cp - "$BACKUP_URI"; then + echo "SQL backup failed; removing any partial upload" + aws $AWS_ARGS s3 rm "$BACKUP_URI" || true + exit 2 + fi echo "SQL backup uploaded successfully" - - rm -rf ${DUMP_PATH}dump.sql.gz else OIFS="$IFS" IFS=',' @@ -96,16 +99,19 @@ else IFS="$OIFS" echo "Creating dump of ${DB} database from ${POSTGRES_HOST}..." - - pg_dump $POSTGRES_HOST_OPTS $DB | gzip | gpg -c --batch --passphrase $GPG_PASSWORD -o ${DUMP_PATH}dump.sql.gz.gpg - echo "Uploading dump to $S3_BUCKET" - cat ${DUMP_PATH}dump.sql.gz.gpg | aws $AWS_ARGS s3 cp - "s3://${S3_BUCKET}${S3_PREFIX}${DB}_$(date +"%Y-%m-%dT%H:%M:%SZ").sql.gz.gpg" || exit 2 + BACKUP_URI="s3://${S3_BUCKET}${S3_PREFIX}${DB}_$(date +"%Y-%m-%dT%H:%M:%SZ").sql.gz.gpg" + if ! pg_dump $POSTGRES_HOST_OPTS "$DB" \ + | gzip \ + | gpg -c --batch --pinentry-mode loopback --passphrase "$GPG_PASSWORD" --output - \ + | aws $AWS_ARGS s3 cp - "$BACKUP_URI"; then + echo "SQL backup failed; removing any partial upload" + aws $AWS_ARGS s3 rm "$BACKUP_URI" || true + exit 2 + fi echo "SQL backup uploaded successfully" - - rm -rf ${DUMP_PATH}dump.sql.gz done fi From 36bf6182ebad5ad47098cc903d844f86d3c1ffe0 Mon Sep 17 00:00:00 2001 From: fmar Date: Mon, 31 Aug 2026 16:14:25 +0200 Subject: [PATCH 2/3] fix(backup): promote completed uploads --- postgres-backup-s3/backup.sh | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/postgres-backup-s3/backup.sh b/postgres-backup-s3/backup.sh index d4155c7..350b9cb 100644 --- a/postgres-backup-s3/backup.sh +++ b/postgres-backup-s3/backup.sh @@ -81,12 +81,19 @@ if [ "${POSTGRES_BACKUP_ALL}" == "true" ]; then echo "Uploading dump to $S3_BUCKET" BACKUP_URI="s3://${S3_BUCKET}${S3_PREFIX}all_$(date +"%Y-%m-%dT%H:%M:%SZ").sql.gz.gpg" + IN_PROGRESS_URI="${BACKUP_URI}.in-progress" if ! pg_dumpall -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER" \ | gzip \ | gpg -c --batch --pinentry-mode loopback --passphrase "$GPG_PASSWORD" --output - \ - | aws $AWS_ARGS s3 cp - "$BACKUP_URI"; then + | aws $AWS_ARGS s3 cp - "$IN_PROGRESS_URI"; then echo "SQL backup failed; removing any partial upload" - aws $AWS_ARGS s3 rm "$BACKUP_URI" || true + aws $AWS_ARGS s3 rm "$IN_PROGRESS_URI" || true + exit 2 + fi + + if ! aws $AWS_ARGS s3 mv "$IN_PROGRESS_URI" "$BACKUP_URI"; then + echo "SQL backup promotion failed; removing the in-progress upload" + aws $AWS_ARGS s3 rm "$IN_PROGRESS_URI" || true exit 2 fi @@ -102,12 +109,19 @@ else echo "Uploading dump to $S3_BUCKET" BACKUP_URI="s3://${S3_BUCKET}${S3_PREFIX}${DB}_$(date +"%Y-%m-%dT%H:%M:%SZ").sql.gz.gpg" + IN_PROGRESS_URI="${BACKUP_URI}.in-progress" if ! pg_dump $POSTGRES_HOST_OPTS "$DB" \ | gzip \ | gpg -c --batch --pinentry-mode loopback --passphrase "$GPG_PASSWORD" --output - \ - | aws $AWS_ARGS s3 cp - "$BACKUP_URI"; then + | aws $AWS_ARGS s3 cp - "$IN_PROGRESS_URI"; then echo "SQL backup failed; removing any partial upload" - aws $AWS_ARGS s3 rm "$BACKUP_URI" || true + aws $AWS_ARGS s3 rm "$IN_PROGRESS_URI" || true + exit 2 + fi + + if ! aws $AWS_ARGS s3 mv "$IN_PROGRESS_URI" "$BACKUP_URI"; then + echo "SQL backup promotion failed; removing the in-progress upload" + aws $AWS_ARGS s3 rm "$IN_PROGRESS_URI" || true exit 2 fi From 4d958d73597d2f59be444ac3a6c35caccc114da8 Mon Sep 17 00:00:00 2001 From: fmar Date: Mon, 31 Aug 2026 16:16:03 +0200 Subject: [PATCH 3/3] fix(checker): ignore in-progress backups --- s3-lastmodified-checker/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/s3-lastmodified-checker/run.sh b/s3-lastmodified-checker/run.sh index 2be8475..1ceb12d 100755 --- a/s3-lastmodified-checker/run.sh +++ b/s3-lastmodified-checker/run.sh @@ -32,7 +32,7 @@ export AWS_DEFAULT_REGION=$S3_REGION # AWS CLI command to list objects in the S3 bucket sorted by modification time -latest_file=$(aws $AWS_ARGS s3api list-objects-v2 --bucket $S3_BUCKET --prefix $S3_PREFIX --query 'sort_by(Contents, &LastModified)[-1].Key' --output text) +latest_file=$(aws $AWS_ARGS s3api list-objects-v2 --bucket $S3_BUCKET --prefix $S3_PREFIX --query 'sort_by(Contents[?ends_with(Key, `.in-progress`) == `false`], &LastModified)[-1].Key' --output text) echo $latest_file