How Next.js Standalone Output Reduces LunaTV's Docker Image Size
Next.js standalone output reduces LunaTV's Docker image size from over 500 MB to approximately 150-200 MB by generating a self-contained bundle in .next/standalone that excludes development dependencies and source code.
LunaTV leverages Next.js standalone output to create production-optimized Docker images that contain only essential runtime assets. By configuring output: 'standalone' in next.config.js and implementing a multi-stage build process, the project eliminates unnecessary build tools and source files from the final container. This approach significantly reduces deployment overhead while maintaining full server functionality according to the MoonTechLab/LunaTV source code.
What Is Next.js Standalone Output?
When enabled in next.config.js, standalone output directs Next.js to emit a self-contained bundle under .next/standalone during the build process. This directory includes the compiled server files and a trimmed node_modules folder containing only production dependencies required to run the application. Unlike standard builds that retain the full source tree and development packages, standalone mode creates a minimal, deployment-ready artifact.
How LunaTV Implements Standalone Output
Enabling Standalone Mode in next.config.js
The configuration resides in the project's root configuration file:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone', // <-- triggers the creation of .next/standalone
reactStrictMode: false,
swcMinify: false,
// … other config …
};
module.exports = require('next-pwa')(nextConfig);
Setting output: 'standalone' instructs the Next.js builder to generate the optimized bundle alongside the standard .next output directory.
Multi-Stage Docker Build Strategy
LunaTV's Dockerfile employs a three-stage build pipeline that isolates build-time dependencies from the final runtime image.
Stage 1: Dependency Installation
The deps stage installs all npm packages required for building the application:
FROM node:20-alpine AS deps
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
This stage includes both production and development dependencies necessary to execute the build process.
Stage 2: Build and Standalone Generation
The builder stage compiles the application and generates the standalone output:
FROM node:20-alpine AS builder
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV DOCKER_ENV=true
RUN pnpm run build // <-- creates .next/standalone
During this phase, Next.js processes the source code and creates the .next/standalone directory containing the trimmed runtime bundle.
Stage 3: Minimal Runtime Image
The runner stage constructs the final production image by copying only essential files from the builder. This selective copying occurs on lines 43-51 of the Dockerfile:
FROM node:20-alpine AS runner
RUN addgroup -g 1001 -S nodejs && adduser -u 1001 -S nextjs -G nodejs
WORKDIR /app
ENV NODE_ENV=production HOSTNAME=0.0.0.0 PORT=3000 DOCKER_ENV=true
# Copy only the standalone output and required assets
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/scripts ./scripts
COPY --from=builder --chown=nextjs:nodejs /app/start.js ./start.js
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
CMD ["node", "start.js"]
The COPY --from=builder directives specifically target the standalone bundle rather than the entire application source, eliminating unnecessary files from the final image.
Image Size Reduction Breakdown
The standalone output achieves size reduction by excluding three major categories of content from the runtime image:
- Source code: The
src/directory and original TypeScript/JavaScript files remain in the builder stage and never reach the runner image. - Development dependencies: Build tools, TypeScript compilers, and testing frameworks installed in the
depsstage are left behind. - Unused node_modules: Only runtime-critical packages required by
server.jsand the application code are included in the standalone bundle.
This approach typically reduces image sizes from over 500 MB to approximately 150-200 MB, representing a 60-70% reduction while preserving full Next.js server capabilities through the custom start.js entry script.
Summary
- Next.js standalone output generates a self-contained bundle in
.next/standalonecontaining only production necessities. - LunaTV's three-stage Dockerfile (deps, builder, runner) ensures build tools never reach the final image.
- The
runnerstage copies exclusively from.next/standalone,public,.next/static, and essential scripts likestart.js. - Image sizes drop from over 500 MB to roughly 150-200 MB by excluding source code and development dependencies.
- Configuration requires only
output: 'standalone'innext.config.jsaccording to the MoonTechLab/LunaTV repository.
Frequently Asked Questions
What is the typical Docker image size reduction when using Next.js standalone output?
Standard Next.js Docker images often exceed 500 MB when they include full node_modules and source code. By implementing standalone output as seen in LunaTV's configuration, production images typically shrink to 150-200 MB, achieving approximately 60-70% size reduction while maintaining all runtime functionality.
Does standalone output include all required runtime dependencies?
Yes. When Next.js generates the .next/standalone directory, it automatically analyzes the server code and copies only the production dependencies required to execute the application. The resulting node_modules folder inside the standalone directory contains exclusively runtime-critical packages, excluding development tools like TypeScript, ESLint, and testing libraries.
Where is the standalone output generated in a Next.js build?
The standalone output is created at .next/standalone within the build directory when output: 'standalone' is configured in next.config.js. During LunaTV's Docker build process, the builder stage generates this directory, and the runner stage copies it using the directive COPY --from=builder /app/.next/standalone ./.
Can I use standalone mode with custom server configurations in LunaTV?
Yes. LunaTV uses a custom start.js script as the container entry point rather than invoking Next.js directly. The standalone bundle supports this pattern because it includes the compiled server code that start.js can import and execute. The Dockerfile copies both the standalone output and the custom startup script into the final image, allowing full control over server initialization while retaining the size benefits of standalone mode.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →