dockernext.jsdocker-composebuild

Docker, cannot locate secrets folders during image build


I am trying to run a container on docker, i have a Dockerfile for building image, and compose.yml for multi-container configurations, note that I am using Next.js to develop my app.

Dockerfile



# First Stage is used to install the dependencies from the host machine app to the image /app directory
FROM node:22-alpine AS initiate
# set the working directory in the container to /app
WORKDIR /app
# copy package.json from the host machine to the /app directory in the image
COPY package*.json ./

RUN --mount=type=secret,id=CONNECTION_STRING \
    --mount=type=secret,id=EMAIL_SERVER_HOST \
    --mount=type=secret,id=EMAIL_SERVER_USER \
    --mount=type=secret,id=EMAIL_SERVER_PASSWORD \
    --mount=type=secret,id=EMAIL_FROM \
     --mount=type=secret,id=NEXTAUTH_URL \
    --mount=type=secret,id=NEXTAUTH_SECRET \
    export CONNECTION_STRING="$(cat /run/secrets/CONNECTION_STRING)" && \
    export EMAIL_SERVER_HOST="$(cat /run/secrets/EMAIL_SERVER_HOST)" && \
    export EMAIL_SERVER_USER="$(cat /run/secrets/EMAIL_SERVER_USER)" && \
    export EMAIL_SERVER_PASSWORD="$(cat /run/secrets/EMAIL_SERVER_PASSWORD)" && \
    export EMAIL_FROM="$(cat /run/secrets/EMAIL_FROM)" && \
    export NEXTAUTH_URL="$(cat /run/secrets/NEXTAUTH_URL)" && \
    export NEXTAUTH_SECRET="$(cat /run/secrets/NEXTAUTH_SECRET)" && \
    npm ci




# 2nd Stage is used to build the app
FROM initiate AS build
COPY . .
# copy the rest of application from the host machine to the /app directory in the image to build the app
RUN --mount=type=secret,id=CONNECTION_STRING \
    --mount=type=secret,id=EMAIL_SERVER_HOST \
    --mount=type=secret,id=EMAIL_SERVER_USER \
    --mount=type=secret,id=EMAIL_SERVER_PASSWORD \
    --mount=type=secret,id=EMAIL_FROM \
    --mount=type=secret,id=NEXTAUTH_URL \
    --mount=type=secret,id=NEXTAUTH_SECRET \
    export CONNECTION_STRING="$(cat /run/secrets/CONNECTION_STRING)" && \
    export EMAIL_SERVER_HOST="$(cat /run/secrets/EMAIL_SERVER_HOST)" && \
    export EMAIL_SERVER_USER="$(cat /run/secrets/EMAIL_SERVER_USER)" && \
    export EMAIL_SERVER_PASSWORD="$(cat /run/secrets/EMAIL_SERVER_PASSWORD)" && \
    export EMAIL_FROM="$(cat /run/secrets/EMAIL_FROM)" && \
    export NEXTAUTH_URL="$(cat /run/secrets/NEXTAUTH_URL)" && \
    export NEXTAUTH_SECRET="$(cat /run/secrets/NEXTAUTH_SECRET)" && \
    npm run build


# 3rd Stage is used to run the app
FROM initiate AS run

RUN addgroup --system --gid 1001 nonroot
RUN adduser --system --uid 1001 runner
# Create a non-root user to run the app and assign it to the nonroot group
USER runner
# Switch to the user runner

COPY --from=build /public ./public
COPY --from=build --chown=runner:nonroot /.next/standalone ./
COPY --from=build --chown=runner:nonroot /.next/static ./.next/static

EXPOSE 3000

CMD ["npm","run","start"]

compose.yml

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      secrets:
      - EMAIL_SERVER_HOST
      - EMAIL_SERVER_PORT
      - EMAIL_SERVER_USER
      - EMAIL_SERVER_PASSWORD
      - EMAIL_FROM
      - CONNECTION_STRING
      - NEXTAUTH_SECRET
      - NEXTAUTH_URL
    environment:
      - PROD='1'
    ports: 
      - "3000:3000"
    
    secrets:
    - CONNECTION_STRING #providing secrets after the image build because they are needed for access to the container
    - EMAIL_FROM
    - EMAIL_SERVER_HOST
    - EMAIL_SERVER_PASSWORD
    - EMAIL_SERVER_PORT
    - EMAIL_SERVER_USER
    - NEXTAUTH_SECRET
    - NEXTAUTH_URL
   
