Deploying with Docker
How to deploy next-forge with Docker for self-hosting.
Docker lets you self-host next-forge on any platform that supports containers — such as Railway, Fly.io, Coolify, DigitalOcean App Platform, or your own server.
Enable standalone output
First, you'll need to enable Next.js standalone output in your shared config. This creates a self-contained build that includes only the files needed to run your app, significantly reducing the final image size.
In packages/next-config/index.ts, add the output property:
export const config: NextConfig = {
output: "standalone",
// ... rest of your config
};Create a Dockerfile
Create a Dockerfile in the root of your repository. This uses a multi-stage build to keep the final image small:
FROM oven/bun:1 AS base
# Stage 1: Install dependencies
FROM base AS deps
WORKDIR /app
COPY package.json bun.lock turbo.json ./
COPY apps/ ./apps/
COPY packages/ ./packages/
RUN bun install --frozen-lockfile
# Stage 2: Build the application
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/ ./
# Set the app to build: app, web, or api
ARG APP_NAME=app
ENV APP_NAME=$APP_NAME
# Add build-time environment variables here
# ARG DATABASE_URL
# ENV DATABASE_URL=$DATABASE_URL
RUN bun run build --filter=@repo/${APP_NAME}
# Stage 3: Production runner
FROM node:20-slim AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
# Set the app to run
ARG APP_NAME=app
ENV APP_NAME=$APP_NAME
COPY --from=builder /app/apps/${APP_NAME}/public ./apps/${APP_NAME}/public
COPY --from=builder --chown=nextjs:nodejs /app/apps/${APP_NAME}/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/${APP_NAME}/.next/static ./apps/${APP_NAME}/.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "apps/${APP_NAME}/server.js"]The CMD above uses a shell-interpolated variable. If your container runtime doesn't support this, replace ${APP_NAME} with the actual app name e.g. node apps/app/server.js.
Create a .dockerignore
Add a .dockerignore to speed up builds and keep secrets out of the image:
node_modules
.next
.git
.env
.env.*Build and run
Build and run the image for a specific app by passing the APP_NAME build argument:
# Build the app
docker build --build-arg APP_NAME=app -t next-forge-app .
# Run it
docker run -p 3000:3000 --env-file .env.local next-forge-appRepeat for web and api if you want to deploy all three.
Using Docker Compose
If you'd prefer to run all apps together, create a docker-compose.yml:
services:
app:
build:
context: .
args:
APP_NAME: app
ports:
- "3000:3000"
env_file:
- .env.local
web:
build:
context: .
args:
APP_NAME: web
ports:
- "3001:3000"
env_file:
- .env.local
api:
build:
context: .
args:
APP_NAME: api
ports:
- "3002:3000"
env_file:
- .env.localThen run everything with:
docker compose up --buildEnvironment variables
When deploying with Docker, pass your environment variables at runtime using --env-file or -e flags. Do not bake secrets into the image. Learn more about how environment variables work in next-forge.
If certain variables are needed at build time (e.g. DATABASE_URL for Prisma), uncomment the relevant ARG and ENV lines in the builder stage.