Installing nyxd on Linux
nyxd runs on any Linux host that meets the kernel and tooling requirements below (desktop, VM, VPS, edge box, ARM or x86). It was first written for a home Raspberry Pi so containers could run without Docker or a heavy daemon; that same footprint applies everywhere you want a minimal stack.
Typical setups: run nyxd on the host and nyx locally, or call the Unix-socket HTTP API from another machine you trust. For macOS/Windows machines and rootful vs rootless feature differences, see Platform matrix.
What you need on each Linux host
| Requirement | Notes |
|---|---|
| Linux with overlayfs, namespaces, cgroups | Debian, Fedora, Ubuntu, Raspberry Pi OS 64-bit, cloud images, etc. |
Root for nyxd | Overlay, netns, and crun state require root today. |
| crun | apt install crun, dnf install crun, or build from source. |
| Kernel modules | See kernel requirements — overlay, bridge, veth, br_netfilter, nftables NAT, etc. |
| nft (optional but recommended) | Used for port maps with the native network driver. |
| Go 1.27+ (build only) | On the target host or cross-compile from a dev machine. |
Networking defaults to -net-driver=native (no /opt/cni/bin). See networking.
Quick install
One command per platform. On Linux it adds the nyxd package repository for your package manager and
installs from it (so your system's upgrades keep nyxd current); on macOS and Windows it downloads the
release and checks it against the signed checksums.txt. Either way nyxd is set up as a service that
starts at boot. It takes well under a minute; Get started walks through it for your system,
up to your first container.
# Linux and macOS
curl -fsSL https://pkgs.nyxd.io/install.sh | sh
# Windows (PowerShell, no admin rights needed once WSL is enabled)
irm https://pkgs.nyxd.io/install.ps1 | iex
What it does:
| Platform | Installs | Service |
|---|---|---|
| Debian, Ubuntu, Fedora, RHEL, Rocky, openSUSE, Arch, Alpine | the nyxd package repository (below) and the nyxd package from it, so upgrades and removal go through your package manager; crun comes as a dependency | enabled and started for the init system in use |
| Any other Linux (Void, Gentoo, Slackware, ...) | nyxd, nyx in /usr/local/bin; installs crun when it can | same |
Linux, --rootless | nyxd, nyx in ~/.local/bin, no root | systemd user service |
| macOS | nyx in /usr/local/bin, vfkit (Homebrew), the nyx VM that runs nyxd | launchd agent runs nyx machine run at login (macos.md) |
| Windows | nyx.exe in %LOCALAPPDATA%\Programs\nyx (added to PATH), the nyx WSL2 distro | logon task "nyx machine" runs nyx machine start |
The service is set up for systemd, OpenRC, runit or SysV init, whichever the machine runs (no
running init, as in a container: it says how to start nyxd by hand). Options:
curl -fsSL .../install.sh | sh -s -- --version v0.10.0 # a given release
curl -fsSL .../install.sh | sh -s -- --method package # the release package once, no repository
curl -fsSL .../install.sh | sh -s -- --method binary # skip the native package
curl -fsSL .../install.sh | sh -s -- --rootless # per user, no root daemon
curl -fsSL .../install.sh | sh -s -- --no-start # set up and enable, do not start
curl -fsSL .../install.sh | sh -s -- --uninstall [--purge] # --purge also deletes images and containers
Afterwards, let your user talk to the daemon without sudo (log in again afterwards):
sudo usermod -aG nyx "$USER" # Alpine: sudo addgroup "$USER" nyx
nyx ping
Package repositories
The quick install adds these for you. To set one up by hand (or in configuration management), with the
same signing key as the releases (fingerprint C910 E8A2 2680 CCCB BB7E 6ED2 F0DC FE8A 242B 1E99):
Debian, Ubuntu (amd64, arm64)
sudo curl -fsSLo /usr/share/keyrings/nyxd.gpg https://pkgs.nyxd.io/nyxd.gpg
echo "deb [signed-by=/usr/share/keyrings/nyxd.gpg] https://pkgs.nyxd.io/apt stable main" | sudo tee /etc/apt/sources.list.d/nyxd.list
sudo apt-get update && sudo apt-get install -y nyxd
Fedora, RHEL, Rocky, AlmaLinux, CentOS Stream (x86_64, aarch64)
sudo curl -fsSLo /etc/yum.repos.d/nyxd.repo https://pkgs.nyxd.io/rpm/nyxd.repo
sudo dnf install -y nyxd # dnf imports the key on first use
openSUSE, SLES
sudo zypper addrepo --refresh https://pkgs.nyxd.io/rpm/nyxd.repo
sudo zypper --gpg-auto-import-keys install -y nyxd
Alpine (x86_64, aarch64)
sudo wget -qO /etc/apk/keys/nyxd.rsa.pub https://pkgs.nyxd.io/alpine/nyxd.rsa.pub
echo https://pkgs.nyxd.io/alpine | sudo tee -a /etc/apk/repositories
sudo apk add nyxd
Arch Linux (x86_64; aarch64 for Arch Linux ARM)
curl -fsSL https://pkgs.nyxd.io/nyxd.asc | sudo pacman-key --add -
sudo pacman-key --lsign-key C910E8A22680CCCBBB7E6ED2F0DCFE8A242B1E99
printf '[nyxd]\nServer = https://pkgs.nyxd.io/arch/$arch\n' | sudo tee -a /etc/pacman.conf
sudo pacman -Sy nyxd
From then on apt upgrade, dnf upgrade, zypper update, apk upgrade or pacman -Syu installs new nyxd
releases. The repositories keep the last five releases.
Install from a release package
Each release has signed packages for amd64 and arm64.
They install nyxd and nyx to /usr/bin, pull in crun, create the nyx group, and install, enable and
start the service for your init system (an upgrade restarts it, removal stops and disables it). Check the
signature first (verifying downloads).
sudo apt install ./nyxd_<version>_amd64.deb # Debian, Ubuntu
sudo dnf install ./nyxd-<version>-1.x86_64.rpm # Fedora, RHEL, Rocky, Alma
sudo pacman -U nyxd-<version>-1-x86_64.pkg.tar.zst # Arch Linux
sudo apk add --allow-untrusted ./nyxd_<version>_x86_64.apk # Alpine (after checking its .sig)
To install without starting it (images built for later, configuration first), put NYXD_NO_START=1 in
/etc/default/nyxd before installing.
Managing the service
Settings live in /etc/default/nyxd for every init system; restart after a change:
NYXD_OPTS="-log-level info -net-driver native" # any nyxd flags (nyxd -h)
NYXD_NO_START=0 # 1: package installs/upgrades do not (re)start it
| Init system | Status / restart | Logs |
|---|---|---|
| systemd | systemctl status nyxd / systemctl restart nyxd | journalctl -u nyxd |
| OpenRC | rc-service nyxd status / rc-service nyxd restart | /var/log/nyxd.log |
| runit | sv status nyxd / sv restart nyxd | runit's log (stderr) |
| SysV | /etc/init.d/nyxd status / /etc/init.d/nyxd restart | /var/log/nyxd.log |
/usr/lib/nyxd/nyxd-service (/usr/local/lib/nyxd/ for install.sh binary installs) does the same on any of
them: nyxd-service status|restart|setup|teardown|detect.
Install to /usr/bin
From a repo build:
sudo make install-usr
# equivalent: sudo make install BINDIR=/usr/bin
Rootless (no root at all)
Run nyxd as your normal user and it switches to rootless mode on its own: it creates a user namespace,
keeps data in ~/.local/share/nyxd, and listens on $XDG_RUNTIME_DIR/nyxd/nyxd.sock, which nyx finds
automatically. Requirements, limits and the systemd user unit are in Rootless.
Windows
On Windows, nyx.exe runs nyxd in its own small WSL2 distro: nyx machine init then nyx machine start.
See Windows.
Use nyx without sudo (rootful daemon)
With a rootful nyxd, the nyx client can run as a normal user if it can open the control socket.
-
Create a POSIX group (once), e.g.
nyxd:sudo getent group nyxd >/dev/null || sudo groupadd --system nyxd -
Add your user to that group, then log out and back in (or
newgrp nyxd) soidlistsnyxdamong your groups:sudo usermod -aG nyxd "$USER" -
Start nyxd with
--socket-group=nyxd. The daemon keeps/run/nyxd/nyxd.sockat mode0660andchowns it toroot:nyxd, so anyone in groupnyxdcan connect.sudo nyxd --base-dir=/var/lib/nyxd --socket-group=nyxdWith systemd, append
--socket-group=nyxdtoExecStart=innyxd.service(the group must exist before the unit starts). -
Install
nyx(andnyxd) toPATH, e.g./usr/bin— the binary stays world-executable755; access control is the socket, not the file mode onnyx:sudo make install-usr -
As your user:
nyx ping,nyx ps,nyx run -d …withoutsudo.
Socket-group membership is root-equivalent for container operations: the API accepts
privileged, capability changes, host bind mounts, and host networking. Treat the nyx /
nyxd group like docker group access—only trusted operators. The daemon still runs those
actions as root (or as the rootless userns owner).
Build on the target host
git clone https://github.com/zrougamed/nyxd.git
cd nyxd
make build build-nyx
sudo make install # default: /usr/local/bin
# or system-wide:
sudo make install-usr # nyxd + nyx → /usr/bin
# or: sudo make install BINDIR=/usr/bin
Cross-compile (e.g. arm64 from x86_64)
make build-arm64
# Example: copy to an arm64 host (replace user/hostname/path)
scp bin/nyxd-arm64 myarm64:/tmp/nyxd && ssh myarm64 'sudo install -m 755 /tmp/nyxd /usr/local/bin/nyxd'
Build the nyx client for GOOS=linux GOARCH=arm64 the same way (add a Makefile target if you want both binaries in one shot).
Optional: CNI plugins
Only if you set nyxd -net-driver=cni:
sudo make install-cni
systemd
sudo cp nyxd.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now nyxd
The unit in this repo uses Type=notify but the daemon does not call sd_notify yet — if systemd complains, switch the unit to Type=simple. See comments in nyxd.service.
Data directories
Default --base-dir=/var/lib/nyxd. Ensure the filesystem is persistent (SD card or USB disk), not a tiny tmpfs-only root.
Only one nyxd may use a given base-dir at a time: the daemon takes an exclusive non-blocking flock on {base-dir}/run/nyxd-daemon.lock. A second start exits with an error instead of corrupting state.
Next steps
- Usage —
nyxcommands andcurlexamples - OpenAPI spec — HTTP API contract
- Networking — native vs CNI,
nyx run/ stop behavior - QEMU Alpine — try nyxd inside QEMU with Alpine 3.23 before touching bare metal
Security scanning (dev machine)
From the repo root (install Trivy and Grype first):
make scan-trivy
make scan-grype
# or both:
make scan