Docker构建后Next.js图片优化器ECONNREFUSED错误排查与解决
Docker部署Next.js后Image组件加载后端图片报ECONNREFUSED错误的解决方法
问题概述
我开发的Next.js应用部署在Docker容器中,后端服务运行在9000端口,前端在3000端口。Avatar标签能正常加载后端的图片,但Next.js的Image组件无法显示图片,返回500内部服务器错误。
错误信息
GET http://localhost:3000/_next/image?url=http%3A%2F%2F127.0.0.1%3A9000%2Fuploads%2Fpublic%2Fbanners%2F1725328558480-LoneSamuraiSekiroWallpapers.jpg&w=1920&q=75 500 (Internal Server Error) GET http://localhost:3000/_next/image?url=http%3A%2F%2F127.0.0.1%3A9000%2Fuploads%2Fpublic%2Fprofile-pictures%2F1725328558477-SamuraAndRaven.jpg&w=1920&q=75 500 (Internal Server Error) TypeError: fetch failed at node:internal/deps/undici/undici:13179:13 at process.processTicksAndRejections (node:internal/process/task_queues:105:5) at async fetchExternalImage (/app/node_modules/next/dist/server/image-optimizer.js:580:17) ... [cause]: Error: connect ECONNREFUSED 127.0.0.1:9000
当前配置
next.config.js
/** @type {import('next').NextConfig} */ const nextConfig = { images: { remotePatterns: [ { protocol: "http", hostname: "127.0.0.1", port: "9000", pathname: "/uploads/**", }, ], }, }; export default nextConfig;
Image组件使用方式
<Image src={encodeURI(baseUrl + banner)} alt={`${name}'s banner`} objectFit="cover" layout="fill" />
Docker-compose.yml
version: "3.8" services: db: image: postgres:14-alpine container_name: postgres-freetube-container ports: - "5432:5432" # Expose PostgreSQL port environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: freetube volumes: - db_data:/var/lib/postgresql/data networks: - freetube_network backend: build: context: ./backend dockerfile: Dockerfile ports: - "9000:9000" # Expose the backend port environment: NODE_ENV: production DATABASE_URL: "postgresql://user:password@db:5432/freetube?schema=public" # Use the db service name as the host PORT: 9000 depends_on: - db command: > sh -c "npx prisma migrate deploy && npm start" networks: - freetube_network frontend: build: context: ./freetube dockerfile: Dockerfile ports: - "3000:3000" # Expose the frontend port environment: NEXT_PUBLIC_API_BASE_URL: "http://backend:9000/api/" # Reference the backend service NEXT_PUBLIC_BASE_URL: "http://backend:9000/" depends_on: - backend networks: - freetube_network # env_file: # - .env # Ensure the .env file is correctly referenced volumes: db_data: networks: freetube_network: driver: bridge
补充信息
- 直接在浏览器访问图片URL可以正常加载;
- Next.js图片优化器尝试从后端获取图片时抛出ECONNREFUSED错误;
- 前端和后端都运行在Docker容器中。
已尝试操作
- 确认后端服务在9000端口正常运行;
- 验证图片可在浏览器中正常加载。
解决方案
问题核心是Docker容器间的网络访问逻辑:
- Next.js的Image组件在服务端渲染时,会由Node.js服务去请求图片资源。此时前端容器内的
127.0.0.1指向容器自身,而非宿主机,所以无法访问到后端容器。 - 而Avatar标签是客户端直接请求图片,浏览器用宿主机的
127.0.0.1:9000可以访问到映射的后端端口,所以正常。
步骤1:修改next.config.js的图片远程配置
把remotePatterns中的hostname改成Docker-compose里的后端服务名backend,因为同一Docker网络内可以通过服务名直接访问:
/** @type {import('next').NextConfig} */ const nextConfig = { images: { remotePatterns: [ { protocol: "http", hostname: "backend", // 替换为服务名 port: "9000", pathname: "/uploads/**", }, ], }, }; export default nextConfig;
步骤2:区分服务端与客户端的图片baseUrl
在代码中判断运行环境,服务端使用容器内的服务地址,客户端使用宿主机地址:
// 定义baseUrl,区分服务端和客户端 const baseUrl = typeof window === 'undefined' ? 'http://backend:9000' // 服务端渲染时用容器服务名访问 : 'http://localhost:9000'; // 客户端用宿主机地址 // Image组件使用 <Image src={encodeURI(baseUrl + banner)} alt={`${name}'s banner`} objectFit="cover" layout="fill" />
步骤3:重启容器
修改配置后,重新构建并启动Docker容器:
docker-compose down docker-compose up --build
这样调整后,Next.js服务端渲染时会通过Docker网络访问后端容器获取图片,客户端则通过宿主机端口访问,两者都能正常加载图片,解决ECONNREFUSED错误。
内容的提问来源于stack exchange,提问作者Van Wilder
相关产品推荐
相关产品推荐