secrets:
  EMAIL_SERVER_HOST:
    file: ./run/secrets/EMAIL_SERVER_HOST
  EMAIL_SERVER_PORT:
    file: ./run/secrets/EMAIL_SERVER_PORT
  EMAIL_SERVER_USER:
    file: ./run/secrets/EMAIL_SERVER_USER
  EMAIL_SERVER_PASSWORD:
    file: ./run/secrets/EMAIL_SERVER_PASSWORD
  EMAIL_FROM:
    file: ./run/secrets/EMAIL_FROM
  CONNECTION_STRING:
    file: ./run/secrets/CONNECTION_STRING
  NEXTAUTH_SECRET:
    file: ./run/secrets/NEXTAUTH_SECRET
  NEXTAUTH_URL:
    file: ./run/secrets/NEXTAUTH_URL

the secrets in host machine are stored in run/secrets/ folder, and this run folder is in the project root.

and the way I access those secrets when PROD is on is like this:


const getVars = async ()=>{
  if((process!.env!.PROD!)=='0'){
    return {
      host: process.env!.EMAIL_SERVER_HOST!,
      port: process.env!.EMAIL_SERVER_PORT!,
      user: process.env!.EMAIL_SERVER_USER!,
      pass: process.env!.EMAIL_SERVER_PASSWORD!,
      from:process.env!.EMAIL_FROM!

    }
  }
return{
   host: await readFile(path.join(process.cwd(), 'run/secrets/EMAIL_SERVER_HOST'),{encoding:'utf-8'}),
  port: await readFile(path.join(process.cwd(), 'run/secrets/EMAIL_SERVER_PORT'),{encoding:'utf-8'}),
  user:await readFile(path.join(process.cwd(), 'run/secrets/EMAIL_SERVER_USER'),{encoding:'utf-8'}),
  pass:await readFile(path.join(process.cwd(), 'run/secrets/EMAIL_SERVER_PASSWORD'),{encoding:'utf-8'}),
  from:await readFile(path.join(process.cwd(), 'run/secrets/EMAIL_FROM'),{encoding:'utf-8'}) as unknown as string
}
  
}

but I get error during the run of the build layer:


 > [build 2/2] RUN --mount=type=secret,id=CONNECTION_STRING     --mount=type=secret,id=EMAIL_SERVER_HOST     --mount=type=secret,id=EMAIL_SERVER_USER     --mount=type=secret,id=EMAIL_SERVER_PASSWORD     --mount=type=secret,id=EMAIL_FROM     --mount=type=secret,id=NEXTAUTH_URL     --mount=type=secret,id=NEXTAUTH_SECRET     export CONNECTION_STRING="$(cat /run/secrets/CONNECTION_STRING)" &&     export EMAIL_SERVER_HOST="$(cat /run/secrets/EMAIL_SERVER_HOST)" &&     export EMAIL_SERVER_USER="$(cat /run/secrets/EMAIL_SERVER_USER)" &&     export EMAIL_SERVER_PASSWORD="$(cat /run/secrets/EMAIL_SERVER_PASSWORD)" &&     export EMAIL_FROM="$(cat /run/secrets/EMAIL_FROM)" &&     export NEXTAUTH_URL="$(cat /run/secrets/NEXTAUTH_URL)" &&     export NEXTAUTH_SECRET="$(cat /run/secrets/NEXTAUTH_SECRET)" &&     npm run build:
