Files
gitea/docs/sqlite-to-postgres-migration.md
2026-04-27 22:10:20 +08:00

8.0 KiB
Raw Blame History

SQLite 迁移到 PostgreSQL

本文档适用于已有 Gitea 服务仍在使用旧版 Docker Compose 和 SQLite尚未更新到引入 PostgreSQL 的最新 commit 的场景。

迁移目标是把 data/gitea/gitea.db 中的业务数据导入到新的 PostgreSQL 数据库同时继续复用现有的仓库、LFS、附件和配置目录。

gitea migrate 不是 SQLite 到 PostgreSQL 的数据搬迁工具。它只会对当前 Gitea 配置指向的数据库执行 Gitea schema migration。

Gitea 自带的跨数据库导出入口是 gitea dump --database postgres。Gitea 没有对应的自动 gitea restore 命令,恢复或导入 PostgreSQL 时需要手动解压 dump 包,并用 psql 导入其中的 gitea-db.sql

迁移前准备

  • 预留维护窗口。迁移期间必须停止 Gitea避免 SQLite 继续写入。
  • 保持 GITEA_VERSION 不变。不要在同一次操作中同时升级 Gitea 版本和切换数据库。
  • 准备 .env 中的 PostgreSQL 配置:
POSTGRES_VERSION=16-alpine
POSTGRES_DB=gitea
POSTGRES_USER=gitea
POSTGRES_PASSWORD=<强密码>
  • 确认可以运行 sqlite3unzip,并且 PostgreSQL 容器里可以运行 psql
  • 本文档假设 docker-compose.yml 把宿主机 ./data 挂载到 Gitea 容器内的 /data。因此 dump 输出到容器内 /data/gitea-dump-postgres.zip 时,宿主机对应文件是 data/gitea-dump-postgres.zip

1. 停止旧 Gitea

在仍使用 SQLite 的旧版 compose 下停止 Gitea

docker compose stop gitea

如果启用了 Caddy可以保持 Caddy 运行显示维护页,也可以一起停止:

docker compose stop caddy

2. 冷备份 SQLite 和数据目录

ts=$(date +%Y%m%d-%H%M%S)
mkdir -p backups/$ts
cp data/gitea/gitea.db backups/$ts/gitea.db
cp data/gitea/conf/app.ini backups/$ts/app.ini
tar -czf backups/$ts/gitea-data.tar.gz data/git data/gitea

备份完成后,记录 SQLite 中的关键表数量,作为迁移后校验基线:

sqlite3 data/gitea/gitea.db "select 'user', count(*) from user union all select 'repository', count(*) from repository union all select 'lfs_meta_object', count(*) from lfs_meta_object union all select 'public_key', count(*) from public_key union all select 'issue', count(*) from issue union all select 'release', count(*) from release;"

3. 使用 Gitea dump 导出 PostgreSQL SQL

在仍使用旧 SQLite 配置时运行 gitea dump。不要先把 Gitea 配置切到 PostgreSQL否则 dump 会尝试读取新数据库。

docker compose run --rm --user git gitea \
  gitea dump -c /data/gitea/conf/app.ini \
  --database postgres \
  --file /data/gitea-dump-postgres.zip

确认宿主机上已经生成 dump 包:

ls -lh data/gitea-dump-postgres.zip

如果当前镜像或环境不能识别 git 用户,可以改用官方镜像常见 UID/GID

docker compose run --rm --user 1000:1000 gitea \
  gitea dump -c /data/gitea/conf/app.ini \
  --database postgres \
  --file /data/gitea-dump-postgres.zip

解压 dump 包,得到 gitea-db.sql

mkdir -p backups/gitea-dump-postgres
unzip -o data/gitea-dump-postgres.zip -d backups/gitea-dump-postgres
ls -lh backups/gitea-dump-postgres/gitea-db.sql

4. 更新代码并只启动 PostgreSQL

更新到包含 PostgreSQL 服务的最新 commit并补齐 .env 中的 PostgreSQL 配置。

git pull

先只启动 PostgreSQL不要启动 Gitea

docker compose up -d postgres

确认 PostgreSQL 可连接:

docker compose exec -T postgres psql -U gitea -d gitea -c "select version();"

5. 确认 PostgreSQL 是空库

检查目标库是否已有表:

docker compose exec -T postgres psql -U gitea -d gitea -c '\dt'

