Search code examples
next.jsamazon-ecsamazon-ecraws-credentials

Next.JS app in ECS does not load env variables nor credentials


I am migrating from Vercel to AWS, and I'm trying to run my app with ECS using ECR. The problem es that the environment variables defined in the ECS are not found in runtime, and if I run a query to an AWS Service, it doesn't recognize the role neither. If I set the environment variables in the github actions workflow and include RUN printenv > .env.production before the RUN npm run build in my Dockerfile, it does find the environment variables, and of course I do not like the solution.

My Dockerfile is something like this:

FROM node:18-alpine AS base

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

COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
  if [ -f package-lock.json ]; then npm ci; \
  elif [ -f yarn.lock ]; then yarn --frozen-lockfile; \
  elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
  else echo "Lockfile not found." && exit 1; \
  fi

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

ARG NODE_ENV
ENV NODE_ENV=${NODE_ENV}
ENV NEXT_TELEMETRY_DISABLED 1

RUN printenv > .env.production

RUN npm run build

FROM base AS runner
WORKDIR /app

RUN apk add --no-cache curl

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

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

COPY --from=builder /app/public ./public

# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
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 ["node", "server.js"]

This solves the environment variables problem, but I couldn't find a solution for the permissions, as of course I don't want to add the access keys in this way. The error is "Could not load credentials from any providers", so is not a problem that the policy does not have a permission, it does not inherit the role from the ECS.


Solution

  • After a few tries, I found out that my problem was because the nextjs output was set as standalone as nextjs documentaciones recommends. But this is for apps that are 100% statics, in my case where I use environments that are configured in ECS or where I have api routes that need to use the assume role it was useless.

    I had to remove the output from next.config.js, and add more files / folders in my Dockerfile so they are copied to the container in ECR. My Dockerfile ended up looking something like this:

    COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
    RUN \
      if [ -f package-lock.json ]; then npm ci; \
      elif [ -f yarn.lock ]; then yarn --frozen-lockfile; \
      elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
      else echo "Lockfile not found." && exit 1; \
      fi
    
    FROM base AS builder
    WORKDIR /app
    COPY --from=deps /app/node_modules ./node_modules
    COPY . .
    COPY package*.json ./
    
    ARG NODE_ENV
    ENV NODE_ENV=${NODE_ENV}
    ENV NEXT_TELEMETRY_DISABLED 1
    
    RUN yarn install
    
    RUN yarn build
    
    FROM base AS runner
    WORKDIR /app
    
    RUN apk add --no-cache curl
    
    ENV NODE_ENV production
    ENV NEXT_TELEMETRY_DISABLED 1
    
    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 all necessary files for runtime, including configurations
    COPY --from=builder --chown=nextjs:nodejs /app/next.config.js ./next.config.js
    COPY --from=builder --chown=nextjs:nodejs /app/utils ./utils
    COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
    COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
    COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json
    
    USER nextjs
    
    EXPOSE 3000
    
    ENV PORT 3000
    
    CMD ["yarn", "start"]