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

Next.js应用写入Docker挂载卷遇EACCES权限拒绝问题排查

问题:Docker部署Next.js与.NET8应用时文件上传权限拒绝错误

Error: EACCES: permission denied, open '/app/public/v1/uploads/639ceeac-65f3-4726-8a31-9e55e603ff9b.png'...

相关配置文件

Next.js的Dockerfile

FROM node:20.15.0-alpine3.20 AS base

FROM base AS deps
RUN apk add --no-cache libc6-compat

WORKDIR /app

COPY --from=ext_path somefile somefile

COPY package*.json ./
RUN mkdir node_modules
RUN npm install --no-audit --omit=dev
RUN npm install sharp

FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/.env.local ./.env.local
COPY . .

ENV NEXT_TELEMETRY_DISABLED 1

RUN npm run build

FROM base AS runner
WORKDIR /app

ENV NODE_ENV production

ENV NEXT_TELEMETRY_DISABLED 1

COPY --from=deps /app/.env.local ./.env.local
COPY --from=ext_path somefile somefile

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/next.config.mjs ./next.config.mjs

COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

RUN mkdir -p ./public/v1/uploads
RUN chown nextjs:nodejs ./public/v1/uploads

USER nextjs

EXPOSE 80

ENV PORT 80
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]

.NET8的Dockerfile

FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS base
WORKDIR /app

EXPOSE 80
ENV ASPNETCORE_URLS=http://+:80

USER app
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
ARG configuration=Release

COPY ["MyApp.sln", "MyApp.sln"]
COPY ["MyApp/MyApp.csproj", "MyApp/"]

RUN dotnet restore "MyApp.sln"

COPY MyApp/ MyApp/

WORKDIR "/MyApp"

FROM build AS publish
ARG configuration=Release
RUN dotnet publish "MyApp.csproj" -c $configuration -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]

production/docker.env文件

SRV_NAME=myproject

DOCKER_ENV=production
RUNTIME_ENV=production

WEB_EXPOSE_PORT=4000
API_EXPOSE_PORT=4100

UPLOAD_MOUNT_PATH=/var/lib/MyApp/web/public/v1/uploads

docker-compose.yml文件

networks:
    default:
        name: ${SRV_NAME}_default
        external: false
    infra:
        name: infra
        external: true
        driver: bridge

services:
    my-web:
        container_name: ${SRV_NAME}-web
        image: jd/my-web:latest
        hostname: ${SRV_NAME}-web
        depends_on:
            - my-api
        build:
            context: ./nextjs
            dockerfile: ./Dockerfile
            network: host
            args:
                NODE_ENV: ${RUNTIME_ENV}
            additional_contexts:
                ext_path: ./${DOCKER_ENV}/ # This is just for reading some additional resources.
        volumes:
            - ${UPLOAD_MOUNT_PATH}:/app/public/v1/uploads:rw
        environment:
            - NODE_ENV=${RUNTIME_ENV}
        ports:
            - ${WEB_EXPOSE_PORT}:80
        networks:
            default:
            infra:

    my-api:
        container_name: ${SRV_NAME}-api
        image: jd/my-api:latest
        hostname: ${SRV_NAME}-api
        build:
            context: ./dotnet8
            dockerfile: ./MyApp/Dockerfile
        volumes:
            - ${UPLOAD_MOUNT_PATH}:/app/public/v1/uploads:rw
        ports:
            - ${API_EXPOSE_PORT}:80
        networks:
            default:
            infra:

问题根源分析

  1. 挂载卷覆盖容器内权限设置:Next.js Dockerfile中已创建并修改./public/v1/uploads目录权限为nextjs:nodejs,但docker-compose用宿主机目录挂载到该路径后,宿主机目录的默认权限(归属宿主机用户)会覆盖容器内的权限配置,导致nextjs用户无写入权限。
  2. 容器用户UID/GID不统一:Next.js使用UID/GID为1001的nextjs用户运行,而.NET8官方镜像默认的app用户UID/GID与1001不一致,两个容器共享同一个挂载目录时,权限规则冲突。
  3. 宿主机目录权限未适配:宿主机的/var/lib/MyApp/web/public/v1/uploads目录默认权限未开放给容器内的非root用户,导致写入失败。

解决方案

1. 统一两个容器的用户UID/GID(核心解决)

调整其中一个容器的用户UID/GID与另一个一致,推荐修改.NET8的app用户UID为1001(对齐Next.js的nextjs用户):

修改.NET8 Dockerfile

在base阶段添加用户调整命令:

FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS base
WORKDIR /app

EXPOSE 80
ENV ASPNETCORE_URLS=http://+:80

# 删除默认app用户,重新创建UID为1001的app用户,对齐Next.js的nextjs用户
RUN deluser app && adduser -D -u 1001 app
USER app

2. 配置宿主机目录初始权限

在宿主机预先创建挂载目录并设置匹配的权限:

mkdir -p /var/lib/MyApp/web/public/v1/uploads
chown 1001:1001 /var/lib/MyApp/web/public/v1/uploads

3. 优化docker-compose配置(可选)

确保两个服务的挂载卷配置一致,无需额外修改,因为用户UID已统一,挂载后容器内用户可正常读写。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 04:13:16