关键是以符号链接驱动版本目录切换:root 指向稳定软链(如 /srv/app/current),该软链再动态指向具体版本目录(如 v20260820);升级时仅需 ln -sf 切换软链并 nginx -s reload,不重启进程、用户无感知,且须注意 apparmor 对 /srv/app/ 等非默认路径的访问限制。

在 Nginx 版本升级过程中,root 指令本身不参与升级切换,它只是静态声明资源根路径。真正实现“无缝目录切换”的关键,是把 静态资源所在目录本身版本化 + 符号链接驱动,让 root 指向一个稳定不变的软链路径(如 /srv/app/current),而该软链再动态指向具体版本目录(如 v20260815 或 v20260820)。这样升级时只需切换软链目标并重载配置,Nginx 读取的 root 路径没变,但实际服务的文件已更新。
用符号链接管理静态资源目录结构
不要把所有前端构建产物直接放在 /srv/app/ 下硬覆盖,而是按发布版本隔离存放:
-
/srv/app/v20260815/—— 上一版构建产物(含 index.html、js/、css/ 等) -
/srv/app/v20260820/—— 新版本构建产物(全新生成,不修改旧目录) -
/srv/app/current → v20260820—— 指向当前生效版本的软链接(由 Nginx 的 root 引用)
对应 Nginx 配置中写成:
location / {<br> root /srv/app/current;<br> index index.html;<br>}
升级时只需切换软链并重载,不重启进程
新版本部署完成后,执行两步即可完成切换:
-
ln -sf v20260820 /srv/app/current—— 原子性更新软链接目标 -
nginx -s reload—— 仅重载配置,worker 进程继续运行,已有连接不受影响
整个过程耗时远低于 1 秒,用户无感知,也无需停止 Nginx 主进程或等待 graceful shutdown。
注意系统级访问限制(尤其 Ubuntu)
Ubuntu 默认启用 AppArmor,其 nginx profile 通常只允许访问 /var/www/** 和 /usr/share/nginx/**。若将资源放在 /srv/app/,即使软链正确、权限合理,也会因 AppArmor 静默拦截导致 403 错误。
- 检查日志:
sudo grep nginx /var/log/syslog | grep DENIED - 临时放行(测试用):
sudo aa-complain /usr/sbin/nginx - 生产环境应定制 profile,添加
/srv/app/** r,等规则
配合 map 实现多环境 root 切换(可选增强)
如果需在同一套 Nginx 中支持 dev/staging/prod 多环境,可在全局用 map 根据请求头或域名映射路径变量,再在 location 中引用:
map $http_x_env $env_root { default "/srv/app/current"; "dev" "/srv/app/dev"; "staging" "/srv/app/staging"; }location / { root $env_root; }
此时 root 的值仍是静态解析的变量,不违反 Nginx 语法限制,又实现了按需切换底层目录的能力。











