I’m trying to set up dev, preview-staging and prod images of Vite+ReactTS via single Dockerfile using multistaging
Here’s Dockerfile’s content:
#
# ---- Base Image ----
# Use to set up defaults
#
ARG APP_DIR_PATH=app
ARG BASE_IMAGE_VER=20.5.1-alpine3.18
ARG BUILD_PLATFORM=linux/amd64
FROM --platform=${BUILD_PLATFORM} node:${BASE_IMAGE_VER} AS base
# see https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
WORKDIR /${APP_DIR_PATH}
ENV NODE_PM=pnpm
RUN npm i -g ${NODE_PM}
COPY package.json ./
COPY pnpm-lock.yaml ./
#
# ---- Dependencies Image ----
# Use to hold cached node_modules ?and prod_modules separate?
#
ARG DEV_NODE_MODULES_DIR_PATH=node_modules
ARG PROD_NODE_MODULES_DIR_PATH=prod_${DEV_NODE_MODULES_DIR_PATH}
FROM base AS dependencies
RUN ${NODE_PM} i -P
# Possibly we don't need prod copy of packages due to Vite uses tsc and itself for build and preview
# for now they are in devDependencies, so...
RUN cp -R ./${DEV_NODE_MODULES_DIR_PATH} ./${PROD_NODE_MODULES_DIR_PATH}
RUN ${NODE_PM} i
#
# ---- Development Image ----
# Use in development
# Note: rebuild image after every package.json|.configs changing
#
ARG VITE_PORT=8080
FROM dependencies AS dev
COPY ./ ./
# keep tracking ./src/
RUN --mount=type=bind,target=./src/,source=./src/,rw=false
ENV VITE_PORT=${VITE_PORT}
EXPOSE ${VITE_PORT}
CMD ["pnpm", "dev"]
#
# ---- Build Image ----
# Use for production's preview
# Note: rebuild image after every package.json|.configs changing
#
ARG BUILD_DIR_PATH=dist
FROM dev AS build
ENV NODE_ENV=production
COPY ./ ./
RUN ${NODE_PM} build --outDir ./${BUILD_DIR_PATH}
CMD ["pnpm", "preview", "--port", "${VITE_PORT}"]
#
# ---- Production Image ----
# Use in production
# Note: rebuild image after every new change
#
# ARG BASE_PROD_IMAGE_VER=1.25.2-alpine
FROM --platform=${BUILD_PLATFORM} nginx:1.25.2-alpine AS prod
WORKDIR /
COPY ./.configs/nginx/nginx.conf /etc/nginx/nginx.conf
RUN rm -rf /usr/share/nginx/html/*
COPY --from=build /app/dist /usr/share/nginx/html/
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
Always getting error at prod
image staging at line
COPY --from=build /app/dist /usr/share/nginx/html/
With this output
=> ERROR [prod 4/5] COPY --from=build /app/dist /usr/share/nginx/html/ 0.0s
------
> [prod 4/5] COPY --from=build /app/dist /usr/share/nginx/html/:
------
build.Dockerfile:85
--------------------
83 | RUN rm -rf /usr/share/nginx/html/*
84 |
85 | >>> COPY --from=build /app/dist /usr/share/nginx/html/
86 |
87 | EXPOSE 80 443
--------------------
ERROR: failed to solve: failed to compute cache key: failed to calculate checksum of ref 122aaccf-8f31-4e47-aff8-56e3d80edefb::zni94o6alhuq2gp7tryq3q8t0: failed to walk /var/lib/docker/tmp/buildkit-mount959429879/app: lstat /var/lib/docker/tmp/buildkit-mount959429879/app: no such file or directory
I don’t have any idea what is going on in stderr
I’m using Docker(v24.0.5) on Windows
What I’ve tried
Nothing at all over stackoverflow
2
Answers
Weird output: It was caching issue
Problem solve: change
to
reason:
WORKDIR
of base stage-image, in this casebuild
imageThere is a specific bug in the way you’re using build-time
ARG
.Each
FROM
line starts a new image and resets the build environment. In particular that resets the sets ofARG
that are "in scope". You can putARG
beforeFROM
but the only things you can do with theseARG
s are set default values and use them inFROM
lines.Your Dockerfile approximately starts with (in this order)
Since you don’t repeat the
ARG
after theFROM
line, it’s not set in thebase
build stage. That means the$APP_DIR_PATH
variable is empty at this point in the setup. That’s not an error, you just executeWORKDIR /
and carry on from here. When you get to thebuild
stage, you build into/dist
and not/app/dist
and that results in the problem you eventually see.This error seems to be repeated for every build stage; you have
ARG
declared immediately before theFROM
line starting the stage that uses it. I suspect you’re avoiding trouble here only because this Dockerfile is so linear, every stage inherits from its immediate predecessor. If you had a separate pathFROM base
that installed only production modules it might see similar issues around undefined variables.You have a lot of variables for things like filesystem paths and command names that don’t quite make sense here. This is doubly true for things like the
node_modules
directory that have fixed names. (Ask, would you ever change these things? Platform or Node version, yes; changingpnpm
toyarn
, not without some other rearrangement.) I’d remove many of these variables, which avoids some ambiguity