docker.net-corenext.js

NextJS TypeError: fetch failed. code: 'ECONNREFUSED' from Docker


I am running a Next.js app on Docker. However, when calling my API I get the following error

TypeError: fetch failed
   at node:internal/deps/undici/undici:13178:13
   at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
   at async m (/app/.next/server/chunks/961.js:3:716)
   at async b (/app/.next/server/pages/user/administration.js:51:1030)
   at async e3 (/app/node_modules/.pnpm/next@14.2.2_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0_sass@1.75.0/node_modules/next/dist/compiled/next-server/pages.runtime.prod.js:31:594)
   at async doRender (/app/node_modules/.pnpm/next@14.2.2_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0_sass@1.75.0/node_modules/next/dist/server/base-server.js:1425:30)
   at async cacheEntry.responseCache.get.routeKind (/app/node_modules/.pnpm/next@14.2.2_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0_sass@1.75.0/node_modules/next/dist/server/base-server.js:1599:28)
   at async NextNodeServer.renderToResponseWithComponentsImpl (/app/node_modules/.pnpm/next@14.2.2_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0_sass@1.75.0/node_modules/next/dist/server/base-server.js:1507:28)
   at async NextNodeServer.renderPageComponent (/app/node_modules/.pnpm/next@14.2.2_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0_sass@1.75.0/node_modules/next/dist/server/base-server.js:1924:24)
   at async NextNodeServer.renderToResponseImpl (/app/node_modules/.pnpm/next@14.2.2_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0_sass@1.75.0/node_modules/next/dist/server/base-server.js:1962:32) {
 [cause]: AggregateError [ECONNREFUSED]:
     at internalConnectMultiple (node:net:1118:18)
     at afterConnectMultiple (node:net:1685:7)
     at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
   code: 'ECONNREFUSED',
   [errors]: [ [Error], [Error] ]

This only happens when calling the API from getServerSideProps. Calling it from the client side, everything works as expected, including authenticated and public API calls.

The API is built with .NET Core and running on localhost:5068 (not in Docker). The Next app is running on localhost:3050 from Docker.

Everything works as expected when I run the Next app locally. So I'm assuming I need some configuration somewhere in Next (or .NET API) to make a successful call from getServerSideProps when Next is running in Docker context.

Dockerfile

FROM node:22.4-alpine AS base

FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json pnpm-lock.yaml* ./
RUN corepack enable pnpm && pnpm i --frozen-lockfile

FROM base AS builder
ENV NEXT_PRIVATE_STANDALONE true
ENV NEXT_PUBLIC_BACKEND_ROOT_URI http://localhost:5068
ENV NEXT_PUBLIC_FRONTEND_URI http://localhost:3050
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN corepack enable pnpm && pnpm run build

FROM base AS runner
WORKDIR /app
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/next.config.js ./
COPY --from=builder /app/public ./public
RUN mkdir .next
RUN chown nextjs:nodejs .next
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3050
ENV PORT 3050
CMD HOSTNAME="0.0.0.0" node server.js

Docker Compose

services:
  sweetnotes.fe:
    image: myimage
    container_name: mycontainer
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - '3050:3050'

Solution

  • By default, Docker containers run on a Docker virtual network created by Docker to isolate the containers from the host. For network purposes, you can think of each container as a separate machine. And localhost in a container means the container itself.

    To reach the host from a container, you need to add the host to the known hostnames of the container using extra_hosts in your compose file. It's common to use the name host.docker.internal as the hostname of the host.

    In your case, it'd look like this

    services:
      sweetnotes.fe:
        image: myimage
        container_name: mycontainer
        build:
          context: .
          dockerfile: Dockerfile
        ports:
          - '3050:3050'
        extra_hosts:
          - 'host.docker.internal:host-gateway'
    

    Then you can change the URL of the backend to

    ENV NEXT_PUBLIC_BACKEND_ROOT_URI http://host.docker.internal:5068
    

    in your Dockerfile and it should work after rebuilding the image.