Skip to content

运维: 1Panel 重装后 OpenResty 网站数据迁移

作者 / AUTHOR 白墨麒麟 BaimoQilin

授权协议 / LICENSE CC-BY-NC-SA 署名-非商业性使用-相同方式共享

撰稿日期 / DATE OF WRITING 1/10/2026 20:19

背景

1Panel 面板发生故障无法访问,重装 1Panel 并将 /opt/1panel/apps/openresty/openresty/ 目录下的备份还原后,由 OpenResty 托管的网站服务均能正常运行,但 1Panel 面板内的「网站」列表显示为空。 此外,1Panel 的“应用导入”功能仅支持包含 app.json 的标准备份包,而现有的仅为应用目录备份,无法通过面板界面直接导入。

原因分析

1Panel 的「网站列表」并非通过扫描 Nginx/OpenResty 的 conf.d/*.conf 配置文件动态生成,而是依赖于 1Panel 自身的 SQLite 数据库存储元数据。 仅迁移 OpenResty 配置文件和站点文件,只能恢复业务层面的运行,由于缺乏面板数据库(记账层)的站点记录,导致控制台列表呈现空白。 值得注意的是,新版本 1Panel(特别是 v2 版本)在查询站点列表时,会根据 OpenResty 的“安装实例 ID”(如 websites.app_install_id)进行过滤。如果将旧数据库导入后,该字段为空或与新安装的实例 ID 不匹配,UI 查询将过滤掉这些记录,导致“数据库中虽有数据,但前端列表无法显示”的现象。

解决方案概述

核心思路:将旧数据库中的网站相关表合并至新数据库,并将 websites 表中的 app_install_id 统一修正为新服务器上 OpenResty 的安装 ID。最后替换新服务器的 1Panel.db 文件(需注意清理 wal/shm 临时文件)。

准备工作

你需要准备两份数据库文件:

  • 旧源数据:旧服务器的 1Panel 数据库备份,重命名为 1Panel-og.db
  • 新源数据:新服务器重装 1Panel 后生成的数据库,通常为 1Panel.db

默认路径通常位于:/opt/1panel/db/1Panel.db (如果路径不同,可使用 find /opt/1panel -name '1Panel.db' 进行查找)

步骤 1:数据库表合并(补全缺失数据)

在任意具备 Python 环境的机器上,将两个数据库文件置于同一目录,执行以下脚本。该脚本逻辑为:仅在新数据库对应表为空,而旧数据库中有数据时,才进行记录迁移,以避免破坏新面板的基础配置。

Python
import sqlite3, shutil, os

old_path = "1Panel-og.db"
new_path = "1Panel.db"
out_path = "1Panel.merged.db"

# 初始化输出文件
if os.path.exists(out_path):
    os.remove(out_path)
shutil.copy2(new_path, out_path)

def list_tables(con):
    cur = con.cursor()
    cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name")
    return [r[0] for r in cur.fetchall()]

def count(con, t):
    return con.execute(f"SELECT COUNT(*) FROM `{t}`").fetchone()[0]

con_new = sqlite3.connect(new_path)
con_old = sqlite3.connect(old_path)
tables = list_tables(con_new)

# 筛选需要迁移的表:新库为空且旧库有数据
tables_to_copy = [t for t in tables if count(con_new, t) == 0 and count(con_old, t) > 0]

con_new.close()
con_old.close()

# 执行合并
con = sqlite3.connect(out_path)
cur = con.cursor()
cur.execute("ATTACH DATABASE ? AS olddb", (old_path,))

for t in tables_to_copy:
    cur.execute(f"INSERT INTO `{t}` SELECT * FROM olddb.`{t}`;")

con.commit()
con.close()

print("Copied tables:", tables_to_copy)
print("Output:", out_path)

预期迁移的表通常包括: websites, website_domains, website_ssls, website_acme_accounts, website_dns_accounts, runtimes (不同版本表名略有差异,主要涉及网站、域名、SSL及关联账户信息)

步骤 2:修正 websites.app_install_id

若合并数据库并重启服务后,「网站」列表依然显示为空,通常由于 websites 表中的 app_install_id 字段未与当前 OpenResty 实例匹配。

首先,在合并后的数据库中查询当前 OpenResty 应用的安装 ID:

Bash
sqlite3 1Panel.merged.db "
SELECT ai.id, a.key
FROM app_installs ai
JOIN apps a ON a.id=ai.app_id
WHERE a.key='openresty'
ORDER BY ai.id
LIMIT 5;
"

假设输出的ai.id2(具体数值视环境而定)。执行以下 SQL 语句进行批量修正:

Bash
sqlite3 1Panel.merged.db "
UPDATE websites
SET app_install_id = 2
WHERE COALESCE(app_install_id,0)=0;
"

验证修正结果:

Bash
sqlite3 1Panel.merged.db "SELECT COUNT(*), MIN(app_install_id), MAX(app_install_id) FROM websites;"
sqlite3 1Panel.merged.db "SELECT id, primary_domain, app_install_id FROM websites LIMIT 20;"

步骤 3:替换数据库并重启服务

将处理好的1Panel.merged.db上传至新服务器(例如/root/1Panel.merged.db),严格按照以下步骤操作,注意处理 SQLite 的 WAL/SHM 临时文件以防止数据回滚或损坏。

Bash
systemctl stop 1panel

cd /opt/1panel/db

cp -a 1Panel.db 1Panel.db.bak.$(date +%F_%H%M%S)

cp -a /root/1Panel.merged.db 1Panel.db
chown root:root 1Panel.db
chmod 600 1Panel.db

rm -f 1Panel.db-wal 1Panel.db-shm

systemctl start 1panel

补充说明:架构原理

为何会出现“网站可访问但列表消失”的情况? 这是因为我们迁移的是 OpenResty/Nginx 的底层运行配置(conf.d/*.conf)及网站静态文件。Nginx 直接读取这些配置文件提供服务,不依赖面板数据库。 而 1Panel 的 UI 列表、SSL 证书管理、DNS 账号及 ACME 申请记录等,储存于 SQLite 数据库中。面板不会反向解析 Nginx 配置文件来重建数据库记录,从而导致出现“服务正常运行、面板及元数据丢失”的状态。

注意事项与常见问题

  1. 替换 DB 文件前必须停止 1Panel 服务,并务必删除1Panel.db-wal1Panel.db-shm文件,否则 SQLite 可能会从日志文件中恢复旧状态。
  2. 如果同时迁移了 OpenResty 的应用目录,建议先在新面板中执行一次 OpenResty 的安装流程,让面板在数据库中初始化应用记录,然后再覆盖conf/www/1pwaf等运行数据。