Nginx配置文件不解析${VAR}环境变量占位符,需通过envsubst模板替换、挂载预渲染配置或自定义镜像在启动前完成变量注入,reload无法更新环境变量。

直接说结论:Nginx 容器本身不支持在运行时用 env 指令动态注入环境变量到配置中——它不是 Shell,env 是 Docker 的运行时参数,不是 Nginx 配置语法。真想透传系统变量(比如 API 地址、Feature Flag),得靠模板替换或启动脚本,否则配置里写 ${API_URL} 会原样发给浏览器,根本不会被解析。
为什么 nginx.conf 里的 ${VAR} 默认不生效
Nginx 配置文件本身不解析 Shell 风格的环境变量占位符。即使你用 docker run -e API_URL=https://api.example.com 启动容器,nginx.conf 中的 proxy_pass http://${API_URL}/v1; 会被当作字面量处理,最终导致 502 或 DNS 解析失败。
- Nginx 在启动时只做一次配置加载,不执行 Shell 替换
-
env指令在 Nginx 配置中仅用于“导出变量供子进程使用”,和配置内容无关 - 官方镜像的
nginx:alpine和nginx:latest都不带envsubst工具,默认无法做模板渲染
三种真正可用的 env 透传方案对比
必须明确场景选方法:是只想改 proxy_pass,还是也要控制缓存策略、日志路径、甚至整个 server 块?
-
轻量级方案(推荐多数前端项目):用
envsubst+ 启动脚本。先在nginx.conf.template写proxy_pass http://${BACKEND_URL};,再通过ENTRYPOINT ["/docker-entrypoint.sh"]调用envsubst /etc/nginx/nginx.conf && exec "$@" -
配置即代码方案:用
docker run -v $(pwd)/conf.d:/etc/nginx/conf.d:ro挂载已渲染好的配置。适合 CI 流程中用sed或gomplate提前替换,避免容器内依赖额外工具 -
高阶可控方案:基于
nginx:alpine自定义镜像,RUN apk add --no-cache gettext安装envsubst,再把模板逻辑固化进Dockerfile。适合多环境共用同一镜像但配置差异大的场景
容易踩的坑:PATH、权限、变量名大小写
哪怕用了 envsubst,也常因细节翻车:
-
envsubst只替换大写变量(如${API_URL}),${api_url}不生效;且必须是 export 过的变量,docker run -e api_url=...无效 - 挂载配置文件时,宿主机文件权限若为 root:root 且 mode 600,Nginx worker 进程(默认
nginx用户)可能读不到,报open() "/etc/nginx/nginx.conf" failed (13: Permission denied) - Alpine 镜像里
envsubst在/usr/bin/envsubst,Debian 系在/usr/bin/envsubst,但路径别硬编码——用which envsubst或直接依赖gettext包名更稳 - 变量值含特殊字符(如
/、:、$)未转义,会导致envsubst解析中断,建议值里避免裸 URL,改用API_HOST+API_PORT拆分
最常被忽略的一点:Nginx reload 不会重新读取环境变量,哪怕你 docker exec -it nginx-container sh -c 'export NEW_VAR=xxx && nginx -s reload',新变量对已加载的配置毫无影响——变量透传必须发生在 Nginx 主进程启动前。


