0.560
0.560 > myportfolio@0.1.0 build
0.560 > next build
0.560
1.523 Attention: Next.js now collects completely anonymous telemetry regarding usage.
1.523 This information is used to shape Next.js' roadmap and prioritize features.
1.523 You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
1.523 https://nextjs.org/telemetry
1.523
1.619   ▲ Next.js 14.2.17
1.619
1.718    Creating an optimized production build ...
119.7  ✓ Compiled successfully
119.7    Linting and checking validity of types ...
127.0
127.0 ./src/app/blog/clientComp.tsx
127.0 29:21  Warning: Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element  @next/next/no-img-element       
127.0 29:21  Warning: img elements must have an alt prop, either with meaningful text, or an empty string for decorative images.  jsx-a11y/alt-text        
127.0
127.0 ./src/app/components/blogs/blogs.tsx
127.0 63:25  Warning: Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element  @next/next/no-img-element       
127.0
127.0 ./src/app/components/experience/experience.tsx
127.0 23:9  Warning: Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element  @next/next/no-img-element        
127.0
127.0 info  - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules
134.7    Collecting page data ...
137.1 Error: ENOENT: no such file or directory, open '/app/run/secrets/EMAIL_SERVER_HOST'
137.1     at async open (node:internal/fs/promises:639:25)
137.1     at async readFile (node:internal/fs/promises:1243:14)
137.1     at async m (/app/.next/server/app/api/auth/[...nextauth]/route.js:67:84167)
137.1     at async g (/app/.next/server/app/api/auth/[...nextauth]/route.js:67:84688)
137.1     at async /app/.next/server/app/api/auth/[...nextauth]/route.js:67:84949 {
137.1   errno: -2,
137.1   code: 'ENOENT',
137.1   syscall: 'open',
137.1   path: '/app/run/secrets/EMAIL_SERVER_HOST'
137.1 }
137.1
137.1 > Build error occurred
137.1 Error: Failed to collect page data for /api/auth/[...nextauth]
137.1     at /app/node_modules/next/dist/build/utils.js:1268:15 {
137.1   type: 'Error'
137.1 }
------
Dockerfile:34
--------------------
  33 |     # copy the rest of application from the host machine to the /app directory in the image to build the app
  34 | >>> RUN --mount=type=secret,id=CONNECTION_STRING \
  35 | >>>     --mount=type=secret,id=EMAIL_SERVER_HOST \
  36 | >>>     --mount=type=secret,id=EMAIL_SERVER_USER \
  37 | >>>     --mount=type=secret,id=EMAIL_SERVER_PASSWORD \
  38 | >>>     --mount=type=secret,id=EMAIL_FROM \
  39 | >>>     --mount=type=secret,id=NEXTAUTH_URL \
  40 | >>>     --mount=type=secret,id=NEXTAUTH_SECRET \
  41 | >>>     export CONNECTION_STRING="$(cat /run/secrets/CONNECTION_STRING)" && \
  42 | >>>     export EMAIL_SERVER_HOST="$(cat /run/secrets/EMAIL_SERVER_HOST)" && \
  43 | >>>     export EMAIL_SERVER_USER="$(cat /run/secrets/EMAIL_SERVER_USER)" && \
  44 | >>>     export EMAIL_SERVER_PASSWORD="$(cat /run/secrets/EMAIL_SERVER_PASSWORD)" && \
  45 | >>>     export EMAIL_FROM="$(cat /run/secrets/EMAIL_FROM)" && \
  46 | >>>     export NEXTAUTH_URL="$(cat /run/secrets/NEXTAUTH_URL)" && \
  47 | >>>     export NEXTAUTH_SECRET="$(cat /run/secrets/NEXTAUTH_SECRET)" && \
  48 | >>>     npm run build
  49 |
--------------------
failed to solve: process "/bin/sh -c export CONNECTION_STRING=\"$(cat /run/secrets/CONNECTION_STRING)\" &&     export EMAIL_SERVER_HOST=\"$(cat /run/secrets/EMAIL_SERVER_HOST)\" &&     export EMAIL_SERVER_USER=\"$(cat /run/secrets/EMAIL_SERVER_USER)\" &&     export EMAIL_SERVER_PASSWORD=\"$(cat /run/secrets/EMAIL_SERVER_PASSWORD)\" &&     export EMAIL_FROM=\"$(cat /run/secrets/EMAIL_FROM)\" &&     export NEXTAUTH_URL=\"$(cat /run/secrets/NEXTAUTH_URL)\" &&     export NEXTAUTH_SECRET=\"$(cat /run/secrets/NEXTAUTH_SECRET)\" &&     npm run build" did not complete successfully: exit code: 1

a screenshot of run/secrets folder:

enter image description here

The folder is on the project root.


Solution

  • You prepend the working directory to the secret file names in your Javascript which causes it to look for the files in /app/run/secrets/ instead of /run/secrets.

    Instead of

    host: await readFile(path.join(process.cwd(), 'run/secrets/EMAIL_SERVER_HOST'),{encoding:'utf-8'}),
    port: await readFile(path.join(process.cwd(), 'run/secrets/EMAIL_SERVER_PORT'),{encoding:'utf-8'}),
    user:await readFile(path.join(process.cwd(), 'run/secrets/EMAIL_SERVER_USER'),{encoding:'utf-8'}),
    pass:await readFile(path.join(process.cwd(), 'run/secrets/EMAIL_SERVER_PASSWORD'),{encoding:'utf-8'}),
    from:await readFile(path.join(process.cwd(), 'run/secrets/EMAIL_FROM'),{encoding:'utf-8'}) as unknown as string
    

    use

    host: await readFile('/run/secrets/EMAIL_SERVER_HOST',{encoding:'utf-8'}),
    port: await readFile('/run/secrets/EMAIL_SERVER_PORT',{encoding:'utf-8'}),
    user:await readFile('/run/secrets/EMAIL_SERVER_USER',{encoding:'utf-8'}),
    pass:await readFile('/run/secrets/EMAIL_SERVER_PASSWORD',{encoding:'utf-8'}),
    from:await readFile('/run/secrets/EMAIL_FROM',{encoding:'utf-8'}) as unknown as string