Migrate legacy ServerKit volume during upgrade

Run the target version upgrade hook to move the SQLite database and its SQLite sidecars from the previously published serverkit-data volume into the new app-local bind directory. Keep the migration scoped to installs whose old Compose still used the named volume and retain the legacy volume for rollback.
This commit is contained in:
okxlin
2026-09-17 19:57:29 +08:00
parent c69d1fee40
commit a0dfe66df8
4 changed files with 182 additions and 2 deletions
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env bash
set -euo pipefail
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export PATH
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd -P)"
DATA_DIR="$ROOT_DIR/data"
COMPOSE_FILE="$ROOT_DIR/docker-compose.yml"
LEGACY_VOLUME="serverkit-data"
MIGRATION_IMAGE="jhd3197/serverkit:1.11.4"
fail() {
printf '%s\n' "$1" >&2
exit 1
}
has_data_files() {
local entry
for entry in "$DATA_DIR"/* "$DATA_DIR"/.[!.]* "$DATA_DIR"/..?*; do
if [[ -e "$entry" || -L "$entry" ]]; then
return 0
fi
done
return 1
}
bash "$SCRIPT_DIR/init.sh"
if ! grep -Fq 'serverkit-data:/app/instance' "$COMPOSE_FILE"; then
exit 0
fi
if [[ -f "$DATA_DIR/serverkit.db" ]]; then
exit 0
fi
if has_data_files; then
fail "ServerKit data directory is non-empty but serverkit.db is missing; refusing legacy-volume migration"
fi
command -v docker >/dev/null 2>&1 || fail "docker is required for legacy ServerKit volume migration"
if ! docker volume inspect "$LEGACY_VOLUME" >/dev/null 2>&1; then
exit 0
fi
docker image inspect "$MIGRATION_IMAGE" >/dev/null 2>&1 || fail "migration image is unavailable: $MIGRATION_IMAGE"
docker run --rm --network none --user 0 --entrypoint /bin/sh \
--volume "$LEGACY_VOLUME:/legacy:ro" \
--volume "$DATA_DIR:/target:rw" \
"$MIGRATION_IMAGE" \
-c '
set -eu
stage=/target/.serverkit-data-migration
[ ! -e "$stage" ] && [ ! -L "$stage" ] || { echo "migration staging path already exists" >&2; exit 1; }
mkdir "$stage"
cleanup() {
rm -rf -- "$stage"
}
trap cleanup EXIT
found=0
found_db=0
for item in /legacy/* /legacy/.[!.]* /legacy/..?*; do
[ -e "$item" ] || [ -L "$item" ] || continue
[ -f "$item" ] && [ ! -L "$item" ] || { echo "legacy volume contains an unsupported non-regular file" >&2; exit 1; }
name=${item##*/}
case "$name" in
serverkit.db|serverkit.db-wal|serverkit.db-shm) ;;
*) echo "legacy volume contains an unexpected file" >&2; exit 1 ;;
esac
cp -p -- "$item" "$stage/$name"
found=1
[ "$name" = serverkit.db ] && found_db=1
done
[ "$found" = 0 ] && exit 0
[ "$found_db" = 1 ] || { echo "legacy volume does not contain serverkit.db" >&2; exit 1; }
for name in serverkit.db-wal serverkit.db-shm serverkit.db; do
if [ -e "$stage/$name" ]; then
[ ! -e "/target/$name" ] && [ ! -L "/target/$name" ] || { echo "target data file already exists" >&2; exit 1; }
chown 999:999 "$stage/$name"
chmod 0640 "$stage/$name"
mv -- "$stage/$name" "/target/$name"
fi
done
'
bash "$SCRIPT_DIR/init.sh"
+1 -1
View File
@@ -29,7 +29,7 @@ ServerKit 是一个自托管的远程服务器管理与运维面板,提供服
3. 通过安装时配置的 HTTP 端口打开 ServerKit。首次打开时使用注册页面创建第一个用户;第一个用户会被授予管理员角色。
4. 如果使用反向代理,请填写 `SERVERKIT_PUBLIC_URL`,并仅在请求一定经过可信反向代理时将 `TRUST_PROXY_HEADERS` 设为 `true`。需要同时允许多个浏览器来源时,在 `CORS_ORIGINS` 中用逗号分隔填写。
SQLite 数据库持久化在应用安装目录的 `data/serverkit.db`,并以 bind mount 映射到容器内的 `/app/instance`。该目录位于 1Panel 应用目录中,便于随应用数据一起备份。不要删除该目录,否则会丢失 ServerKit 数据。
SQLite 数据库持久化在应用安装目录的 `data/serverkit.db`,并以 bind mount 映射到容器内的 `/app/instance`。该目录位于 1Panel 应用目录中,便于随应用数据一起备份。不要删除该目录,否则会丢失 ServerKit 数据。由旧版 `serverkit-data` 卷升级时,目标版本会在新目录为空时自动迁移 SQLite 文件;旧卷会保留,不会自动删除。
## 版本
+1 -1
View File
@@ -29,7 +29,7 @@ This is ServerKit's containerized deployment. The package does not mount the Doc
3. Open ServerKit on the configured HTTP port. On the first visit, use the registration page to create the first user; the first user is given the administrator role.
4. When using a reverse proxy, set `SERVERKIT_PUBLIC_URL`. Set `TRUST_PROXY_HEADERS` to `true` only when every request is guaranteed to pass through a trusted proxy. Add multiple browser origins as a comma-separated `CORS_ORIGINS` value.
The SQLite database is persisted at `data/serverkit.db` in the application install directory and bind-mounted to `/app/instance`. This keeps the data under the 1Panel application directory for application backups. Do not remove this directory unless the ServerKit data can be discarded.
The SQLite database is persisted at `data/serverkit.db` in the application install directory and bind-mounted to `/app/instance`. This keeps the data under the 1Panel application directory for application backups. Do not remove this directory unless the ServerKit data can be discarded. When upgrading from the legacy `serverkit-data` volume, the target version migrates the SQLite files when the new directory is empty; the legacy volume is retained and is not deleted automatically.
## Versions
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env bash
set -euo pipefail
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export PATH
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd -P)"
DATA_DIR="$ROOT_DIR/data"
COMPOSE_FILE="$ROOT_DIR/docker-compose.yml"
LEGACY_VOLUME="serverkit-data"
MIGRATION_IMAGE="jhd3197/serverkit:latest"
fail() {
printf '%s\n' "$1" >&2
exit 1
}
has_data_files() {
local entry
for entry in "$DATA_DIR"/* "$DATA_DIR"/.[!.]* "$DATA_DIR"/..?*; do
if [[ -e "$entry" || -L "$entry" ]]; then
return 0
fi
done
return 1
}
bash "$SCRIPT_DIR/init.sh"
if ! grep -Fq 'serverkit-data:/app/instance' "$COMPOSE_FILE"; then
exit 0
fi
if [[ -f "$DATA_DIR/serverkit.db" ]]; then
exit 0
fi
if has_data_files; then
fail "ServerKit data directory is non-empty but serverkit.db is missing; refusing legacy-volume migration"
fi
command -v docker >/dev/null 2>&1 || fail "docker is required for legacy ServerKit volume migration"
if ! docker volume inspect "$LEGACY_VOLUME" >/dev/null 2>&1; then
exit 0
fi
docker image inspect "$MIGRATION_IMAGE" >/dev/null 2>&1 || fail "migration image is unavailable: $MIGRATION_IMAGE"
docker run --rm --network none --user 0 --entrypoint /bin/sh \
--volume "$LEGACY_VOLUME:/legacy:ro" \
--volume "$DATA_DIR:/target:rw" \
"$MIGRATION_IMAGE" \
-c '
set -eu
stage=/target/.serverkit-data-migration
[ ! -e "$stage" ] && [ ! -L "$stage" ] || { echo "migration staging path already exists" >&2; exit 1; }
mkdir "$stage"
cleanup() {
rm -rf -- "$stage"
}
trap cleanup EXIT
found=0
found_db=0
for item in /legacy/* /legacy/.[!.]* /legacy/..?*; do
[ -e "$item" ] || [ -L "$item" ] || continue
[ -f "$item" ] && [ ! -L "$item" ] || { echo "legacy volume contains an unsupported non-regular file" >&2; exit 1; }
name=${item##*/}
case "$name" in
serverkit.db|serverkit.db-wal|serverkit.db-shm) ;;
*) echo "legacy volume contains an unexpected file" >&2; exit 1 ;;
esac
cp -p -- "$item" "$stage/$name"
found=1
[ "$name" = serverkit.db ] && found_db=1
done
[ "$found" = 0 ] && exit 0
[ "$found_db" = 1 ] || { echo "legacy volume does not contain serverkit.db" >&2; exit 1; }
for name in serverkit.db-wal serverkit.db-shm serverkit.db; do
if [ -e "$stage/$name" ]; then
[ ! -e "/target/$name" ] && [ ! -L "/target/$name" ] || { echo "target data file already exists" >&2; exit 1; }
chown 999:999 "$stage/$name"
chmod 0640 "$stage/$name"
mv -- "$stage/$name" "/target/$name"
fi
done
'
bash "$SCRIPT_DIR/init.sh"