Search code examples
dockernext.jsyarnpkgpreactdocker-multi-stage-build

Running multi-stacker docker built docker image fails – Nexts cannot find react module


I'm building a multi-stage docker image for a project that uses nextjs and preact within a lerna monorepo.

The multi-stage build succeeds, however, when I want to run the image, nextjs throws an error that 'react' cannot be found.

The Dockerfile for the multi-stage build:

FROM node:12 as builder

RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash -s -- -b /usr/local/bin

RUN npm -g config set user root && \
    npm install -g lerna

WORKDIR /app
COPY . ./

ARG TARGET_APP_FOLDER=apps/app-1

# Install dependencies
COPY yarn.lock ./
WORKDIR /app/${TARGET_APP_FOLDER}
RUN lerna bootstrap \
    -- --production

# Build
ARG PROJECT_ID
RUN test -n "$PROJECT_ID" || (echo "PROJECT_ID not set. Need to set PROJECT_ID to the GCP project ID you're deploying to" && false)

ENV PROJECT_ID=${PROJECT_ID}
RUN yarn run build:prod && \
    /usr/local/bin/node-prune

################################################

# Our final image
FROM node:12-alpine

RUN yarn global add next

ARG TARGET_APP_FOLDER=apps/app-1

WORKDIR /app

# copy from build image
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/${TARGET_APP_FOLDER}/.next ./.next
COPY --from=builder /app/${TARGET_APP_FOLDER}/node_modules ./node_modules
COPY --from=builder /app/${TARGET_APP_FOLDER}/package.json ./package.json

ENV PORT=8080
CMD yarn run start -p ${PORT}

I verified in the final that the react module has been copied to the node_modules folder. The react module itself is just using an alias to use preact/compat.

When running everything in a single builder it works perfectly fine:

FROM node:12 as builder

RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash -s -- -b /usr/local/bin

RUN npm -g config set user root && \
    npm install -g lerna

WORKDIR /app
COPY . ./

ARG TARGET_APP_FOLDER=apps/app-1

# Install dependencies
COPY yarn.lock ./
WORKDIR /app/${TARGET_APP_FOLDER}
RUN lerna bootstrap \
    -- --production

# Build
ARG PROJECT_ID
RUN test -n "$PROJECT_ID" || (echo "PROJECT_ID not set. Need to set PROJECT_ID to the GCP project ID you're deploying to" && false)

ENV PROJECT_ID=${PROJECT_ID}
RUN yarn run build:prod && \
    /usr/local/bin/node-prune

# Start the server
ENV PORT=8080
CMD yarn run start -p ${PORT}

Solution

  • Fixed it!

    This is the dockerfile I'm using right now in case anyone stumbles on the same issue:

    FROM node:12-alpine AS base
    
    # DEFAULT ARGS
    ENV BUILD_FOLDER=/build
    ENV TARGET_APP_FOLDER=/apps/my_app
    
    # CHECK PROJECT ID IS SET
    ARG PROJECT_ID
    RUN test -n "$PROJECT_ID" || (echo "PROJECT_ID not set. Need to set PROJECT_ID to the GCP project ID you're deploying to" && false)
    ENV PROJECT_ID=${PROJECT_ID}
    
    ###### BUILDER BASE PREPARING FILES ######
    
    FROM base AS builder_base
    
    WORKDIR /base
    
    RUN apk update && \
        apk add curl bash && \
        apk --no-cache add g++ make libpng-dev && \
        curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash -s -- -b /usr/local/bin
    
    # COPY YARN LOCK
    COPY yarn.lock ./
    
    # Copy source
    COPY . ./
    
    # Remove unused source
    RUN bin/delete-unused-apps.sh ${TARGET_APP_FOLDER}
    
    ###### BUILDER ######
    
    FROM builder_base AS build
    WORKDIR ${BUILD_FOLDER}
    COPY --from=builder_base /base ./
    
    ENV PATH=${BUILD_FOLDER}/node_modules/.bin:$PATH
    RUN yarn install --production
    
    WORKDIR ${BUILD_FOLDER}/${TARGET_APP_FOLDER}
    
    RUN yarn build:prod && \
        /usr/local/bin/node-prune
    
    
    ###### RUNNER ######
    
    FROM base AS runner
    ARG PROJECT_ID
    ENV PROJECT_ID=${PROJECT_ID}
    ENV PATH=/app/node_modules/.bin:$PATH
    WORKDIR /app
    
    COPY --from=build ${BUILD_FOLDER}/node_modules ./node_modules
    COPY --from=build ${BUILD_FOLDER}${TARGET_APP_FOLDER}/.next ./.next
    COPY --from=build ${BUILD_FOLDER}${TARGET_APP_FOLDER}/public ./public
    COPY --from=build ${BUILD_FOLDER}${TARGET_APP_FOLDER}/package*.json ./
    
    # Start the server
    ENV PORT=8080
    CMD yarn run start -p ${PORT}