Programming

Creating a better Docker Container for Payload/Next.JS

Written by Preston Garrison
Post hero image

Creating a Docker Container for Payload and Next JS

In a previous post I showed you how to create a docker container and deploy it, however this is a much better way. This way allows you to upload less of your code, and deploy the docker container only using the files necessary. This also passes all the env fields during build

First create a new Dockerfile deleting any old docker files

1# Base image for all stages
2FROM node:22-alpine AS base
3
4# Common ARGs for build-time variables (Make them available for all stages)
5ARG DATABASE_URI
6ARG PAYLOAD_SECRET
7ARG PAYLOAD_PUBLIC_SERVER_URL
8ARG NEXT_PUBLIC_SERVER_URL
9ARG NEXT_PUBLIC_IS_LIVE
10ARG PAYLOAD_PUBLIC_DRAFT_SECRET
11ARG NEXT_PRIVATE_DRAFT_SECRET
12ARG REVALIDATION_KEY
13ARG NEXT_PRIVATE_REVALIDATION_KEY
14
15# ENV variables (Optional: persist build-time variables as runtime ENV)
16ENV DATABASE_URI=${DATABASE_URI}
17ENV PAYLOAD_SECRET=${PAYLOAD_SECRET}
18ENV PAYLOAD_PUBLIC_SERVER_URL=${PAYLOAD_PUBLIC_SERVER_URL}
19ENV NEXT_PUBLIC_SERVER_URL=${NEXT_PUBLIC_SERVER_URL}
20ENV NEXT_PUBLIC_IS_LIVE=${NEXT_PUBLIC_IS_LIVE}
21ENV PAYLOAD_PUBLIC_DRAFT_SECRET=${PAYLOAD_PUBLIC_DRAFT_SECRET}
22ENV NEXT_PRIVATE_DRAFT_SECRET=${NEXT_PRIVATE_DRAFT_SECRET}
23ENV REVALIDATION_KEY=${REVALIDATION_KEY}
24ENV NEXT_PRIVATE_REVALIDATION_KEY=${NEXT_PRIVATE_REVALIDATION_KEY}
25
26# Dependencies Stage
27FROM base AS deps
28RUN apk add --no-cache libc6-compat
29WORKDIR /app
30
31COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
32
33RUN \
34 if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
35 elif [ -f package-lock.json ]; then npm ci; \
36 elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
37 else echo "Lockfile not found." && exit 1; \
38 fi
39
40# Build Stage
41FROM base AS builder
42WORKDIR /app
43COPY --from=deps /app/node_modules ./node_modules
44COPY . .
45
46# Use build-time variables during build
47RUN \
48 if [ -f yarn.lock ]; then yarn run build; \
49 elif [ -f package-lock.json ]; then npm run build; \
50 elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
51 else echo "Lockfile not found." && exit 1; \
52 fi
53
54# Runner Stage
55FROM base AS runner
56WORKDIR /app
57
58# Use runtime variables here
59ENV NODE_ENV production
60ENV NEXT_TELEMETRY_DISABLED 1
61
62# Create nextjs user and group
63RUN addgroup --system --gid 1001 nodejs
64RUN adduser --system --uid 1001 nextjs
65
66COPY --from=builder /app/public ./public
67
68RUN mkdir .next
69
70COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
71COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
72
73RUN chown -R nextjs:nodejs /app
74
75USER nextjs
76
77EXPOSE 3000
78
79ENV PORT 3000
80
81CMD HOSTNAME="0.0.0.0" node server.js
82

Next create a script to build the docker:

1#!/bin/bash
2export $(cat .env | grep "=" | xargs) && \
3docker buildx build --platform linux/amd64,linux/arm64 \
4 --build-arg DATABASE_URI=$DATABASE_URI \
5 --build-arg PAYLOAD_SECRET=$PAYLOAD_SECRET \
6 --build-arg PAYLOAD_PUBLIC_SERVER_URL=$PAYLOAD_PUBLIC_SERVER_URL \
7 --build-arg NEXT_PUBLIC_SERVER_URL=$NEXT_PUBLIC_SERVER_URL \
8 --build-arg NEXT_PUBLIC_IS_LIVE=$NEXT_PUBLIC_IS_LIVE \
9 --build-arg PAYLOAD_PUBLIC_DRAFT_SECRET=$PAYLOAD_PUBLIC_DRAFT_SECRET \
10 --build-arg NEXT_PRIVATE_DRAFT_SECRET=$NEXT_PRIVATE_DRAFT_SECRET \
11 --build-arg REVALIDATION_KEY=$REVALIDATION_KEY \
12 --build-arg NEXT_PRIVATE_REVALIDATION_KEY=$NEXT_PRIVATE_REVALIDATION_KEY \
13 -t your_docker_name/appname_app:latest --push .
14

The server side is done the same as my previous post on creating a docker image

Preston Garrison