Skip to content

SourceRegistry/node-lxc

Repository files navigation

node-lxc

Node.js native bindings for LXC (Linux Containers) — a complete, production-ready wrapper around liblxc built with N-API.

npm version license Node.js ≥ 12 Platform: Linux

Features

  • Full lifecycle control — create, start, stop, reboot, shutdown, destroy
  • Async-first — all blocking operations return Promises backed by a libuv thread pool
  • Interactive console — non-blocking console sessions with EventEmitter interface
  • Rich configuration API — get, set, clear, and bulk-read container configuration
  • Snapshot support — create, list, restore, and destroy snapshots
  • CRIU integration — checkpoint, restore, and live-migrate containers
  • Mount injection — bind-mount paths into running containers without restart
  • Resource metrics — async cgroup stat reads (memory, CPU, block I/O)
  • Network management — attach/detach interfaces, query IPs and interfaces
  • Device hotplug — add/remove device nodes into running containers
  • Seccomp support — retrieve seccomp notification file descriptors
  • Memory-safe — no leaks; all heap buffers freed, all async captures by value
  • TypeScript — full type declarations included

Requirements

  • Node.js ≥ 12.17
  • LXC ≥ 4.0 (runtime library only)

Install LXC on Debian/Ubuntu:

sudo apt install lxc

Verify your kernel supports LXC features:

lxc-checkconfig

Installation

npm install node-lxc

Pre-built binaries are included for the following platforms — no compilation step required:

Architecture GNU triplet
x86-64 x86_64-linux-gnu
ARM64 aarch64-linux-gnu

Other Linux architectures must be built from source.

Quick Start

import { Container, GetVersion } from "node-lxc";

console.log("LXC", GetVersion()); // e.g. "5.0.2"

// Create and start a container
const c = new Container("my-container");

await c.create({
    template: "download",
    argv: ["--dist", "ubuntu", "--release", "jammy", "--arch", "amd64"],
});

c.setConfigItem("lxc.net.0.type", "veth");
c.setConfigItem("lxc.net.0.link", "lxcbr0");
c.setConfigItem("lxc.net.0.flags", "up");

await c.start();
console.log(c.state);   // "RUNNING"
console.log(c.initPID); // e.g. 12345

// Run a command and get the exit code
const code = await c.exec({ argv: ["/bin/sh", "-c", "echo hello"] });
console.log(code); // 0

await c.shutdown(30);
await c.destroy();

API

Global functions

import {
    GetVersion,               // → string
    GetGlobalConfigItem,      // (key: string) → string
    ListAllContainers,        // (lxcpath?: string) → string[]
    ListAllDefinedContainers, // (lxcpath?: string) → string[]
    ListAllActiveContainers,  // (lxcpath?: string) → string[]
    ConfigItemIsSupported,    // (key: string) → boolean
    HasApiExtension,          // (extension: string) → boolean
    GetWaitStates,            // () → string[]
} from "node-lxc";

Container constructor

const c = new Container(name: string, configPath?: string, alt_file?: string);
Parameter Description
name Container name
configPath LXC container directory (defaults to lxc.lxcpath)
alt_file Alternate configuration file path

Lifecycle

await c.create({ template?, argv?, bdevtype?, bdev_specs?, flags? });
await c.start(useinit?, argv?);
await c.stop();
await c.reboot(timeout?);          // → Promise<boolean>
await c.shutdown(timeout?);        // → Promise<boolean>
await c.freeze();
await c.unfreeze();
await c.destroy({ include_snapshots?, force? });
await c.clone(options);            // → Promise<Container>

State and introspection

c.name                   // string
c.defined                // boolean
c.running                // boolean
c.state                  // ContainerState ("RUNNING" | "STOPPED" | ...)
c.initPID                // number (-1 if not running)
c.error                  // { num: number, string: string | null }
c.daemonize              // boolean (get/set)
c.configPath             // string (get/set)
c.configFileName         // string
c.mayControl()           // → boolean
await c.initPIDFd()      // → number (pidfd)
await c.devptsFd()       // → number

Configuration

c.getConfigItem(key)              // → string | null
c.setConfigItem(key, value)
c.clearConfigItem(key)
c.clearConfig()
c.getRunningConfigItem(key)       // → string | null (live, running container)
c.getKeys(prefix?)                // → string[]
c.getConfigItems(prefix?)         // → Record<string, string | null>
await c.loadConfig(alt_file)
await c.save(alt_file)

Execution and console

