I have a Nextjs project using the create T3 app stacks (with the exception of using drizzle orm instead pf Prisma).
On my development environment everything is working just fine
Now I wanted to deploy my project on my linux server using docker.
Ubuntu 22.04.2 LTS - Platform: linux/arm64/v8
The docker-compose build process is working just fine. Both services are up and running and the environments variables are set up correctly (unless I forgot to set and important one?) but I cannot acces any pages of the app. When I try to open it the server is logging several runtime erros complaining about a refused connection to localhost. My setup and the error are posted below.
My Dockerfile (from T3 docs)
##### DEPENDENCIES
FROM --platform=linux/arm64/v8 node:16-alpine3.17 AS deps
RUN apk add --no-cache libc6-compat openssl1.1-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \
else echo "Lockfile not found." && exit 1; \
fi
##### BUILDER
FROM --platform=linux/arm64/v8 node:16-alpine3.17 AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# ENV NEXT_TELEMETRY_DISABLED 1
RUN \
if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=0 yarn build; \
elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=0 npm run build; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=0 pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
##### RUNNER
FROM --platform=linux/arm64/v8 node:16-alpine3.17 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.mjs ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
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"]
My docker-compose
version: '3.8'
services:
nextjs-app-bpl-v3:
build:
context: .
dockerfile: Dockerfile
container_name: nextjs-app-bpl-v3
restart: unless-stopped
working_dir: /app
ports:
- '3187:3000'
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres-db-bpl-v3:5432/${POSTGRES_DB}
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
- NEXTAUTH_URL=${NEXTAUTH_URL}
- DISCORD_CLIENT_ID=${DISCORD_CLIENT_ID}
- DISCORD_CLIENT_SECRET=${DISCORD_CLIENT_SECRET}
image: t3-app-bpl-v3
depends_on:
- postgres-db-bpl-v3
networks:
- nginx_network
postgres-db-bpl-v3:
image: postgres:latest
container_name: postgres-db-bpl-v3
restart: unless-stopped
ports:
- '5578:5432'
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
volumes:
- postgres-data-bpl-v3:/var/lib/postgresql/data
networks:
- nginx_network
volumes:
postgres-data-bpl-v3:
networks:
nginx_network:
external: true
My environment (.env)
NEXTAUTH_SECRET="MY_SECRET"
NEXTAUTH_URL="https://example.com"
# Next Auth Providers
DISCORD_CLIENT_ID="DC_ID"
DISCORD_CLIENT_SECRET="DC_SECRET"
POSTGRES_USER="username"
POSTGRES_PASSWORD="my_password"
POSTGRES_DB="db_name"
next.config.mjs
await import("./src/env.mjs");
/** @type {import("next").NextConfig} */
const config = {
reactStrictMode: true,
i18n: {
locales: ["en"],
defaultLocale: "en",
},
webpackDevMiddleware: (
/** @type {{ watchOptions: { poll: number; aggregateTimeout: number; }; }} */ config
) => {
config.watchOptions = {
poll: 1000,
aggregateTimeout: 300,
};
return config;
},
swcMinify: true,
output: "standalone",
};
export default config;
Container logs:
Attaching to nextjs-app-bpl-v3
nextjs-app-bpl-v3 | - ready started server on 07135160c06c:3000, url: http://07135160c06c:3000
nextjs-app-bpl-v3 | Listening on port 3000 url: http://07135160c06c:3000
nextjs-app-bpl-v3 | Warning - Configuring `keepAlive: false` is deprecated. Use `{ headers: { connection: "close" } }` instead.
nextjs-app-bpl-v3 | - error Failed to handle request for /
nextjs-app-bpl-v3 | TypeError: fetch failed
nextjs-app-bpl-v3 | at Object.fetch (/app/node_modules/next/dist/compiled/undici/index.js:1:26669)
nextjs-app-bpl-v3 | at processTicksAndRejections (node:internal/process/task_queues:96:5)
nextjs-app-bpl-v3 | at async invokeRequest (/app/node_modules/next/dist/server/lib/server-ipc/invoke-request.js:21:12)
nextjs-app-bpl-v3 | at async requestHandler (/app/node_modules/next/dist/server/lib/start-server.js:336:33)
nextjs-app-bpl-v3 | at async Server.<anonymous> (/app/node_modules/next/dist/server/lib/start-server.js:152:13) {
nextjs-app-bpl-v3 | cause: Error: connect ECONNREFUSED 127.0.0.1:45761
nextjs-app-bpl-v3 | at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1278:16) {
nextjs-app-bpl-v3 | errno: -111,
nextjs-app-bpl-v3 | code: 'ECONNREFUSED',
nextjs-app-bpl-v3 | syscall: 'connect',
nextjs-app-bpl-v3 | address: '127.0.0.1',
nextjs-app-bpl-v3 | port: 45761
nextjs-app-bpl-v3 | }
nextjs-app-bpl-v3 | }
nextjs-app-bpl-v3 | - error Failed to handle request for /favicon.ico
nextjs-app-bpl-v3 | TypeError: fetch failed
nextjs-app-bpl-v3 | at Object.fetch (/app/node_modules/next/dist/compiled/undici/index.js:1:26669)
nextjs-app-bpl-v3 | at processTicksAndRejections (node:internal/process/task_queues:96:5)
nextjs-app-bpl-v3 | at async invokeRequest (/app/node_modules/next/dist/server/lib/server-ipc/invoke-request.js:21:12)
nextjs-app-bpl-v3 | at async requestHandler (/app/node_modules/next/dist/server/lib/start-server.js:336:33)
nextjs-app-bpl-v3 | at async Server.<anonymous> (/app/node_modules/next/dist/server/lib/start-server.js:152:13) {
nextjs-app-bpl-v3 | cause: Error: connect ECONNREFUSED 127.0.0.1:45761
nextjs-app-bpl-v3 | at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1278:16) {
nextjs-app-bpl-v3 | errno: -111,
nextjs-app-bpl-v3 | code: 'ECONNREFUSED',
nextjs-app-bpl-v3 | syscall: 'connect',
nextjs-app-bpl-v3 | address: '127.0.0.1',
nextjs-app-bpl-v3 | port: 45761
nextjs-app-bpl-v3 | }
nextjs-app-bpl-v3 | }
Why is the runtime trying to connect to localhost? I don't see any point where I have a reference to this. The error is happening on the server (not on the client when opening the page) The client only shows a 500: internal server error.
My workaround for now is:
I removed the output: "standalone" and updated the Dockerfile by removing the RUNNER section and start the server with npm run start (next start)
This is working although I am still wondering why it does not work with the standalone output
My question and goal would be to use the standalone server as intended by fixing this TypeError: fetch failed.
What are the exact downside of not using the standalone build inside of my docker container?
Thanks for any explanation!
I may be mistaken, but I think some ENV variables are required at build time and some at run time when using the
output: standaloneoption in yournext.config.{m}jsfile. I'm not sure if this is correct or not, but this is how I have my Dockerfile working onnext 13.4.12:and in my
docker-compose.yml:Also worth noting that there is currently an open issue with
next 13.4.13and above with ENV variables andoutput: standalone.