Docker构建NextJS应用Postgres连接拒绝问题求助
问题分析与解决方案
核心问题
- 单独执行
docker build时,NextJS构建静态页面阶段触发ECONNREFUSED (errno: -111)错误,但docker-compose up --build可正常运行 - 删除主页中调用
getPayload()/payload.find()的组件后能完成构建,但部署到Vercel后,客户端子页正常,服务端子页、管理子页及部分应用请求返回500错误
根本原因
单独docker build是孤立的构建环境,没有启动PostgreSQL服务。而NextJS静态生成(SSG)阶段会执行服务端代码,调用getPayload()时会尝试连接数据库,此时数据库未运行就会触发连接拒绝。docker-compose会先启动健康的PostgreSQL服务再执行构建/启动服务,所以能正常连接。
解决方案
1. 构建阶段规避数据库连接(推荐)
修改NextJS构建逻辑,静态生成时用模拟数据或离线快照,不直接连数据库:
- 在
next.config.js中添加环境变量区分构建/运行环境:module.exports = { env: { IS_BUILDING: process.env.NODE_ENV === 'production' && process.env.BUILD_STAGE === 'true', }, }; - 在使用
getPayload()的组件中判断环境,返回模拟数据:export async function getStaticProps() { if (process.env.IS_BUILDING) { // 构建阶段返回模拟数据,跳过数据库连接 return { props: { data: [{ id: 1, title: '占位数据' }], }, }; } // 运行环境正常连接数据库 const payload = await getPayload(); const data = await payload.find({ collection: 'posts' }); return { props: { data } }; } - 构建时传入环境变量:
docker build --build-arg BUILD_STAGE=true .
2. 构建阶段临时启动数据库(适合必须连库的场景)
修改应用Dockerfile,在builder阶段临时启动PostgreSQL:
FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY package.json pnpm-lock.yaml* ./ COPY . . # 安装并临时启动PostgreSQL RUN apk add --no-cache postgresql RUN mkdir -p /run/postgresql && chown postgres:postgres /run/postgresql RUN su postgres -c "initdb -D /var/lib/postgresql/data" RUN su postgres -c "pg_ctl start -D /var/lib/postgresql/data" # 等待数据库就绪 RUN su postgres -c "pg_isready -U postgres -h localhost" # 执行构建 RUN \ if [ -f yarn.lock ]; then yarn run build; \ elif [ -f package-lock.json ]; then npm run build; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi # 构建完成后停止数据库 RUN su postgres -c "pg_ctl stop -D /var/lib/postgresql/data"
构建时指定临时数据库地址:
docker build --build-arg DATABASE_URI=postgres://postgres@localhost/postgres .
3. Vercel部署修复
Vercel上的500错误是因为运行环境未正确配置Payload连接:
- 在Vercel项目环境变量中添加
DATABASE_URI,注意不能用postgres主机名,要填数据库的公网地址 - 检查
pages/api/[...payload].ts的初始化逻辑是否正确:import { getPayload } from 'payload'; import config from '../../payload.config'; export default async function handler(req, res) { const payload = await getPayload({ config, req, }); await payload.init({ config, req, }); return payload.express(req, res); }
原配置格式化
docker-compose.yml
version: '3' services: postgres: build: context: postgres restart: always env_file: - .env volumes: - pgdata:/var/lib/postgresql/data ports: - "5434:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 payload: image: node:18-alpine working_dir: /home/node/app env_file: - .env depends_on: postgres: condition: service_healthy volumes: - .:/home/node/app - node_modules:/home/node/app/node_modules ports: - "3000:3000" command: > sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev" volumes: pgdata: node_modules: data:
postgres/Dockerfile
FROM postgres:latest RUN cd /var/lib/postgresql/ && \ openssl req -new -text -passout pass:abcd -subj /CN=localhost -out server.req -keyout privkey.pem && \ openssl rsa -in privkey.pem -passin pass:abcd -out server.key && \ openssl req -x509 -in server.req -text -key server.key -out server.crt && \ chmod 600 server.key && \ chown postgres:postgres server.key CMD ["postgres", "-c", "ssl=on", "-c", "ssl_cert_file=/var/lib/postgresql/server.crt", "-c", "ssl_key_file=/var/lib/postgresql/server.key" ]
应用Dockerfile
FROM node:18-alpine AS base FROM base AS deps RUN apk add --no-cache libc6-compat WORKDIR /app RUN apk add --no-cache postgresql-client RUN corepack enable && corepack prepare pnpm@latest --activate COPY package.json pnpm-lock.yaml* ./ RUN pnpm install --frozen-lockfile FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY package.json pnpm-lock.yaml* ./ COPY . . RUN \ if [ -f yarn.lock ]; then yarn run build; \ elif [ -f package-lock.json ]; then npm run build; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi FROM base AS runner WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED 1 RUN apk add --no-cache postgresql-client RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/public ./public RUN mkdir .next RUN chown nextjs:nodejs .next COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 CMD HOSTNAME="0.0.0.0" node server.js
内容的提问来源于stack exchange,提问作者user30565566
相关产品推荐
相关产品推荐