// Run a command; returns its exit code
await c.exec({ argv: string[], ...lxc_attach_options })  // → number

// Run a command and capture all output
const { exitCode, stdout, stderr } = await c.execOutput({ argv: ["/bin/hostname"] });

// Run a command and stream output as events
const session = await c.execAsync({ argv: ["/usr/bin/top", "-b", "-n1"] });
session
  .on("stdout", (chunk: Buffer) => process.stdout.write(chunk))
  .on("stderr", (chunk: Buffer) => process.stderr.write(chunk))
  .on("exit",   (code: number)  => console.log("exit:", code));
session.kill();          // send SIGTERM
session.kill(9);         // send SIGKILL

// Open a shell; returns the shell's exit code
await c.attach(options?)                                  // → number

// Non-blocking async console session (EventEmitter)
const session = await c.consoleAsync(ttynum?)  // → ConsoleSession
session.on("data", (chunk: Buffer) => { ... });
session.on("close", () => { ... });
session.write(data);
session.resize(cols, rows);
session.close();

// Blocking console (connects current stdio)
await c.console(ttynum, [stdinfd, stdoutfd, stderrfd], escape);

// Raw TTY allocation
const [ttyfd, ptxfd] = await c.consoleGetFds(ttynum?);

Resource metrics

const stats: ContainerStats = await c.stats();
// {
//   "memory.usage_in_bytes":             string | null,
//   "memory.limit_in_bytes":             string | null,
//   "memory.memsw.usage_in_bytes":       string | null,
//   "cpuacct.usage":                     string | null,
//   "cpu.stat":                          string | null,
//   "blkio.throttle.io_service_bytes":   string | null,
// }

Network

await c.getInterfaces()                              // → string[]
await c.getIPs(iface, "inet")                        // → string[] (IPv4)
await c.getIPs(iface, "inet6", scope)                // → string[] (IPv6)
await c.attachInterface(dev, dst_dev?)
await c.detachInterface(dev, dst_dev?)

Devices and cgroups

await c.addDeviceNode(src_path, dest_path?)
await c.removeDeviceNode(src_path, dest_path?)
c.getCGroupItem(subsys)              // → string | undefined
c.setCGroupItem(subsys, value)

Snapshots

await c.snapshot(commentfile)                      // → number (snap index)
await c.snapshotList()                             // → lxc_snapshot[]
await c.snapshotRestore(snapname, newname?)
await c.snapshotDestroy(snapname)
await c.snapshotDestroy(true)                      // destroy all

Mount injection (running container)

await c.mount(source, target, filesystemtype, mountflags, mnt)
await c.umount(source, mountflags, mnt)

CRIU checkpoint / restore / migrate

await c.checkpoint(directory, stop?, verbose?)
await c.restore(directory, verbose?)
await c.migrate(cmd: LXC_MIGRATE, options?)

Seccomp

await c.seccompNotifyFd()        // → number
await c.seccompNotifyFdActive()  // → number

Console ring buffer

await c.consoleLog({ clear?, read?, read_max? })  // → string

Examples

The examples/ directory contains runnable scripts for common use cases:

Script Description
examples/create/ Create a container from a template
examples/start/ Start a container
examples/execute/ Run a command in a container
examples/attach/ Open a shell in a container
examples/console_async/ Non-blocking console session
examples/clone/ Clone a container
examples/checkpoint/ CRIU checkpoint/restore
examples/concurrent_create/ Concurrent container creation
examples/stats/ Read cgroup resource metrics
examples/images/ Browse available LXC images

Run any example with ts-node:

npm run example:create
npm run example:execute
npm run example:console_async

Building from Source

Required additional dependencies:

Tool Version
liblxc-dev same as LXC
g++ ≥ 7
cmake any recent
sudo apt install lxc lxc-dev g++ cmake

Clone and build:

git clone https://github.com/SourceRegistry/node-lxc.git
cd node-lxc
npm install
npx node-gyp configure && npx node-gyp build
npx tsc --build

Testing

npm test

The test suite uses Node.js's built-in node:test runner. Tests that require root access (container create/start/stop/destroy) are automatically skipped unless the process is running as root.

To run the full integration test suite:

sudo npm test

Contributing

Bug reports and pull requests are welcome on GitHub.

Please ensure:

  • New C++ code uses std::unique_ptr<char[]> for heap buffers
  • Async lambda captures use by-value ([this, var] not [this, &var])
  • All public API changes include TypeScript type updates and JSDoc

License

Apache 2.0 © 2026 ProjectSource V.O.F.

See Also