You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Dockerfile+Github Actions部署至Nginx时端口占用问题排查

问题:Docker部署时端口80被占用,无法与Nginx共存

我正尝试通过Nginx,使用Dockerfile和Github Actions进行应用部署,却遇到如下错误:

Error starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use.

停止服务器上的Nginx后即可部署,但我需要Nginx保持运行状态,请问我哪里操作有误?


我的Dockerfile内容

FROM node:20-alpine

WORKDIR /app

COPY package*.json yarn.lock ./

RUN yarn

COPY . .

EXPOSE 8081

CMD [ "yarn", "start" ]

我的Github Actions文件内容

name: Deploy to DigitalOcean

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4.0.0

      - name: Set up Docker
        uses: docker/setup-buildx-action@v2.10.0

      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and push image to Docker Hub
        uses: docker/build-push-action@v4.2.1
        with:
          context: .
          push: true
          tags: ${{ secrets.DOCKERHUB_USERNAME }}/example:${{ github.sha }}
          build-args: |
            NODE_VERSION=20

      - name: Deploy to DigitalOcean Droplet
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.DROPLET_IP }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          port: 22
          script: |
            echo "Pulling Docker image..."
            docker pull ${{ secrets.DOCKERHUB_USERNAME }}/example:${{ github.sha }}

            echo "Stopping and removing existing container (if it exists)..."
            if docker ps -a | grep -q "example-container"; then
              docker stop example-container
              docker rm example-container
            fi

            echo "Starting a new container..."
            docker run -d --name example-container -p 80:8081 ${{ secrets.DOCKERHUB_USERNAME }}/example:${{ github.sha }}

我的Nginx配置文件(/etc/nginx/sites-available/example.com)内容

server {
        root /var/www/example.com/html;
        index index.html index.htm index.nginx-debian.html;
        server_name example.com www.example.com;
        location / {
                try_files $uri $uri/ =404;
                proxy_pass http://localhost:8081;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
        }
    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80;
        listen [::]:80;

        server_name example.com www.example.com;
    return 404; # managed by Certbot
}

问题原因及解决方案

问题原因

你在Docker启动命令里把容器的8081端口映射到了主机的80端口(-p 80:8081),但主机上的Nginx已经在监听80端口处理HTTP请求(第二个server块明确配置了listen 80),两者端口冲突导致Docker启动失败。

解决方案

方案1:修改Docker端口映射,避开80端口

把Docker的端口映射改成主机的其他空闲端口,比如8080,再调整Nginx的反向代理指向这个新端口:

  1. 更新Github Actions里的Docker启动命令:
    docker run -d --name example-container -p 8080:8081 ${{ secrets.DOCKERHUB_USERNAME }}/example:${{ github.sha }}
    
  2. 修改Nginx配置中的proxy_pass:
    proxy_pass http://localhost:8080;
    
  3. 重启Nginx生效:
    sudo systemctl restart nginx
    

方案2:Docker容器与Nginx同网络,用容器名直接通信(推荐)

这种方式不需要暴露主机端口,安全性更高:

  1. 创建一个Docker网络:
    docker network create nginx-network
    
  2. 修改Docker启动命令,加入网络并去掉主机端口映射:
    docker run -d --name example-container --network nginx-network ${{ secrets.DOCKERHUB_USERNAME }}/example:${{ github.sha }}
    
  3. 修改Nginx配置中的proxy_pass,直接用容器名访问:
    proxy_pass http://example-container:8081;
    
  4. 重启Nginx和容器:
    sudo systemctl restart nginx
    docker restart example-container
    

额外注意事项

你当前Nginx配置里try_files $uri $uri/ =404;在proxy_pass之前,会优先从本地/var/www/example.com/html读取静态文件。如果你的应用静态文件都在Docker容器里,需要删除root和try_files相关配置,或者调整顺序让代理优先。


内容的提问来源于stack exchange,提问作者kar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 22:24:51