配置指南

分享数据库不花钱-每天自动备份

写在前面

如果你使用的数据库已经花钱开启使用了自动备份功能,那么可以忽略我这篇文章;

这篇文章主要解决咱们网站上线后,不花钱进行数据库自动备份的问题;用户数据很重要,很有必要每天备份一份,而且还不占用你本地空间。接下来跟我操作;

原理

这是PostgreSQL 数据库自动化备份方案(主要是面向Supabase、Neon数据库),通过 GitHub Actions 定时执行脚本,将备份文件自动提交到 Git 仓库中。

  • 执行环境:利用 GitHub Actions 的云端环境,无需自备服务器。

  • 备份工具:使用 pg_dump 工具进行数据库导出。

  • 定时触发:通过 GitHub Actions 的 schedule(cron)功能,每天定时运行。

  • 文件存储:备份导出的 .sql 文件按日期存储在 backups/ 目录下,并自动 commit/push 回仓库。

实操

1、github新建一个私有仓库

2、将仓库弄到本地

(1)根目录下新建 config.json

配置项说明:

  • name: 数据库的标识名称,也将作为备份文件夹名。

  • description: 项目描述,方便备注。

  • url: PostgreSQL 的连接字符串(例如:postgresql://user:password@host:port/dbname)。

  • tables: 可选。

    • null 时:执行全量备份。

    • 为数组时(如 ["users", "orders"]):仅备份指定的表。

示例 config.json


{

  "databases": [

    {

      "name": "xxx-app",

      "description": "A网站数据库 - 全量备份",

      "url": "postgresql://postgres:password@db-host:5432/postgres",

      "tables": null

    },

    {

      "name": "xxx-site",

      "description": "B网站数据库 - 指定表备份",

      "url": "postgresql://user:pass@host:5432/db",

      "tables": ["posts", "categories"]

    }

  ]

}

3、新建scripts/backup.sh(先新建scripts,再新建backup.sh)

将下面内容复制粘贴进去


#!/bin/bash

set -e



CONFIG_FILE="config.json"

BACKUP_DIR="backups"

DATE=$(date +%Y%m%d)

YEAR=$(date +%Y)

MONTH=$(date +%m)



# Cleanup logic: on the last day of the month, delete the previous month's backup directory

if [ "$(date -v+1d +%d)" = "01" ]; then

    PREV_YEAR=$(date -v1d -v-1d +%Y)

    PREV_MONTH=$(date -v1d -v-1d +%m)

    echo "Last day of the month. Cleaning up backups for $PREV_YEAR-$PREV_MONTH..."

    for DB_DIR in "$BACKUP_DIR"/*; do

        if [ -d "$DB_DIR/$PREV_YEAR/$PREV_MONTH" ]; then

            echo "Removing: $DB_DIR/$PREV_YEAR/$PREV_MONTH"

            rm -rf "$DB_DIR/$PREV_YEAR/$PREV_MONTH"

        fi

    done

fi



if [ ! -f "$CONFIG_FILE" ]; then

    echo "Error: config.json not found"

    exit 1

fi



DB_COUNT=$(jq '.databases | length' $CONFIG_FILE)

echo "Found $DB_COUNT databases to backup"



for ((i=0; i<$DB_COUNT; i++)); do

    DB_NAME=$(jq -r ".databases[$i].name" $CONFIG_FILE)

    DB_DESC=$(jq -r ".databases[$i].description" $CONFIG_FILE)

    DB_URL=$(jq -r ".databases[$i].url" $CONFIG_FILE)

    TABLES=$(jq -r ".databases[$i].tables" $CONFIG_FILE)

    

    echo "=========================================="

    echo "Backing up: $DB_NAME ($DB_DESC)"

    echo "=========================================="

    

    if [ -z "$DB_URL" ] || [ "$DB_URL" == "null" ]; then

        echo "Warning: No database URL found for $DB_NAME"

        continue

    fi

    

    mkdir -p "$BACKUP_DIR/$DB_NAME/$YEAR/$MONTH"

    BACKUP_FILE="$BACKUP_DIR/$DB_NAME/$YEAR/$MONTH/${DATE}.sql"

    

    if [ "$TABLES" == "null" ]; then

        echo "Mode: Full backup"

        pg_dump "$DB_URL" \

            --clean \

            --if-exists \

            --quote-all-identifiers \

            --no-owner \

            --no-privileges \

            -f "$BACKUP_FILE"

    else

        TABLE_ARGS=""

        TABLE_LIST=$(jq -r ".databases[$i].tables[]" $CONFIG_FILE)

        for TABLE in $TABLE_LIST; do

            TABLE_ARGS="$TABLE_ARGS -t public.$TABLE"

        done

        echo "Mode: Partial backup - tables: $TABLE_LIST"

        pg_dump "$DB_URL" \

            --clean \

            --if-exists \

            --quote-all-identifiers \

            --no-owner \

            --no-privileges \

            $TABLE_ARGS \

            -f "$BACKUP_FILE"

    fi

    

    echo "Backup saved: ${BACKUP_FILE}"

    echo "Done: $DB_NAME"

    echo ""

done



echo "All backups completed!"

4、GitHub Actions 设置

根目录下新建.github/workflows/backup.yml(先新建.github,再新建workflows,再新建backup.yml)

将下面内容复制粘贴进去


name: Daily Database Backup



on:

  schedule:

    - cron: '0 2 * * *'

  workflow_dispatch:



permissions:

  contents: write



jobs:

  backup:

    runs-on: ubuntu-latest

    

    steps:

      - name: Checkout repository

        uses: actions/checkout@v4

        with:

          fetch-depth: 0

        

      - name: Install dependencies

        run: |

          sudo apt-get update

          sudo apt-get install -y jq

          

      - name: Run backup script with PostgreSQL 17

        run: |

          chmod +x scripts/backup.sh

          # 使用Docker运行PostgreSQL 17客户端

          docker run --rm -v $(pwd):/workspace -w /workspace \

            postgres:17 bash -c "apt-get update && apt-get install -y jq && ./scripts/backup.sh"

          

      - name: Commit and push backups

        run: |

          git config --local user.email "github-actions[bot]@users.noreply.github.com"

          git config --local user.name "github-actions[bot]"

          git add backups/

          git diff --staged --quiet || git commit -m "backup: $(date +%Y-%m-%d)"

          git push
  • 定时执行:默认设置在每天协调世界时 (UTC) 02:00(北京时间 10:00)运行。【这个时间可以改】

    
    on:
    
      schedule:
    
        - cron: '0 2 * * *'
  • 手动触发:支持 workflow_dispatch,你可以在 GitHub 仓库的 Actions 标签页找到 "Daily Database Backup" 手动点击 "Run workflow" 立即执行。

  • Postgres 版本:脚本默认使用 postgres:17 镜像执行备份。

然后将其提交到仓库,然后第二天就开始执行了。

5、执行成功后,备份文件将按照以下结构生成:


backups/

└── {数据库名称}/

    └── {年}/

        └── {月}/

            └── {年月日}.sql
image

6、安全建议 (重要)

  • 私有仓库:务必将此仓库设为 Private(私有),因为 config.json 中包含数据库的明文密码。