这篇记录一下我自己实操的一套迁移流程:
内网有一套 WordPress(Docker),公网 VPS 上也跑着 Trojan-go + Caddy(梯子),我希望“梯子不坏”的前提下,把 WordPress 迁到公网,并用 v.nightfarer.one 访问。
最终我采用了比较稳的 方案A:导出 SQL + wp-content 打包,不用插件,迁移更可控。
最终架构(迁完就是这个形态)
访问链路大概是:
浏览器 → https://v.nightfarer.one:443 → Caddy(入口 TLS) → Nginx Proxy Manager(NPM 统一反代管理) → WordPress(Apache/PHP) → MySQL
关键点:
- Caddy 继续做入口(443/80),梯子逻辑不动大框架
- Trojan-go 让出 443,改到 8443(客户端端口也要改)
- NPM 只负责管理转发(更方便以后加站点/改反代),不强求它签证书
0)准备:先把变量记清楚(很重要)
下面这些名字你按自己机器上的实际情况替换(看 docker ps):
- 公网 Trojan/Caddy 容器:
trojan-trojan-1trojan-caddy-1
- 公网 WordPress 栈:
wp-stack-wordpress-1wp-stack-db-1wp-stack-npm-1
建议先在两台机器(内网旧服务器 / 公网新服务器)都跑一遍:
docker ps
确认容器名对得上。
1)公网 VPS:先把 Trojan 挪走(释放 443 给 Caddy)
因为 443 只能被一个东西占用。如果 Trojan 占着 443,Caddy 就没法做入口 TLS。
我的做法是:Trojan 映射到 8443:443(Trojan 容器内部仍然是 443,只是宿主机端口换成 8443)。
1.1 修改 Trojan 的 docker-compose.yml(公网服务器)
把 Trojan 的 ports 改成:
ports:
- "8443:443"
- "8443:443/udp"
然后重建:
cd ~/trojan
docker compose down
docker compose up -d
验证端口有没有符合预期:
ss -lntp | egrep ':(443|8443)\b'
- 看到
:8443有监听(docker-proxy)说明 Trojan 映射成功 :443后面应该留给 Caddy/反代入口用
2)DNS:v.nightfarer.one 指向公网 VPS
DNS(例如 Cloudflare)里把:
A v → 公网VPS_IP
然后验证解析是否生效:
dig v.nightfarer.one +short
3)公网 VPS:Caddy 入口反代到 NPM(别踩 127.0.0.1 的坑)
一个经典坑:容器内的 127.0.0.1 不是宿主机 127.0.0.1。
所以 Caddy 容器里写:
reverse_proxy 127.0.0.1:8080
大概率会翻车(除非你很明确用 host network 或特殊桥接)。
3.1 让 Caddy 能“看见” NPM(公网服务器)
我这里验证 Caddy 容器能解析 NPM 容器名:
docker exec -it trojan-caddy-1 sh -c "getent hosts wp-stack-npm-1 || ping -c 1 wp-stack-npm-1"
如果能输出 wp-stack-npm-1 的 IP,说明网络可达。
如果你这里解析不到,大概率是两个 compose 不在同一个 docker network。
最简单的补救方式之一是:docker network connect wp-stack_backend trojan-caddy-1
(把 Caddy 容器挂到 wp-stack 的网络里)
3.2 配 Caddyfile(公网服务器)
我用的示例(入口站点 v.nightfarer.one → NPM):
v.nightfarer.one {
reverse_proxy wp-stack-npm-1:80
}
重启 Caddy:
docker restart trojan-caddy-1
验证:
curl -I https://v.nightfarer.one
4)NPM:反代到 WordPress,并传递 HTTPS 头
在 NPM 面板里新建 Proxy Host:
- Domain Names:
v.nightfarer.one - Forward Hostname / IP:
wp-stack-wordpress-1 - Forward Port:
80 - Scheme:
http
4.1 强烈建议加 NPM Advanced(避免 WordPress 识别不到 HTTPS)
在 NPM 的 “Advanced / 自定义 Nginx 配置” 填:
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
解释一下:
WordPress 很依赖这些头来判断自己是不是在 HTTPS 下。没有的话就容易出现登录跳转、后台循环跳转等问题。
5)迁移方案A:导出 SQL + 打包 wp-content(最稳)
这个方案就是 WordPress “本体迁移”:
- **数据库:**导出
wordpress.sql - **内容:**打包
wp-content(主题、插件、上传文件都在里面)
5.1 内网旧服务器:导出数据库(MySQL 在 Docker 里)
如果你直接在宿主机跑:
mysqldump -u wpuser -p wordpress > wordpress.sql
很多时候会报 Access denied 或连不上(因为数据库不是 localhost,而是在容器网络里)。
正确姿势:进 MySQL 容器里 dump。
✅ 推荐(MySQL 8 常见权限坑:tablespaces):
docker exec wp-stack-db-1 \
mysqldump -u wpuser -pwppass --no-tablespaces wordpress \
> /root/wordpress.sql
ls -lh /root/wordpress.sql
如果你看到类似
PROCESS privilege报错,就是--no-tablespaces这个参数没加导致的。
5.2 内网旧服务器:打包 wp-content
wp-content 在 WordPress 容器里,一般路径是 /var/www/html/wp-content。
打包:
docker exec wp-stack-wordpress-1 \
tar -czf /tmp/wp-content.tar.gz -C /var/www/html wp-content
拷到宿主机:
docker cp wp-stack-wordpress-1:/tmp/wp-content.tar.gz /root/wp-content.tar.gz
ls -lh /root/wp-content.tar.gz
5.3 把文件从内网传到公网 VPS
我用 scp(你也可以 rsync):
scp /root/wordpress.sql /root/wp-content.tar.gz root@公网IP:/root/
5.4 公网 VPS:导入数据库(重点:别让 < 在宿主机被解析)
先把 SQL 放进 MySQL 容器:
docker cp /root/wordpress.sql wp-stack-db-1:/tmp/wordpress.sql
docker exec -it wp-stack-db-1 ls -lh /tmp/wordpress.sql
然后导入(✅关键写法):
docker exec -i wp-stack-db-1 sh -c \
"mysql -u wpuser -pwppass wordpress < /tmp/wordpress.sql"
为什么要这样写?
因为如果你写成:docker exec -it wp-stack-db-1 mysql ... < /tmp/wordpress.sql
那个< /tmp/wordpress.sql会被 宿主机 shell 先解析,容器里根本读不到,就会出现 “No such file”。
验证导入是否成功(随便查个数量):
docker exec -it wp-stack-db-1 mysql -u wpuser -pwppass wordpress -e \
"SELECT COUNT(*) AS posts FROM wp_posts;"
5.5 公网 VPS:恢复 wp-content
把压缩包丢进 WordPress 容器:
docker cp /root/wp-content.tar.gz wp-stack-wordpress-1:/tmp/wp-content.tar.gz
解压覆盖(注意先删旧 wp-content):
docker exec -it wp-stack-wordpress-1 sh -c \
"cd /var/www/html && rm -rf wp-content && tar -xzf /tmp/wp-content.tar.gz"
修权限(不修的话,后台可能无法上传/装插件):
docker exec -it wp-stack-wordpress-1 \
chown -R www-data:www-data /var/www/html/wp-content
重启:
docker restart wp-stack-wordpress-1
6)确认域名(home / siteurl)正确
虽然我最终内外网都用同域名 v.nightfarer.one,但迁移后依然建议确认一下。
docker exec -it wp-stack-db-1 mysql -u wpuser -pwppass wordpress -e \
"SELECT option_name, option_value FROM wp_options WHERE option_name IN ('home','siteurl');"
必要时更新:
docker exec -it wp-stack-db-1 mysql -u wpuser -pwppass wordpress -e \
"UPDATE wp_options SET option_value='https://v.nightfarer.one' WHERE option_name IN ('home','siteurl');"
7)检查上传大小限制(我这里已经到 256MB)
后台一般会显示 “最大上传文件大小:xxx MB”。
更“底层”的方式(看 PHP 配置):
docker exec -it wp-stack-wordpress-1 php -i | egrep \
'upload_max_filesize|post_max_size|memory_limit|max_execution_time|max_input_time'
如果你想改得更大(例如 512MB),一般要通过挂载自定义 php ini 文件来做(不建议在容器里手改)。
8)常见坑(我踩过的,顺手写一下)
- 坑1:容器里写 127.0.0.1 反代宿主机
基本不行,除非你用 host network 或做特殊桥接。
更稳的是用 容器名:端口,前提是两个容器在同一个 Docker network。 - 坑2:NPM 申请证书失败(HTTP-01)
因为 80/443 并不在 NPM 手里(入口在 Caddy),Let’s Encrypt 验证自然失败。
我这里的策略是:证书交给 Caddy 自动签,NPM 只做内部转发。 - 坑3:导入 SQL 用
<会报文件找不到
记住:<会在宿主机先解析。正确姿势是:docker exec -i ... sh -c "mysql ... < /tmp/file.sql"
结尾
迁完之后,我这边表现是:
- 首页内容正常
/wp-admin/正常登录- 上传限制显示 256MB(也能从 PHP 配置验证)
整个流程属于“尽量少动梯子、稳稳把 WordPress 搬过去”的方式,后面要再挂新站点也很轻松:基本就是 NPM 里加一个 Proxy Host、Caddy 入口加一个站点块。
Comments NOTHING