宝塔面板每次点"保存"都在重写你的 vhost:用 extension 目录挂配置,顺手排掉 cron 的 PATH 雷和面板自己的 300MB
在 VPS 上跑宝塔,最容易踩的其实不是装什么组件,而是你改的东西什么时候被面板悄悄覆盖。下面这几个现场都是我自己或客户环境里实际踩过的,不是理论推演。
一、先验证:vhost 到底是不是你的
随便找一个站点,记下 md5,然后去面板点一下"保存"(改伪静态、加域名、切 PHP 版本、续签 SSL 任意一个动作都行),再算一次。
md5sum /www/server/panel/vhost/nginx/example.com.conf
# 面板点一下保存
md5sum /www/server/panel/vhost/nginx/example.com.conf
两次不一样,甚至文件大小都变了。原因是宝塔的站点配置不是"编辑",是 panelSite.py 里的模板字符串整体重生成——你的 location ^~ /api/、你的 client_max_body_size、你手写的 add_header,全都没了。
网上流行说"改 /www/server/panel/data/ 下的模板",那玩意在 Python 源码里,升级一次面板全打回原形,别干这种事。
二、extension 目录才是那个不被吞的口子
宝塔 7.7 之后在生成的 server 块里插了一行 include,指向按域名分目录的 extension。老站点可能没这行,先确认:
/www/server/nginx/sbin/nginx -T 2>/dev/null \
| awk '/server_name example\.com/,/^}/' | grep -n 'include'
看到类似这行就说明口子是通的:
include /www/server/panel/vhost/nginx/extension/example.com/*.conf;
没有的话,在面板里随便保存一次站点让它补上(新版本都会生成)。然后按域名建目录:
mkdir -p /www/server/panel/vhost/nginx/extension/example.com
# 目录名必须和 vhost 文件名一致;绑了多域名时用的是主域名
这里第一个坑:include 位置在 server 块末尾,意味着你写的正则 location 永远抢不过前面 enable-php-74.conf 里的 location ~ [^/]\.php(/|$)。nginx 的正则 location 是按配置文件出现顺序取的,第一个匹配就停。所以 extension 里写 location ~ \.php$ 是不生效的,属于"改了、没报错、也不管用"。
正确的姿势是用优先级更高的匹配方式。给导出接口单独放开超时和上传体积:
# /www/server/panel/vhost/nginx/extension/example.com/export.conf
location ^~ /api/export/ {
proxy_pass http://127.0.0.1:8080;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
proxy_request_buffering off;
client_max_body_size 512m;
client_body_temp_path /www/wwwroot/.tmp 1 2;
}
location ^~ /private/ {
deny all;
}
^~ 是前缀匹配且命中后跳过正则,所以能盖住宝塔默认那套。想动 PHP 的 fastcgi 参数,就得在 extension 里自己写完整的 location(含 fastcgi_pass unix:/tmp/php-cgi-74.sock; 和 include fastcgi.conf;),不能去 include enable-php-74.conf,那是个完整的 location 块,塞不进另一个 location 里。
extensions 里的语法错,会静默扩散到全站
宝塔保存站点的流程是:写文件 → nginx -t → 成功才 reload,失败回滚。关键点:它只回滚自己刚写的那一个 vhost 文件,绝不会帮你回滚你在 extension 里新加的 conf。
结果就是:extension 里多打一个分号,之后你在面板上做的所有站点操作全部失败,站点看起来"运行中",但改动一律不生效,报错也只在面板弹出的那一瞬间。老手第一反应应该是:
/www/server/nginx/sbin/nginx -t
它会直接告诉你是哪个文件第几行。别翻面板日志,没用的。
另外提醒一句,/www/server/nginx/conf/nginx.conf(http 块)不会被自动重写,limit_req_zone、map、log_format 这些只能写在这里。改之前 cp nginx.conf nginx.conf.bak.$(date +%F),面板的"配置修改"页面是手动保存的,相对安全,但别手滑点恢复默认。
三、计划任务里 node/ffmpeg 全失踪,是那 6 条 PATH 干的
宝塔每个计划任务会在 /www/server/cron/ 下生成一个 hash 文件,文件头固定这么写:
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
就这 6 条。你在 SSH 里敲 node -v 好好的,是因为 ~/.bashrc 里挂了 nvm 或者 /www/server/nodejs/v16.x/bin。cron 里不读 bashrc,直接 command not found,日志里一行 node: command not found,很多人以为是任务没触发。
# 直接看宝塔生成的真实环境
head -5 /www/server/cron/$(ls -t /www/server/cron | grep -v '\.log$' | head -1)
修法有两种,选省事的:在面板"任务脚本"里第一行自己补 PATH(脚本内容会被原样拼进生成的文件,位置在 PATH 那行之后,能生效):
export PATH=/www/server/nodejs/v16.20.2/bin:/www/server/php/74/bin:$PATH
# 顺手加个互斥,防止上一轮没跑完就叠上来
exec 9>/tmp/sync_job.lock
flock -n 9 || exit 0
cd /www/wwwroot/example.com
node ./scripts/sync.js >> /www/wwwlogs/sync.log 2>&1
flock 这段强烈建议加。宝塔的计划任务默认没有重叠保护,一个跑 5 分钟的任务挂在 */1 上,三分钟就给你叠出三个进程抢同一份文件,最后表现为"数据偶尔错乱",排查方向还容易被带到数据库上。
四、顺手一个:.user.ini 你 rm 不掉
站点根目录的 .user.ini 里锁着 open_basedir,是 chattr +i 的。cron 里跑的 PHP 脚本要读写站点目录以外的路径,就是被它拦的,报错还特别含糊。
lsattr /www/wwwroot/example.com/.user.ini
# ----i---------e---- /www/wwwroot/example.com/.user.ini
chattr -i /www/wwwroot/example.com/.user.ini
# 改完记得加回去,或者直接在面板站点设置里关掉"防跨站攻击"
chattr +i /www/wwwroot/example.com/.user.ini
五、1G 内存的 VPS 上,面板自己就是个大户
宝塔面板不止一个进程。除了 BT-Panel 主进程,还有个 BT-Task 常驻跑任务队列,装了防火墙、网站监控报表之后还会再多一两个。1C1G 的机器上两个 Python 进程稳在 120~200MB,加上 MySQL 5.7 按默认参数吃掉 300MB 朝上,PHP-FPM 一开,OOM-Killer 就开始挑人杀了。
ps -eo pid,rss,comm,args --sort=-rss | grep -Ei 'bt-|python|mysqld|php-fpm' | head -15
能砍的先砍:面板设置里的监控数据、消息推送、不用的插件全关;MySQL 的"性能调整"在 1G 机器上给的是 128M buffer pool,这个别嫌小,比被 OOM 杀掉强。
如果站点是正经跑业务的,我的建议是别在 1G 上硬挤——轻云互联的 KVM 实例磁盘是 NVMe 直通,MySQL 的 doublewrite 和 binlog 落在同一块盘上时 IOPS 不会成为瓶颈,配合独享核,宝塔的负载曲线也不会被 steal time 画出一堆假尖峰。这种场景 2G 起步,能省掉后面一半的排查时间。
六、一张检查清单
- 想加 nginx 配置 → 只写
/www/server/panel/vhost/nginx/extension/{主域名}/*.conf,永远别碰 vhost 本体 - 正则 location 在 extension 里抢不到位置 → 用
^~或= - 站点改动不生效 → 先
nginx -t,别翻面板日志 - 计划任务里找不到命令 → 看生成脚本那 6 条 PATH,自己 export
- 计划任务数据错乱 → 先查是不是重叠执行,加
flock - PHP 脚本被拦 →
lsattr看.user.ini的i位