如果已经有 Gitea 业务表,先确认这些表不是生产数据。确认可丢弃后,停止 PostgreSQL备份并移走 data/postgres/,再重新启动空库:

docker compose stop postgres
ts=$(date +%Y%m%d-%H%M%S)
mv data/postgres data/postgres.before-migration-$ts
docker compose up -d postgres

再次运行 \dt,确认目标库为空。

6. 导入 dump SQL 到 PostgreSQL

使用 psql 导入 gitea-db.sql

docker compose exec -T postgres psql -U gitea -d gitea < backups/gitea-dump-postgres/gitea-db.sql

导入完成后,检查目标库关键表数量:

docker compose exec -T postgres psql -U gitea -d gitea -c "select 'user', count(*) from \"user\" union all select 'repository', count(*) from repository union all select 'lfs_meta_object', count(*) from lfs_meta_object union all select 'public_key', count(*) from public_key union all select 'issue', count(*) from issue union all select 'release', count(*) from release;"

这些数量应与第 2 步 SQLite 基线一致。

7. 启动 Gitea

确认 docker-compose.yml 已通过 GITEA__database__* 环境变量指向 PostgreSQL然后启动 Gitea

docker compose up -d gitea
docker compose logs -f gitea

Gitea 启动时会对 PostgreSQL 执行必要的 schema migration。日志中不应出现数据库连接、类型转换、唯一约束或 migration 错误。

如果使用 Caddy

docker compose up -d caddy

8. 迁移后验证

  • 使用原管理员账号登录 Web UI。
  • 确认用户、组织、仓库、Issue、Release、SSH Key 等数据存在。
  • 执行一次 HTTP 或 HTTPS clone。
  • 执行一次 SSH clone。
  • 推送一个普通 commit。
  • 如果使用 Git LFS执行一次 LFS push。
  • 检查日志:
docker compose logs gitea postgres

常见问题

Gitea is not supposed to be run as root

docker compose run 默认可能以 root 用户启动一次性容器,但 Gitea 不允许以 root 运行。给 dump 命令加上 --user git

docker compose run --rm --user git gitea \
  gitea dump -c /data/gitea/conf/app.ini \
  --database postgres \
  --file /data/gitea-dump-postgres.zip

如果 git 用户不可用,改用:

docker compose run --rm --user 1000:1000 gitea \
  gitea dump -c /data/gitea/conf/app.ini \
  --database postgres \
  --file /data/gitea-dump-postgres.zip

Unable to create dump file "/gitea-dump-*.zip": permission denied

gitea dump 默认会把文件写到容器当前目录。如果当前目录是 /,非 root 用户没有写权限。显式指定 --file /data/gitea-dump-postgres.zip,让 dump 写入已挂载且可持久化的 /data 目录。

pgloader 备选方案

如果 gitea dump --database postgres 在当前版本或数据集上失败,可以把 pgloader 作为备选迁移工具。使用前仍然要停止 Gitea、完成冷备份并确认 PostgreSQL 目标库为空。

<POSTGRES_PASSWORD> 替换为 .env 中的真实密码:

docker run --rm \
  --network gitea_default \
  -v "$PWD/data/gitea:/data/gitea:ro" \
  dimitri/pgloader:latest \
  pgloader sqlite:///data/gitea/gitea.db postgresql://gitea:<POSTGRES_PASSWORD>@postgres:5432/gitea

如果 compose project 网络名不是 gitea_default,替换 --network 参数。

回滚

如果迁移后验证失败,先停止新 Gitea

docker compose stop gitea

回滚到旧版 SQLite compose或临时移除 docker-compose.yml 中的 GITEA__database__* 环境变量,避免它覆盖 app.ini

恢复备份:

cp backups/<备份时间>/gitea.db data/gitea/gitea.db
cp backups/<备份时间>/app.ini data/gitea/conf/app.ini

再启动旧 SQLite 版本:

docker compose up -d gitea

在迁移完全确认前,不要删除 data/gitea/gitea.dbbackups/data/gitea-dump-postgres.zip 或迁移前的 data/postgres.before-migration-*

演练建议

正式迁移前,建议在复制出来的目录中完整演练一次:

cp -a gitea gitea-migration-dry-run
cd gitea-migration-dry-run

演练中也要验证 dump 导出、SQL 解压、空库检查、psql 导入、表数量对比、Gitea 启动和 Web/Git 操作。演练通过后,再在正式目录执行维护窗口迁移。