Deploying Next.js SaaS applications with Docker provides consistency, scalability, and portability across different environments. This guide covers multi-stage builds, monorepo optimization, Prisma integration, and production deployment strategies used in Fastack.
Why Docker for Next.js SaaS applications?
Docker containerization offers several advantages for Next.js SaaS deployments:
- Consistency - Same environment across development, staging, and production
- Isolation - Application dependencies don't conflict with host system
- Scalability - Easy horizontal scaling with container orchestration
- Portability - Deploy to any Docker-compatible platform (VPS, cloud, Kubernetes)
- Optimization - Multi-stage builds reduce final image size and build time
Fastack includes a production-ready Dockerfile optimized for monorepo architectures with Turborepo, making deployment straightforward and efficient.
Next.js standalone output configuration
Before creating a Dockerfile, configure Next.js to output a standalone build. This creates a minimal production bundle that includes only necessary files:
const nextConfig = {
// Enable standalone output for Docker deployments
output: 'standalone',
transpilePackages: [
'@saas/ui-core',
'@saas/auth',
// ... other workspace packages
],
};
module.exports = nextConfig;The standalone output mode creates a self-contained directory in .next/standalone that includes the Next.js server, required dependencies, and your application code. This significantly reduces the Docker image size compared to copying the entire node_modules directory.
Fastack's Next.js configuration includes transpilePackages to ensure all workspace packages are properly bundled, which is essential for monorepo deployments.
Multi-stage Dockerfile architecture
Fastack uses a multi-stage Dockerfile with three stages: deps, builder, and runner. This approach optimizes build caching and minimizes the final image size:
Stage 1: Dependencies (deps)
The first stage installs all dependencies and generates Prisma Client:
FROM node:20-slim AS base
# Install dependencies only when needed
FROM base AS deps
# Install build dependencies and OpenSSL for Prisma
RUN apt-get update && apt-get install -y --no-install-recommends \
openssl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy package files
COPY package.json package-lock.json* ./
COPY turbo.json ./
COPY tsconfig.json ./
# Copy all package.json files for dependency resolution
COPY packages/*/package.json ./packages/
COPY apps/*/package.json ./apps/
# Install dependencies
RUN npm ci
# Copy all source code for packages (needed for workspace dependencies)
COPY packages ./packages
COPY apps ./apps
# Generate Prisma Client (needed by packages)
WORKDIR /app/packages/database
RUN npx prisma generateThis stage leverages Docker layer caching—if package files don't change, Docker reuses the cached layer, significantly speeding up subsequent builds. Fastack's monorepo structure requires copying all package.json files first to resolve workspace dependencies correctly.
Stage 2: Builder
The builder stage compiles the application using Turborepo:
FROM base AS builder
# Install OpenSSL for Prisma (required during build)
RUN apt-get update && apt-get install -y --no-install-recommends \
openssl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy dependencies from deps stage
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/package.json ./package.json
COPY --from=deps /app/packages ./packages
COPY --from=deps /app/apps ./apps
# Set build arguments
ARG APP_NAME
ARG BUILD_ID
ARG NEXT_PUBLIC_ENV_FILE
# Copy NEXT_PUBLIC env file (created by build script)
COPY nextpublic-build.env ./nextpublic-build.env
ENV APP_NAME=${APP_NAME}
# Regenerate Prisma Client in builder stage
WORKDIR /app/packages/database
RUN npx prisma generate
# Build dependencies first (packages that the app depends on)
WORKDIR /app
RUN npx turbo run build --filter="...${APP_NAME}" --filter="!${APP_NAME}"
# Build the app with NEXT_PUBLIC_* variables
WORKDIR /app/apps/${APP_NAME}
RUN ENV_FILE="/app/nextpublic-build.env"; \
if [ -f "$ENV_FILE" ]; then \
set -a && \
. "$ENV_FILE" && \
set +a && \
npx next build; \
else \
npx next build; \
fiThe builder stage uses Turborepo's --filter flag to build only the necessary dependencies before building the target app. This is crucial for monorepo deployments where you might have multiple apps sharing packages.
Fastack's build process handles NEXT_PUBLIC_* environment variables by sourcing them from a build-time file, ensuring client-side variables are embedded in the bundle during build.
Stage 3: Runner (production)
The final stage creates a minimal production image:
FROM base AS runner
WORKDIR /app
ARG APP_NAME
ENV APP_NAME=${APP_NAME}
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# Install OpenSSL for Prisma (required for Prisma Client to work)
RUN apt-get update && apt-get install -y --no-install-recommends \
openssl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user for security
RUN groupadd --system --gid 1001 nodejs
RUN useradd --system --uid 1001 nextjs
# Copy built application (standalone output)
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
COPY --from=builder --chown=nextjs:nodejs /app/apps/${APP_NAME}/public ./apps/${APP_NAME}/public
# Copy Prisma schema (needed for migrations)
COPY --from=builder --chown=nextjs:nodejs /app/packages/database/prisma ./packages/database/prisma
# Install Prisma CLI for migrations
RUN npm install -g prisma@^5.9.1
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# Start the application
CMD sh -c "node apps/${APP_NAME}/server.js"The runner stage copies only the standalone output, static files, and public assets. It runs as a non-root user nextjs for security and includes Prisma CLI for running migrations at runtime. Fastack's Dockerfile ensures the Prisma schema is available for migrations while keeping the image size minimal.
Handling Prisma in Docker
Prisma requires special consideration in Docker builds due to native binary dependencies:
OpenSSL installation
Prisma Client requires OpenSSL for database connections. Fastack's Dockerfile installs OpenSSL in all three stages:
- deps stage - For generating Prisma Client during dependency installation
- builder stage - For regenerating Prisma Client with correct binary target
- runner stage - For Prisma Client to work at runtime
Regenerating Prisma Client in the builder stage ensures the binary matches the OpenSSL version in the final image, preventing runtime errors.
Prisma migrations at runtime
Fastack includes Prisma CLI in the production image to enable running migrations:
# Run migrations before starting the app
docker run --rm \
--env-file .env.production \
your-app:latest \
sh -c "prisma migrate deploy --schema=packages/database/prisma/schema.prisma && node apps/your-app/server.js"Alternatively, you can run migrations as a separate step in your deployment pipeline before starting the container, which is the recommended approach for production deployments.
Building Docker images
Fastack includes build scripts that handle environment variables and build arguments:
# Build Docker image for a specific app
./deploy/scripts/build.sh --app fastack-boilerplate --build-id $(git rev-parse --short HEAD)
# Build without Docker (for local testing)
./deploy/scripts/build.sh --app fastack-boilerplate --no-dockerThe build script extracts NEXT_PUBLIC_* variables from your environment files and creates a build-time file that's copied into the Docker build context. This ensures client-side environment variables are available during the Next.js build process.
For manual builds, you can use Docker directly:
docker build \
-f deploy/docker/Dockerfile.base \
--build-arg APP_NAME=fastack-boilerplate \
--build-arg BUILD_ID=$(git rev-parse --short HEAD) \
--build-arg NEXT_PUBLIC_ENV_FILE=nextpublic-build.env \
-t fastack-boilerplate:latest \
.Running containers in production
When running your container, ensure environment variables are properly configured:
docker run -d \
--name fastack-app \
--network saas-network \
-p 3000:3000 \
--env-file .env.production \
-e NEXTAUTH_URL_INTERNAL=http://localhost:3000 \
--restart unless-stopped \
fastack-boilerplate:latestKey considerations for production:
- Network isolation - Use Docker networks to isolate containers
- Restart policy -
--restart unless-stoppedensures the container restarts on failure - Internal URLs - Set
NEXTAUTH_URL_INTERNALfor NextAuth.js to use localhost instead of external IP - Environment files - Use
--env-fileto load all environment variables
Optimization strategies
Fastack's Docker setup includes several optimizations:
Layer caching
The Dockerfile structure maximizes cache hits by:
- Copying package files before source code
- Installing dependencies in a separate stage
- Using
npm cifor faster, reproducible installs
Image size reduction
The final image is minimized by:
- Using Next.js standalone output (excludes unnecessary files)
- Multi-stage builds (build tools not included in final image)
- Cleaning apt cache after installing system packages
Monorepo considerations
Fastack's Dockerfile is designed for Turborepo monorepos:
- Workspace dependencies - All packages are copied to maintain workspace links
- Turborepo filtering - Builds only necessary dependencies using
--filterflags - Package transpilation - Next.js config includes all workspace packages in
transpilePackages - Shared Prisma schema - Database package is accessible to all apps in the monorepo
This architecture allows Fastack to deploy multiple apps from the same codebase while sharing common packages, reducing build time and image size.
Best practices and security
Fastack follows Docker security best practices:
- Non-root user - Container runs as
nextjsuser (UID 1001) - Minimal base image - Uses
node:20-slimto reduce attack surface - No build tools in production - Build dependencies are excluded from final image
- Proper file ownership - Files are owned by the non-root user using
--chown
Conclusion
Docker deployment for Next.js SaaS applications provides consistency, scalability, and portability. Fastack's production-ready Dockerfile handles monorepo complexities, Prisma integration, and environment variable management, making deployment straightforward.
By using multi-stage builds, standalone output, and optimized caching strategies, Fastack ensures fast builds and minimal production images. The included build and deployment scripts automate the process, reducing the chance of configuration errors.
Whether deploying to a VPS, cloud platform, or Kubernetes cluster, Fastack's Docker setup provides a solid foundation for production deployments.
Ready to build your SaaS faster?
Get our scalable, production-ready boilerplate to save endless hours of development and setup
