With this method you can package any type of Next.js App including Apps w/ SSR, API Routes, Server Actions and Proxy (Middleware).
Prerequisites
- Obviously Git
- Your Next.js App remote Repository w/ package.json and package-lock.json
- Your NixOS Server
Getting started
- Add the required output mode to your next.config.ts:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone",
};
export default nextConfig;
- Your npmDepsHash is the unique id of your package-lock.json. We will need it for the next step, so prefetch it:
nix run --extra-experimental-features "nix-command flakes" nixpkgs#prefetch-npm-deps package-lock.json
- Create your own flake.nix in the same directory as the package.json, make sure to edit the generic "Your Next.js App" Labels and insert your prefetched npmDepsHash from the last step:
{
description = "Your Next.js App Description";
# Feel free to choose the input channel yourself
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs }:
let
# Support all Nix-capable Systems
supportedSystems = [
"x86_64-linux" # Standard Intel/AMD Linux
"aarch64-linux" # ARM Linux
"x86_64-darwin" # Intel Macs
"aarch64-darwin" # Apple Silicon Macs
];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
in {
packages = forAllSystems (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in {
default = pkgs.buildNpmPackage {
pname = "your-nextjs-app";
version = "1.0.0";
src = ./.;
# Insert your prefetched npmDepsHash from the last step
npmDepsHash = "sha256-...";
postBuild = ''
cp -r public .next/standalone/
cp -r .next/static .next/standalone/.next/
'';
installPhase = ''
cp -r .next/standalone $out
'';
};
}
);
nixosModules.default = { config, lib, pkgs, ... }:
let
cfg = config.services.your-nextjs-app;
basePackage = self.packages.${pkgs.system}.default;
customPackage = basePackage.overrideAttrs (oldAttrs: cfg.environment);
in {
options.services.your-nextjs-app = {
enable = lib.mkEnableOption "Your Next.js App";
environment = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = {};
description = "Key-Value Pairs for Build-Time and Run-Time";
example = {
PORT = "3000";
NODE_ENV = "production";
};
};
};
config = lib.mkIf cfg.enable {
systemd.services.your-nextjs-app = {
description = "Your Next.js App Service";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
environment = cfg.environment;
serviceConfig = {
ExecStart = "${pkgs.nodejs_24}/bin/node ${customPackage}/server.js";
Restart = "always";
};
};
};
};
};
}
With every package update and newly added package to your Next.js App you will need to prefetch your npmDepsHash again (repeat step 2) and update your flake.nix.
- Generate your flake.lock for reproducitbility, commit and push:
nix flake lock --extra-experimental-features "nix-command flakes" && git commit -m "Added flake.nix and flake.lock" && git push
- Update your NixOS Server. Example minimal NixOS Build Flake, keep in mind to replace all generic values:
{
description = "Your NixOS Server";
# Feel free to choose the input channel yourself
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
your-nextjs-app.url = "github:YourUsername/YourRepository";
# Example input with Codeberg SSH:
# "git+ssh://git@codeberg.org/YourUsername/YourRepository.git"
};
outputs = { self, nixpkgs, your-nextjs-app, ... }:
{
nixosConfigurations = {
"your-hostname-x86_64" = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [ configuration.nix ];
};
"your-hostname-aarch64" = nixpkgs.lib.nixosSystem {
system = "aarch64-linux";
modules = [ configuration.nix ];
};
};
};
}
- Use the provided Service of your Package. Example minimal configuration.nix in the same directory as flake.nix from step 5, keep in mind to replace all generic values:
{ config, pkgs, lib, ... }:
{
# Demo User feel free to insert your own
users.users."your-user" = {
isNormalUser = true;
initialPassword = "changeme";
extraGroups = [ "wheel" ];
openssh.authorizedKeys.keys = "Your .pub SSH Key";
};
services.your-nextjs-app = {
enable = true;
# Define all environment variables your App needs
environment = {
PORT = "3000";
NODE_ENV = "production";
};
};
networking = {
hostname = "your-hostname";
firewall = {
enable = true;
# Open the Port used by your Next.js App
allowedTCPPorts = [ 3000 ];
};
}
- Rebuild your NixOS System, keep in mind to replace all generic values:
nixos-rebuild switch --flake /path/to/flakeAndConf#<your-hostname-x86_64> or <your-hostname-aarch64> --extra-experimental-features "nix-command flakes"
- Now the your Next.js App should be available as a Service on your NixOS Server. Enjoy <3
Top comments (0)