Package Next.js App as Nix Derivation and deploy as Service on NixOS
Immutable Next.js Deployments on NixOS: A Flake-Based Service Architecture
Current Situation Analysis
Modern Next.js applications demand deployment strategies that guarantee consistency across development, staging, and production. Traditional approaches often rely on Docker containers or CI/CD pipelines that assemble artifacts dynamically. While functional, these methods introduce drift: Docker images can vary based on base layer updates, and script-based deployments may fail to capture implicit dependencies or environment nuances.
The core pain point is reproducibility. When a Next.js app includes Server-Side Rendering (SSR), API routes, or Middleware, the build output becomes complex. Developers frequently encounter "works on my machine" issues where the production environment differs subtly from the build environment, leading to runtime failures that are difficult to debug.
Nix offers a solution by treating the application as a derivation—a deterministic build instruction that produces a bit-for-bit identical output given the same inputs. By leveraging output: "standalone" in Next.js and packaging the result as a Nix derivation, you eliminate dependency drift entirely. The application, its Node.js runtime, and all npm dependencies are captured in a single, immutable store path. This approach is often overlooked because developers assume Nix is only for system configuration, not application packaging. However, Nix's buildNpmPackage function provides a robust mechanism for bundling Node.js applications with precise control over the build lifecycle.
WOW Moment: Key Findings
The following comparison highlights the operational advantages of using a Nix derivation over traditional containerization for Next.js services.
| Approach | Reproducibility Guarantee | Artifact Size | Dependency Drift | Rollback Complexity |
|---|---|---|---|---|
| Docker Container | Low (Base image updates cause drift) | Large (Includes OS layers, unused deps) | High (npm install varies by cache/time) | Manual image tagging required |
| PM2 / Systemd Script | None (Relies on host environment) | Minimal (Source + node_modules) | Critical (Host node version/npm version matters) | Manual file replacement |
| Nix Derivation | Absolute (Hash-based immutability) | Optimized (Only required deps included) | Zero (Locked via npmDepsHash) | Atomic switch via nixos-rebuild |
Why this matters: The Nix approach ensures that the binary running in production is mathematically identical to the binary built locally. The npmDepsHash mechanism locks the dependency tree, preventing supply-chain surprises. Furthermore, rolling back a deployment is as simple as reverting the flake input and rebuilding, with zero risk of partial updates.
Core Solution
This section outlines the architecture for packaging a Next.js application as a Nix derivation and exposing it as a managed service on NixOS. The solution uses a flake-based workflow to ensure portability and version control.
1. Configure Next.js Standalone Output
Next.js must be configured to generate a standalone output. This mode creates a minimal production bundle containing only the necessary files to run the server, excluding development dependencies and source code.
File: next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
// Enables the standalone output mode required for Nix packaging.
// This generates a .next/standalone directory with a self-contained server.
output: "standalone",
};
export default nextConfig;
Rationale: The standalone output is mandatory. Without it, the build produces a structure that includes the entire node_modules directory and source files, which defeats the purpose of Nix's dependency management and results in bloat
🎉 Mid-Year Sale — Unlock Full Article
Base plan from just $4.99/mo or $49/yr
Sign in to read the full article and unlock all 635+ tutorials.
Sign In / Register — Start Free Trial7-day free trial · Cancel anytime · 30-day money-back
