---
name: node2nix
version: 1.0
description: node2nix Skill
---
# node2nix Skill
A specialized skill for converting NPM packages to Nix expressions using node2nix, enabling declarative and reproducible
Node.js package management with Nix.
## Skill Overview
**Purpose**: Provide comprehensive support for using node2nix to generate Nix expressions from NPM packages, handle
dependencies, and integrate Node.js projects with Nix/NixOS.
**Invoke When**:
- Converting npm projects to Nix packages
- Creating Nix derivations for Node.js applications
- Packaging Node.js tools for NixOS
- Managing Node.js dependencies declaratively
- Building reproducible Node.js applications
- Troubleshooting node2nix issues
- Setting up development environments for Node.js
## Core Capabilities
### 1. Installation
#### Via Nix (Recommended)
```bash
# Install node2nix from nixpkgs
nix-env -f '<nixpkgs>' -iA nodePackages.node2nix
# Or with nix profile
nix profile install nixpkgs#nodePackages.node2nix
# Verify installation
node2nix --version
```
#### On NixOS
```nix
# /etc/nixos/configuration.nix
environment.systemPackages = with pkgs; [
nodePackages.node2nix
];
```
#### Via home-manager
```nix
# home.nix
home.packages = with pkgs; [
nodePackages.node2nix
];
```
#### Via npm (Alternative)
```bash
# Global installation
npm install -g node2nix
# Or use npx (no installation)
npx node2nix
```
### 2. Basic Usage
#### Quick Start - Generate from package.json
```bash
# Navigate to your Node.js project
cd my-nodejs-project
# Generate Nix expressions
node2nix
# This creates three files:
# - node-packages.nix (package definitions)
# - node-env.nix (build logic)
# - default.nix (composition expression)
```
#### Build the Package
```bash
# Build the package
nix-build -A package
# Result symlink points to build output
./result/bin/my-app
# Or install to profile
nix-env -f default.nix -iA package
```
#### Generated Files Explained
**default.nix** - Main entry point:
```nix
{ pkgs ? import <nixpkgs> {} }:
let
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
};
in
{
package = nodePackages.package;
shell = nodePackages.shell;
}
```
**node-packages.nix** - Package definitions:
```nix
# Generated by node2nix
# Contains all package definitions with dependencies
{
# ... dependency definitions
"my-package" = nodeEnv.buildNodePackage {
name = "my-package";
version = "1.0.0";
src = ./.;
dependencies = [ ... ];
# ... build configuration
};
}
```
**node-env.nix** - Build environment:
```nix
# Shared build logic for all packages
# Handles npm install, dependency linking, etc.
# Usually not modified directly
```
### 3. Lock File Support
#### Using package-lock.json (npm 5+)
```bash
# Generate with package-lock.json
node2nix -l package-lock.json
# Or explicitly
node2nix --lock package-lock.json
# Ensures exact dependency versions
```
#### Using npm-shrinkwrap.json
```bash
# Generate with shrinkwrap file
node2nix -l npm-shrinkwrap.json
```
#### Using yarn.lock
```bash
# Generate with yarn lock file
node2nix -l yarn.lock
# Note: Requires yarn support in node2nix
```
**Why Use Lock Files?**
- **Reproducibility**: Exact same packages every time
- **Version Pinning**: Lock specific dependency versions
- **Security**: Prevent unexpected updates
- **Consistency**: Same builds across machines
### 4. Development vs Production Dependencies
#### Production Mode (Default)
```bash
# Only install production dependencies
node2nix
# Explicit production mode
node2nix --production
```
#### Development Mode
```bash
# Include devDependencies
node2nix --development
# Useful for building developer tools
node2nix --development -i package.json
```
#### Example: Building a Tool with Dev Dependencies
```bash
# For tools like TypeScript compiler
node2nix --development
# Result includes devDependencies
nix-build -A package
```
### 5. Node.js Version Targeting
#### Default (Node.js 12+)
```bash
# Uses latest LTS Node.js
node2nix
```
#### Node.js 4.x Compatibility
```bash
# Enable Node.js 4.x mode
node2nix -4
# Equivalent to:
node2nix --nodejs-4
```
#### Node.js 6.x
```bash
node2nix -6
```
#### Node.js 8.x
```bash
node2nix -8
```
#### Custom Node.js Version in Generated Package
```nix
# Override Node.js version in default.nix
{ pkgs ? import <nixpkgs> {} }:
let
nodejs = pkgs.nodejs_20; # Use Node.js 20
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
nodejs = nodejs;
};
in
{
package = nodePackages.package;
}
```
### 6. Input Files
#### Custom Input File
```bash
# Use custom package.json location
node2nix -i packages/backend/package.json
# Output to custom directory
node2nix -i package.json -o node-packages.nix
# Custom composition file
node2nix -c my-composition.nix
```
#### Multiple Packages
```bash
# Create a node-packages.json file
cat > node-packages.json <<EOF
[
"express",
"lodash",
"axios"
]
EOF
# Generate from package list
node2nix -i node-packages.json
# Builds packages without package.json
```
#### Supplement File
```bash
# Add extra packages not in dependencies
cat > supplement.json <<EOF
{
"global-tools": {
"pm2": "^5.0.0",
"nodemon": "^2.0.0"
}
}
EOF
# Generate with supplements
node2nix --supplement-input supplement.json
```
### 7. Private Registries & Authentication
#### Private Registry
```bash
# Configure private registry
node2nix
--registry "https://registry.company.com"
--registry-auth-token "YOUR_AUTH_TOKEN"
# With scope
node2nix
--registry "https://registry.company.com"
--registry-scope "@mycompany"
```
#### Multiple Registries
```bash
# Different registries for different scopes
node2nix
--registry "https://public.npm.org"
--registry "https://private.company.com"
--registry-scope "@company"
--registry-auth-token "TOKEN"
```
#### Using .npmrc
```bash
# node2nix respects .npmrc settings
cat > .npmrc <<EOF
@mycompany:registry=https://registry.company.com/
//registry.company.com/:_authToken=YOUR_TOKEN
EOF
node2nix
```
#### Private Git Repositories
```bash
# Enable SSH for private git deps
node2nix --use-fetchgit-private
# For dependencies like:
# "my-lib": "git+ssh://git@github.com/company/lib.git"
```
### 8. Dependency Handling
#### Peer Dependencies
```bash
# Include peer dependencies
node2nix --include-peer-dependencies
# Useful for plugin systems
```
#### Strip Optional Dependencies
```bash
# Remove optional dependencies
node2nix --strip-optional-dependencies
# Helps when optional deps cause build failures
```
#### Bypass Cache
```bash
# Force fresh package metadata fetch
node2nix --bypass-cache
# Useful when registry data is stale
```
#### No Copy DevDependencies
```bash
# Don't copy devDependencies to store
node2nix --no-copy-node-env
# Reduces closure size
```
### 9. Override Mechanism
#### Basic Override
```nix
# default.nix
{ pkgs ? import <nixpkgs> {} }:
let
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
};
in
{
package = nodePackages.package.override {
# Add native dependencies
buildInputs = with pkgs; [
python3
pkgs.cairo
pkgs.pango
];
# Skip npm install phase
dontNpmInstall = true;
# Custom build phase
buildPhase = ''
npm run custom-build
'';
};
}
```
#### Override Specific Package
```nix
{ pkgs ? import <nixpkgs> {} }:
let
nodePackages = (import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
}).override {
# Override for specific dependency
"canvas" = oldAttrs: {
buildInputs = oldAttrs.buildInputs ++ [
pkgs.cairo
pkgs.pango
pkgs.giflib
];
preInstall = ''
export CANVAS_NO_REBUILD=1
'';
};
# Fix bcrypt native module
"bcrypt" = oldAttrs: {
buildInputs = [ pkgs.python3 ];
};
};
in
{
package = nodePackages.package;
}
```
#### Global Override
```nix
# Override all packages
{ pkgs ? import <nixpkgs> {} }:
let
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
# Global overrides
globalBuildInputs = with pkgs; [
python3
pkgs.nodejs.libv8
];
};
in
nodePackages
```
### 10. Development Shell
#### Generate Shell Environment
```bash
# Generate with shell support
node2nix
# Enter development shell
nix-shell -A shell
# Now you can:
# - Modify source code
# - Run npm scripts
# - Test without rebuilding
```
#### Enhanced Shell
```nix
# shell.nix
{ pkgs ? import <nixpkgs> {} }:
let
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
};
in
nodePackages.shell.override {
buildInputs = with pkgs; [
# Additional development tools
nodejs_20
nodePackages.typescript
nodePackages.eslint
nodePackages.prettier
# Native dependencies for development
python3
cairo
pango
];
shellHook = ''
echo "Node.js development environment"
echo "Node version: $(node --version)"
echo "npm version: $(npm --version)"
# Set up node_modules symlink
[ -d node_modules ] || ln -s $NODE_PATH node_modules
# Custom environment variables
export NODE_ENV=development
export DEBUG=*
'';
}
```
#### Using the Shell
```bash
# Enter shell
nix-shell
# Run development server
npm run dev
# Run tests
npm test
# Build
npm run build
```
### 11. Common Patterns
#### Pattern 1: Simple CLI Tool
```bash
# Project structure:
# my-cli/
# ├── package.json
# ├── package-lock.json
# └── bin/
# └── my-cli.js
# Generate Nix expressions
cd my-cli
node2nix -l package-lock.json
# Build
nix-build -A package
# Test
./result/bin/my-cli --version
# Install
nix-env -f default.nix -iA package
```
#### Pattern 2: Web Application
```bash
# Express.js app with dependencies
node2nix -l package-lock.json
# Custom default.nix for systemd service
```
```nix
{ pkgs ? import <nixpkgs> {} }:
let
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
};
app = nodePackages.package;
in
{
inherit app;
# Systemd service
service = pkgs.writeTextFile {
name = "my-app.service";
text = ''
[Unit]
Description=My Node.js App
After=network.target
[Service]
Type=simple
ExecStart=${app}/bin/my-app
Restart=on-failure
Environment="NODE_ENV=production"
[Install]
WantedBy=multi-user.target
'';
};
}
```
#### Pattern 3: Monorepo Package
```bash
# Workspace project
# monorepo/
# ├── package.json
# ├── packages/
# │ ├── app/
# │ │ └── package.json
# │ └── lib/
# │ └── package.json
# Generate from root
node2nix -l package-lock.json
# Or generate per package
cd packages/app
node2nix -l ../../package-lock.json
```
#### Pattern 4: TypeScript Project
```bash
# Include dev dependencies for TypeScript
node2nix --development -l package-lock.json
# Build includes TypeScript compilation
```
```nix
{ pkgs ? import <nixpkgs> {} }:
let
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
};
in
{
package = nodePackages.package.override {
buildPhase = ''
# Compile TypeScript
npm run build
'';
installPhase = ''
mkdir -p $out/bin
cp -r dist/* $out/
# Create wrapper
makeWrapper ${pkgs.nodejs}/bin/node $out/bin/my-app
--add-flags "$out/index.js"
'';
};
}
```
#### Pattern 5: Electron App
```bash
# Electron requires development dependencies
node2nix --development -l package-lock.json
```
```nix
{ pkgs ? import <nixpkgs> {} }:
let
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
};
in
{
package = nodePackages.package.override {
buildInputs = with pkgs; [
# Electron dependencies
xorg.libX11
xorg.libXtst
gtk3
nss
nspr
alsa-lib
cups
dbus
atk
cairo
pango
gdk-pixbuf
gtk3
];
# Don't rebuild native modules
ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
};
}
```
### 12. NixOS Integration
#### Package in NixOS Configuration
```nix
# /etc/nixos/configuration.nix
{ config, pkgs, ... }:
let
myNodeApp = import /path/to/my-app {
inherit pkgs;
};
in
{
environment.systemPackages = [
myNodeApp.package
];
# Or as a systemd service
systemd.services.my-node-app = {
description = "My Node.js Application";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${myNodeApp.package}/bin/my-app";
Restart = "on-failure";
User = "nodejs";
# Security hardening
DynamicUser = true;
ProtectSystem = "strict";
NoNewPrivileges = true;
PrivateTmp = true;
};
environment = {
NODE_ENV = "production";
PORT = "3000";
};
};
}
```
#### Module for Node.js App
```nix
# modules/my-app.nix
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.my-app;
myApp = import ../my-app {
inherit pkgs;
};
in
{
options.services.my-app = {
enable = mkEnableOption "My Node.js App";
port = mkOption {
type = types.int;
default = 3000;
description = "Port to listen on";
};
environment = mkOption {
type = types.attrsOf types.str;
default = {};
description = "Environment variables";
};
};
config = mkIf cfg.enable {
systemd.services.my-app = {
description = "My Node.js Application";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${myApp.package}/bin/my-app";
Restart = "on-failure";
DynamicUser = true;
};
environment = cfg.environment // {
NODE_ENV = "production";
PORT = toString cfg.port;
};
};
networking.firewall.allowedTCPPorts = [ cfg.port ];
};
}
```
### 13. Flake Integration
#### flake.nix for Node.js Project
```nix
{
description = "My Node.js Application";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
};
in
{
packages = {
default = nodePackages.package;
my-app = nodePackages.package;
};
apps.default = {
type = "app";
program = "${nodePackages.package}/bin/my-app";
};
devShells.default = nodePackages.shell.override {
buildInputs = with pkgs; [
nodejs_20
nodePackages.typescript
];
};
}
);
}
```
#### Use Flake
```bash
# Build
nix build
# Run
nix run
# Development shell
nix develop
# Update dependencies
node2nix -l package-lock.json
nix flake lock --update-input nixpkgs
```
### 14. Troubleshooting
#### Issue 1: Native Module Build Failures
**Problem**: Package with native dependencies fails to build
**Solution:**
```nix
{ pkgs ? import <nixpkgs> {} }:
let
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
};
in
{
package = nodePackages.package.override {
buildInputs = with pkgs; [
python3 # For node-gyp
pkgs.nodejs.libv8
# Common native deps
cairo
pango
giflib
libjpeg
libpng
];
# Environment for native builds
NIX_CFLAGS_COMPILE = "-I${pkgs.cairo.dev}/include/cairo";
NIX_LDFLAGS = "-L${pkgs.cairo}/lib";
};
}
```
#### Issue 2: Plugin Discovery Failures
**Problem**: Application can't find plugins/modules
**Solution:**
```bash
# In development shell
nix-shell -A shell
# Create node_modules symlink
ln -s $NODE_PATH node_modules
# Or in shell.nix:
shellHook = ''
[ -d node_modules ] || ln -s $NODE_PATH node_modules
'';
```
#### Issue 3: Package Not Found in Registry
**Problem**: node2nix can't fetch package
**Solution:**
```bash
# Bypass cache
node2nix --bypass-cache
# Or update lock file
npm install
npm update
node2nix -l package-lock.json
```
#### Issue 4: Peer Dependency Issues
**Problem**: Missing peer dependencies
**Solution:**
```bash
# Include peer dependencies
node2nix --include-peer-dependencies -l package-lock.json
```
#### Issue 5: Private Git Repository Access
**Problem**: Can't fetch from private git repos
**Solution:**
```bash
# Enable SSH for git
node2nix --use-fetchgit-private
# Ensure SSH keys are configured
# For declarative builds, use fetchgit with SSH URL override
```
#### Issue 6: Large Closure Size
**Problem**: Generated package has large closure
**Solution:**
```bash
# Production mode (no dev deps)
node2nix --production -l package-lock.json
# Strip optional dependencies
node2nix --strip-optional-dependencies
```
```nix
# Remove unnecessary build dependencies
{
package = nodePackages.package.override {
dontNpmInstall = true;
installPhase = ''
# Install only what's needed
mkdir -p $out
cp -r dist $out/
cp package.json $out/
'';
};
}
```
### 15. Advanced Usage
#### Custom Composition
```bash
# Generate with custom composition file
node2nix -c my-composition.nix
# Allows custom package structure
```
#### Patch Packages
```nix
{ pkgs ? import <nixpkgs> {} }:
let
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
};
in
{
package = nodePackages.package.overrideAttrs (oldAttrs: {
patches = [
./patches/fix-vulnerability.patch
];
postPatch = ''
# Patch package.json
substituteInPlace package.json
--replace "old-version" "new-version"
'';
});
}
```
#### Multi-Platform Support
```nix
{ pkgs ? import <nixpkgs> {} }:
let
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
};
# Platform-specific overrides
package = if pkgs.stdenv.isDarwin
then nodePackages.package.override {
buildInputs = with pkgs.darwin.apple_sdk.frameworks; [
CoreServices
Foundation
];
}
else nodePackages.package;
in
{
inherit package;
}
```
#### Development vs Production Builds
```nix
# default.nix
{ pkgs ? import <nixpkgs> {}
, production ? true
}:
let
nodePackages = import ./node-packages.nix {
inherit pkgs;
inherit (pkgs) system fetchurl fetchgit stdenv lib;
};
in
{
package = nodePackages.package.override {
buildPhase = if production
then ''
export NODE_ENV=production
npm run build
''
else ''
export NODE_ENV=development
npm run build:dev
'';
};
}
```
## Best Practices
### DO ✅
1. **Always use lock files**
```bash
node2nix -l package-lock.json
```
2. **Version control generated files**
```bash
git add node-packages.nix node-env.nix default.nix
git commit -m "Add Nix expressions for Node.js project"
```
3. **Use production mode for deployments**
```bash
node2nix --production -l package-lock.json
```
4. **Override packages with native deps**
```nix
buildInputs = [ pkgs.python3 pkgs.cairo ];
```
5. **Test in nix-shell before building**
```bash
nix-shell -A shell
npm test
```
6. **Pin nixpkgs version**
```nix
pkgs ? import (fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/COMMIT.tar.gz";
sha256 = "...";
}) {}
```
7. **Document overrides and customizations**
```nix
# Override for canvas - requires Cairo
buildInputs = [ pkgs.cairo ];
```
8. **Use flakes for modern projects**
```nix
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
```
9. **Separate development and production configs**
```bash
# dev: node2nix --development
# prod: node2nix --production
```
10. **Keep node2nix up to date**
```bash
nix-env -u nodePackages.node2nix
```
### DON'T ❌
1. **Don't commit node_modules**
```bash
# .gitignore
node_modules/
```
2. **Don't skip lock files**
```bash
# ❌ Bad
node2nix
# ✅ Good
node2nix -l package-lock.json
```
3. **Don't ignore build failures silently**
```nix
# ❌ Bad - hides issues
dontBuild = true;
# ✅ Good - fix the issue
buildInputs = [ requiredDeps ];
```
4. **Don't hardcode paths**
```nix
# ❌ Bad
"/usr/bin/node"
# ✅ Good
"${pkgs.nodejs}/bin/node"
```
5. **Don't mix npm and nix package management**
```bash
# ❌ Don't run npm install manually
# ✅ Let Nix handle it
```
6. **Don't commit secrets**
```bash
# Never commit tokens or auth
# Use environment variables or secrets management
```
7. **Don't skip regeneration after updates**
```bash
# After npm install/update:
node2nix -l package-lock.json
```
## Command Reference
```bash
# Basic usage
node2nix # Generate from package.json
node2nix -l package-lock.json # Use lock file
node2nix --development # Include devDependencies
node2nix -i packages.json # Custom input file
# Node.js versions
node2nix -4 # Node.js 4.x
node2nix -6 # Node.js 6.x
node2nix -8 # Node.js 8.x
# Registry configuration
node2nix --registry URL # Custom registry
node2nix --registry-auth-token TOKEN # Auth token
node2nix --registry-scope SCOPE # Scoped packages
# Dependency handling
node2nix --include-peer-dependencies # Include peers
node2nix --strip-optional-dependencies # Remove optional
node2nix --bypass-cache # Force fresh fetch
# Advanced
node2nix --use-fetchgit-private # Private git repos
node2nix --supplement-input FILE # Additional packages
node2nix --no-copy-node-env # Smaller closure
# Output control
node2nix -o node-packages.nix # Custom output
node2nix -c composition.nix # Custom composition
node2nix -e node-env.nix # Custom environment
# Help
node2nix --help # Show help
node2nix --version # Show version
```
## Success Metrics
- **Reproducible Builds**: Same package.json → same output
- **Declarative**: Everything in Nix expressions
- **Version Controlled**: Generated files tracked in git
- **Integrated**: Works with NixOS, flakes, home-manager
- **Tested**: Builds successfully in clean environment
- **Documented**: Overrides and customizations explained
Ready to convert NPM packages to Nix with node2nix! 📦