Skip to content

Docker 部署 NGINX:配置静态站点与反向代理 ​

一台主机上既要托管静态页面,又要把 /api 请求转给应用服务,还要在改配置时尽量不打断已有连接。NGINX 把这些入口统一放进声明式配置;官方 Docker 镜像再把安装、进程前台运行和跨架构分发交给容器完成。

这篇指南选择 nginx:1.31.6 作为示例版本,完成一个本机可访问的静态站点,然后把同一份 server 配置扩展为反向代理。配置文件、站点内容和容器生命周期都由宿主机命令控制,适合先在开发机或单机服务上建立可重复的运行方式。

项目来源与版本信息 ​

信息内容
项目标题NGINX
项目描述NGINX 是面向 HTTP、HTTPS、邮件协议的开源 Web 服务器、反向代理、负载均衡器、API 网关和内容缓存。
GitHub 仓库nginx/nginx
官网地址nginx.org
主要开发语言C 97.74%、Vim Script 1.63%、XS 0.38%
开源许可证BSD-2-Clause
最近代码更新2026-09-30(GitHub pushed_at,核对日期:2026-10-10)

先确定采用方式 ​

NGINX 源码仓库负责软件本身;官方 Docker 镜像由 nginx/docker-nginx 维护,并发布到 Docker Hub 的 nginx Official Image。镜像默认把 NGINX 以前台方式运行,容器停止时进程状态也会被 Docker 记录下来。静态文件放在 /usr/share/nginx/html,主配置位于 /etc/nginx/nginx.conf,conf.d/*.conf 会被主配置包含。

下面这条主路径适合单机或开发环境:

项目选择
镜像nginx:1.31.6(mainline,固定 tag)
备选版本nginx:1.30.5(stable)
方法image-run,单容器运行
支持架构官方列表包含 amd64、arm64v8、arm32v7、ppc64le、riscv64、s390x 等
宿主机入口示例绑定 127.0.0.1:8080,不会直接暴露公网
数据位置宿主机 site/ 和 conf.d/;删除目录会删除对应内容

镜像 tag 解决的是软件版本复现,不代表已经替你完成 TLS、访问控制、日志留存或多机高可用。digest 才是不可变的镜像内容标识,严格复现时应在拉取后记录 docker image inspect 的 RepoDigest。公网入口应再放在防火墙和 TLS 反向代理之后,并为镜像更新保留回滚 tag 或 digest。

NGINX 配置的三个层次 ​

NGINX 配置由指令和上下文组成。http 包含 server,server 再包含 location;请求先按监听端口和主机名选中 server,再按 URI 选择匹配的 location。root 决定静态文件在容器内的路径,proxy_pass 则把请求转到上游服务。官方 Beginner's Guide给出了这套层级和重载语义。

Mermaid 流程图
查看源码
flowchart LR
    Client[浏览器或 API 客户端] --> Port[宿主机 127.0.0.1:8080]
    Port --> Nginx[NGINX 容器 :80]
    Nginx --> Server[server 按端口和域名选择]
    Server --> Static[location /\n/usr/share/nginx/html]
    Server --> Proxy[location /api/\nproxy_pass 上游应用]
    Static --> Files[(宿主机 site/)]
    Proxy --> App[应用服务]

这意味着“改了文件”与“改了路由”是两类操作:替换 site/index.html 通常马上能读到新内容;改动 NGINX 指令则必须先用 nginx -t 检查,再发送 reload 信号。语法检查失败时,旧 worker 继续提供服务,不能把未检查的配置直接覆盖到线上。

用 Docker 启动第一个站点 ​

执行者需要准备 Docker Engine 或 Docker Desktop、Bash 和 curl,Compose 在本篇主路径中不是必需项。以下命令在一个新的工作目录执行;这些命令不会下载或执行 NGINX 源码,只拉取官方镜像。

创建内容和配置 ​

bash
mkdir -p nginx-lab/site nginx-lab/conf.d
cd nginx-lab

cat > site/index.html <<'HTML'
<!doctype html>
<html lang="zh-CN">
  <meta charset="utf-8">
  <title>NGINX in Docker</title>
  <h1>NGINX is serving this page</h1>
HTML

cat > conf.d/default.conf <<'NGINX'
server {
    listen 80;
    server_name _;

    root /usr/share/nginx/html;
    index index.html;

    location = /healthz {
        default_type text/plain;
        return 200 "ok\n";
    }

    location / {
        try_files $uri $uri/ =404;
    }
}
NGINX

conf.d/default.conf只定义虚拟主机,不覆盖镜像内的主配置;挂载整个 /etc/nginx/conf.d 后,容器只会读取工作目录里的这一份站点配置。healthz 是一个应用无关的 HTTP 探针,便于脚本检查 NGINX 是否已经能够处理请求。

运行并验证 ​

bash
docker pull nginx:1.31.6
docker run -d \
  --name nginx-lab \
  --restart unless-stopped \
  -p 127.0.0.1:8080:80 \
  -v "$PWD/site:/usr/share/nginx/html:ro" \
  -v "$PWD/conf.d:/etc/nginx/conf.d:ro" \
  nginx:1.31.6

docker ps --filter name=nginx-lab
docker exec nginx-lab nginx -t
curl --fail http://127.0.0.1:8080/healthz
curl --fail http://127.0.0.1:8080/
docker logs --tail=50 nginx-lab

成功信号是容器状态为 Up、nginx -t 输出 syntax is ok 和 test is successful,健康检查返回 ok,首页包含 NGINX is serving this page。如果宿主机的 8080 已被占用,把左侧端口改成其他端口,例如 18080:80,同时修改后续 curl 地址;容器内仍监听 80。

配置生效和优雅重载 ​

先编辑 conf.d/default.conf,例如把 return 200 "ok\n"; 改成 return 200 "ready\n";,然后在工作目录执行:

bash
docker exec nginx-lab nginx -t
docker exec nginx-lab nginx -s reload
curl --fail http://127.0.0.1:8080/healthz

只有第一条命令成功才执行 reload。NGINX master 进程会先检查新配置,再让旧 worker 完成正在处理的请求;检查失败时旧配置继续工作。若容器已经退出,先看 docker logs nginx-lab,不要对一个未运行的容器反复发送 reload。

让 NGINX 反向代理应用 ​

静态站点之外,最常见的使用方式是让 NGINX 对外提供统一入口,把 /api/ 转给后端应用。下面的配置假设应用和 NGINX 在同一个 Docker 网络中,并且应用服务名为 app、监听 3000 端口;在 Compose 中服务名就是可解析的 DNS 名称。

将 conf.d/default.conf 的 server 改为:

nginx
server {
    listen 80;
    server_name _;

    location = /healthz {
        default_type text/plain;
        return 200 "ok\n";
    }

    location / {
        root /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://app:3000;
        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 $scheme;
    }
}

配置中的 proxy_pass http://app:3000; 会保留 /api/ 前缀;如果后端只接受去掉前缀的路径,应用路由要求决定 proxy_pass 的写法,不能只复制这一行。Linux 上若后端运行在宿主机而不是 Docker 网络,Docker DNS 不会自动解析 app;可以把应用加入同一网络,或显式配置 host-gateway。127.0.0.1 在容器内指向 NGINX 容器本身,不是宿主机。

应用网络可用 Compose 管理,但 NGINX 主路径仍然是同一个官方镜像。一个最小的网络片段如下,后端服务的镜像和端口必须替换成真实值:

yaml
services:
  nginx:
    image: nginx:1.31.6
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - ./site:/usr/share/nginx/html:ro
      - ./conf.d:/etc/nginx/conf.d:ro
    depends_on:
      - app

  app:
    image: your-app:version
    expose:
      - "3000"

启动后用 curl http://127.0.0.1:8080/api/health 检查真实后端响应。depends_on 只控制启动顺序,不等于应用已经健康;长期运行应为后端增加 healthcheck,并在 NGINX 日志中核对上游连接错误。

配置环境变量模板 ​

官方镜像入口会处理 /etc/nginx/templates/*.template,把变量替换后的文件写入 /etc/nginx/conf.d。需要在不同环境切换端口等非敏感值时,可以挂载模板目录;上游地址仍要满足同一 Docker 网络中的名称解析条件:

nginx
server {
    listen ${NGINX_PORT};
    server_name _;

    root /usr/share/nginx/html;
    location / {
        try_files $uri $uri/ =404;
    }
}

保存为 templates/default.conf.template。这个模板只依赖挂载的静态目录,因此可以独立验证;需要把 /api/ 转给上游时,沿用前一节的 proxy_pass 配置,并把 NGINX 和应用放进同一个 Docker 网络。

bash
docker run -d \
  --name nginx-template \
  -p 127.0.0.1:18080:80 \
  -v "$PWD/site:/usr/share/nginx/html:ro" \
  -v "$PWD/templates:/etc/nginx/templates:ro" \
  -e NGINX_PORT=80 \
  nginx:1.31.6
docker exec nginx-template nginx -t
curl --fail http://127.0.0.1:18080/

成功信号是 nginx -t 报告语法检查通过且首页可访问。模板替换适合非敏感的地址和端口。不要把证书私钥、数据库密码或访问令牌放进环境变量模板;容器环境变量可被有 Docker 管理权限的用户读取,敏感值应交给专用 secret 机制或受限的只读挂载。

日常维护、升级和清理 ​

日志和状态 ​

bash
docker ps --filter name=nginx-lab
docker logs --since=10m nginx-lab
docker inspect --format '{{.State.Status}} {{.Config.Image}}' nginx-lab

官方镜像将访问和错误日志导向容器标准输出,通常直接用 docker logs 收集;如果自定义 access_log 或 error_log 到文件,必须另外挂载目录并制定轮转策略。日志能说明 NGINX 是否收到请求,不能替代上游应用的业务指标。

固定版本升级和回退 ​

升级前先检查 NGINX CHANGES 与目标镜像的 Dockerfile,记录当前镜像 tag 或 digest,并在测试环境执行 nginx -t、静态页面和上游接口验收。把 NEW_IMAGE 替换成目标 tag 或 digest;下面的切换会短暂停止旧容器,并保留旧容器名用于失败回退:

bash
set -euo pipefail
OLD_IMAGE=nginx:1.31.6
NEW_IMAGE="${NEW_IMAGE:-nginx:1.31.6}"
test "$NEW_IMAGE" != "$OLD_IMAGE" || { echo 'set NEW_IMAGE to a newer approved tag or digest'; exit 1; }
docker pull "$NEW_IMAGE"
docker inspect --format '{{.Config.Image}}' nginx-lab
docker stop nginx-lab
docker rename nginx-lab nginx-lab-old
docker run -d \
  --name nginx-lab \
  --restart unless-stopped \
  -p 127.0.0.1:8080:80 \
  -v "$PWD/site:/usr/share/nginx/html:ro" \
  -v "$PWD/conf.d:/etc/nginx/conf.d:ro" \
  "$NEW_IMAGE"
docker exec nginx-lab nginx -t
curl --fail http://127.0.0.1:8080/healthz
docker rm nginx-lab-old

如果新容器的 nginx -t 或 HTTP 验收失败,先删除新容器,再执行 docker rename nginx-lab-old nginx-lab && docker start nginx-lab,恢复旧容器。验收通过后再删除 nginx-lab-old;这组命令不会删除宿主机的 site/ 和 conf.d/。NGINX 的静态内容和配置位于绑定目录,删除容器不会备份这些目录。

备份和恢复绑定目录 ​

site/ 保存页面和静态资源,conf.d/ 保存路由配置;两者是本路径的恢复单元。先停掉容器或至少停止写入,再把目录打包到受限的备份位置:

bash
mkdir -p backups
tar -C "$PWD" -czf "backups/nginx-lab-$(date +%Y%m%d-%H%M%S).tgz" site conf.d

恢复前确认压缩包来源和目标工作目录。下面的命令会解压并覆盖同名文件,随后用配置检查和 HTTP 请求验收;备份中不存在的新增文件需要按业务判断是否清理:

bash
docker stop nginx-lab
tar -xzf backups/nginx-lab-YYYYMMDD-HHMMSS.tgz -C "$PWD"
docker start nginx-lab
docker exec nginx-lab nginx -t
curl --fail http://127.0.0.1:8080/healthz
curl --fail http://127.0.0.1:8080/

将 YYYYMMDD-HHMMSS 替换为实际备份文件名。备份不包含镜像层、Docker 网络或上游应用数据;恢复这些目录后仍需拉取目标镜像并确认上游服务可访问。

停止与卸载 ​

bash
docker stop nginx-lab
docker rm nginx-lab

这只删除容器,不删除 nginx-lab/site、nginx-lab/conf.d 或已经拉取的镜像。确认不再需要站点内容后,再由操作者单独删除工作目录;不要把 docker rm 描述为数据备份或恢复操作。

常见问题 ​

容器启动后马上退出。 先执行 docker logs nginx-lab,再运行 docker run --rm -v "$PWD/conf.d:/etc/nginx/conf.d:ro" nginx:1.31.6 nginx -t 检查挂载配置。常见原因是少了分号、server 或 location 花括号不匹配,或者上游主机名无法解析。

页面返回 403 或 404。 检查宿主机 site/index.html 是否存在,以及 root 是否仍指向 /usr/share/nginx/html。挂载整个 site 目录后,容器内原有默认欢迎页会被遮住;这是预期行为。

改完配置没有变化。 先执行 docker exec nginx-lab nginx -t,再执行 docker exec nginx-lab nginx -s reload。如果修改的是挂载目录之外的文件,容器不会自动看到;如果容器被重建,确认新的 docker run 仍挂载了同一目录。

反向代理出现 host not found in upstream。 proxy_pass 中的名称必须是同一 Docker 网络里的服务名或可解析 DNS 名。容器内的 localhost 只代表 NGINX 自己;后端在宿主机时要使用平台提供的宿主机地址映射,或把两个服务加入同一网络。

是否可以直接公开 8080 端口? 这篇示例绑定到 127.0.0.1,适合本机验证。公开服务应改为明确的防火墙策略,在 TLS 终止点配置证书、限制管理接口,并检查 Host、转发头和上游访问控制;不能把 HTTP 健康探针当成认证。

部署验证 ​

本指南只完成了静态来源核对和命令级验收设计,没有在当前环境启动 NGINX 容器。实际部署时应依次观察容器保持 Up、docker exec nginx-lab nginx -t 输出 syntax is ok 与 test is successful、/healthz 返回 ok、首页包含示例文本,并在反向代理场景确认上游响应和 NGINX 日志。配置重载、升级和恢复都要重复这组检查;这些结果不能由文档阅读代替。

总结 ​

NGINX 的 Docker 主路径适合把站点内容和 conf.d 配置放在宿主机,用固定镜像启动容器,再通过 nginx -t 和优雅 reload 控制变更。公开服务还需要 TLS、访问控制、日志轮转和可回退的镜像版本;绑定目录是站点与配置的恢复单元,不能把删除容器当成备份。

参考资料 ​

全部公开文章由同一个站点构建和发布。