Manual NQ2 installation on Linux
Commands for Ubuntu and Raspberry Pi OS: Node.js 24, NQ2, PM2, optional Nginx, MQTT and Zigbee2MQTT.
Perform the steps yourself instead of running the NQ2 installer. These commands match the current installer defaults: NQ2 latest and Zigbee2MQTT 2.14.2.
Order: dependencies → NQ2 → Nginx → Mosquitto → Zigbee2MQTT. NQ2 and common dependencies are required. Nginx, Mosquitto and Zigbee2MQTT are optional and enabled by default. Skip sections marked “optional” to omit a component. Without Nginx, use port 8080. Without Mosquitto, use an existing broker and enter its address and credentials in Zigbee2MQTT configuration. Common tools (Node.js, PM2, Corepack and build tools) are installed first; Zigbee2MQTT npm dependencies and compilation follow its source download at the end.
1. Check the existing system
This guide is for a new application installation on running Ubuntu 22.04/24.04 or Raspberry Pi OS / Debian 12 or 13 with systemd. Raspberry Pi OS Lite 64-bit on a Raspberry Pi 4 is the example. Execute commands in Bash on the target device as a regular sudo-enabled user, not root. Internet access and a working network are required; for SSH, sign in with your existing account.
If you do not have an OS yet, follow steps 1–13 of the card guide, set the account, network and SSH, then boot the Pi without adding the NQ2 script and return here. Do not run raspianimager.cmd or install.sh alongside this method. Alternatively, use the existing-system installer.
Inspect existing services and ports before running commands. This is not an upgrade of an existing NQ2 installation. /opt/nq2 and (if installing Zigbee2MQTT) /opt/zigbee2mqtt must be unused. If you already use Nginx or Mosquitto, preserve their configuration and skip the relevant sections rather than overwriting active files. You need Python 3.10+ and hardware supported by Node.js 24 and the application’s dependencies. A Zigbee coordinator is required only for Zigbee2MQTT.
If a command fails, stop and resolve the cause before continuing. Set your own passwords before first starting the application. First check the account and architecture:
whoami
dpkg --print-architecture
hostname
2. Install packages and Node.js 24
These commands install dependencies and download the NodeSource repository
setup script. You can inspect it with less before running it (press q to exit).
It is a third-party setup script, not the NQ2 installer. Node.js is fixed to
major version 24, matching the current installer. The manual steps also use
the additional unzip and nano packages.
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg git make g++ gcc libsystemd-dev avahi-daemon libnss-mdns logrotate unzip nano openssl python3
curl -fsSL --retry 3 -o "$HOME/nodesource-setup-24.sh" https://deb.nodesource.com/setup_24.x
less "$HOME/nodesource-setup-24.sh"
sudo bash "$HOME/nodesource-setup-24.sh"
sudo apt-get install -y nodejs
sudo sh -c 'umask 022; npm install -g corepack pm2'
sudo corepack enable
corepack prepare pnpm@11.26.0 --activate
sudo systemctl enable --now avahi-daemon
node --version
pm2 --version
Wi-Fi: disable power saving
If the Raspberry Pi or Linux server uses Wi-Fi, disable Wi-Fi power saving to address intermittent disconnections after idle periods. The device can appear switched off while it is still running. The current NQ2 installer configures this automatically on systems using NetworkManager. For a manual installation or an older bundle, follow the Wi-Fi configuration and verification guide. Ethernet does not require this Wi-Fi change.
3. Download NQ2 latest and run npm install
The latest ZIP is dynamic, so this guide does not pin a fixed archive checksum. After extraction run npm install as your regular user. The commands below locate package.json even inside a wrapper folder. If downloading returns 404, the archive has not been published: stop installation. /opt/nq2 must not already exist.
mkdir "$HOME/nq2-manual-download"
cd "$HOME/nq2-manual-download"
curl -fL --retry 3 -o nq2.zip https://nq2.io/api/apps/nq2-local/download.zip
unzip nq2.zip -d unpack
sudo mkdir /opt/nq2
sudo chown "$(id -un):$(id -gn)" /opt/nq2
python3 - <<'PYCODE'
from pathlib import Path
import shutil
root = Path('unpack')
packages = [p.parent for p in root.rglob('package.json') if 'node_modules' not in p.relative_to(root).parts]
if not packages: raise SystemExit('No package.json in NQ2 ZIP')
depth = min(len(p.relative_to(root).parts) for p in packages)
packages = [p for p in packages if len(p.relative_to(root).parts) == depth]
if len(packages) != 1: raise SystemExit('Ambiguous NQ2 package root')
shutil.copytree(packages[0], '/opt/nq2', dirs_exist_ok=True)
PYCODE
cd /opt/nq2
npm install --no-audit --no-fund
4. Configure NQ2 in .env
Create .env in the application directory /opt/nq2. Use KEY=value syntax, without JSON braces or commas. NQ2 reads this file at startup. The application administrator account is separate from SSH and MQTT.
cd /opt/nq2
umask 077
cat > .env <<'EOF'
NODE_ENV=production
PORT=8080
ADMIN_EMAIL=admin@admin.local
ADMIN_PASSWORD=admin123
EOF
chmod 600 .env
nano .env
Before first start, set your own administrator email and password. In nano, save with Ctrl+O, Enter and exit with Ctrl+X. Quote passwords containing # or spaces. Do not distribute this file.
5. Start NQ2 in PM2 and enable startup
Run PM2 commands as the same regular account. Start the application from its directory, save the process list and register PM2 for systemd startup:
cd /opt/nq2
pm2 start index.js --name nq2
pm2 save
sudo env PATH="$PATH" pm2 startup systemd -u "$(id -un)" --hp "$HOME"
sudo mkdir -p "/etc/systemd/system/pm2-$(id -un).service.d"
sudo tee "/etc/systemd/system/pm2-$(id -un).service.d/network.conf" > /dev/null <<'EOF'
[Unit]
Wants=network-online.target
After=network-online.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable "pm2-$(id -un)"
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:compress true
pm2 set pm2-logrotate:workerInterval 30
pm2 save
pm2 list
PM2 remembers the application working directory. Configure startup once for this user; a later pm2 save also includes Zigbee2MQTT added at the end. Do not use sudo pm2 start or sudo pm2 save. PM2 startup documentation.
Remove initial administrator settings after signing in
After the first startup and a successful sign-in to NQ2, remove ADMIN_EMAIL and ADMIN_PASSWORD from the application’s .env file. They are needed only during the first startup to create the administrator account. Keep the runtime settings, for example:
NODE_ENV=production
PORT=8080
Edit /opt/nq2/.env on Linux or C:\ProgramData\NQ2\apps\nq2\.env on a default Windows installation. Do this after confirming that your administrator login works.
6. Limit other logs
pm2-logrotate handles application logs. These settings also rotate the PM2 daemon log and limit the system journal.
sudo tee /etc/logrotate.d/nq2-pm2 > /dev/null <<EOF
$HOME/.pm2/pm2.log {
su $(id -un) $(id -gn)
daily
maxsize 10M
rotate 7
compress
missingok
notifempty
copytruncate
}
EOF
sudo mkdir -p /etc/systemd/journald.conf.d
sudo tee /etc/systemd/journald.conf.d/nq2.conf > /dev/null <<'EOF'
[Journal]
SystemMaxUse=100M
RuntimeMaxUse=50M
EOF
sudo systemctl restart systemd-journald
7. Nginx — port 80 to NQ2 on 8080 — optional
This block assumes a fresh system without other Nginx sites. proxy_pass
forwards requests to NQ2 without changing the browser address. Restart Nginx
only after nginx -t succeeds. Avahi advertises the .local hostname.
sudo apt-get install -y nginx
sudo tee /etc/nginx/sites-available/nq2 > /dev/null <<'EOF'
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
}
}
EOF
sudo rm -f /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/nq2 /etc/nginx/sites-enabled/nq2
sudo nginx -t
sudo systemctl enable nginx avahi-daemon
sudo systemctl restart nginx avahi-daemon
8. Mosquitto — MQTT username and password — optional
This example uses the MQTT username nq2; change it in the command if needed.
mosquitto_passwd asks for the password twice. Enter your own password or nq2
to use the package default. Use the same credentials in Zigbee2MQTT.
Use -c only when creating the password file on a fresh system: it overwrites
an existing password file. Mosquitto authentication documentation.
By default, the broker listens only on 127.0.0.1:1883 and requires authentication,
matching mqtt_allow_remote: false in config.yaml. In a manual installation,
to allow other computers, replace listener 1883 127.0.0.1 below with
listener 1883 0.0.0.0. Clients use the server IP and MQTT credentials.
The equivalent installer option is mqtt_allow_remote: true. Editing config.yaml
alone does not change an already installed broker. This does not assign a public
IP address, configure router forwarding or change existing firewall rules.
sudo apt-get install -y mosquitto mosquitto-clients
sudo mosquitto_passwd -c /etc/mosquitto/nq2.passwd nq2
sudo chown root:mosquitto /etc/mosquitto/nq2.passwd
sudo chmod 640 /etc/mosquitto/nq2.passwd
sudo tee /etc/mosquitto/conf.d/nq2.conf > /dev/null <<'EOF'
listener 1883 127.0.0.1
allow_anonymous false
password_file /etc/mosquitto/nq2.passwd
EOF
sudo systemctl enable mosquitto
sudo systemctl restart mosquitto
sudo systemctl is-active mosquitto
sudo tee "/etc/systemd/system/pm2-$(id -un).service.d/network.conf" > /dev/null <<'EOF'
[Unit]
Wants=network-online.target mosquitto.service
After=network-online.target mosquitto.service
EOF
sudo systemctl daemon-reload
9. Install Zigbee2MQTT 2.14.2 — optional
Access to the Zigbee coordinator
Add your account to dialout. Then disconnect from SSH and log back in
using the same account so the new group membership takes effect.
sudo usermod -aG dialout "$(id -un)"
exit
After reconnecting over SSH, continue installation as the same account. Skip the dialout group step for a network coordinator.
Download and dependencies
Version 2.14.2 matches the current package. Run pnpm without sudo,
using your Imager account. Corepack reads the package manager version from
the repository and may ask to download it on first use.
Zigbee2MQTT installation documentation.
sudo git clone --depth 1 --branch 2.14.2 https://github.com/Koenkk/zigbee2mqtt.git /opt/zigbee2mqtt
sudo chown -R "$(id -un):$(id -gn)" /opt/zigbee2mqtt
cd /opt/zigbee2mqtt
pnpm install --frozen-lockfile
pnpm run build
mkdir -p data
nano data/configuration.yaml
10. Configure Zigbee2MQTT — optional
Paste this configuration into the editor. Set user and password to the
broker credentials from step 8. In nano, save with Ctrl+O, Enter and exit with Ctrl+X.
Inside a single-quoted YAML password, escape an apostrophe by doubling it,
for example 'password''with apostrophe'. After saving, run
chmod 600 /opt/zigbee2mqtt/data/configuration.yaml.
onboarding: true allows you to select the coordinator on first start.
Use a stable /dev/serial/by-id/... path and the correct adapter type
(such as ember or zstack, depending on the hardware).
version: 5
onboarding: true
mqtt:
server: mqtt://127.0.0.1:1883
base_topic: zigbee2mqtt
user: 'nq2'
password: 'nq2'
frontend:
enabled: true
host: 0.0.0.0
port: 8090
advanced:
log_output: [console]
network_key: GENERATE
pan_id: GENERATE
ext_pan_id: GENERATE
11. Start Zigbee2MQTT in PM2 — optional
Only now add Zigbee2MQTT to PM2 using the same account as NQ2. Z2M_ONBOARD_URL sets the first-run onboarding screen to port 8090. Skip this step if you are not installing Zigbee2MQTT.
cd /opt/zigbee2mqtt
export Z2M_ONBOARD_URL="http://0.0.0.0:8090"
pm2 start index.js --name zigbee2mqtt
pm2 save
unset Z2M_ONBOARD_URL
pm2 list
NQ2 remains running. The PM2 startup service from step 5 is shared by both applications; do not register it again. pm2 save records the list containing nq2 and zigbee2mqtt for restoration at boot.
12. Check operation and reboot
Without Nginx, the NQ2 address is http://nq2.local:8080 (use your actual hostname).
If Zigbee2MQTT is disabled, skip its port 8090 and coordinator checks.
If the Imager hostname is nq2, open http://nq2.local for NQ2 and
http://nq2.local:8090 for Zigbee2MQTT. Substitute your actual hostname if different.
You can also use the IP address shown by your router. .local requires mDNS
support on the client. Complete coordinator selection and MQTT setup from step 8
in Zigbee2MQTT. A missing coordinator or incorrect adapter type may prevent Zigbee
from starting.
Check processes and the NQ2 HTTP response below. Then reboot the Pi, log back in
and repeat pm2 list and the checks of both web interfaces.
pm2 list
sudo systemctl is-active avahi-daemon
curl -I http://127.0.0.1:8080/
sudo reboot
13. Troubleshooting and changing settings
Use the commands below to inspect application logs; press Ctrl+C to stop following.
After editing settings in /opt/nq2/.env, run:
pm2 restart nq2
pm2 save
NQ2 reads .env again. Set ADMIN_EMAIL and ADMIN_PASSWORD before first start; editing them later is not a reset procedure for an existing administrator account. After changing Zigbee2MQTT configuration, use pm2 restart zigbee2mqtt and pm2 save.
Do not use sudo pm2 list: it accesses another user’s process list.
This manual method does not create the nq2-firstboot service or automatically
retry installation. After an interruption, return to the incomplete step; inspect
existing directories and password files before repeating commands that create them.
An interrupted package installation may require sudo dpkg --configure -a and
sudo apt-get -f install before retrying the failed step.
Diagnose system account and network issues separately: this guide does not configure
them. Do not distribute files containing your credentials or your private prepared card.
pm2 logs nq2 --lines 100
pm2 logs zigbee2mqtt --lines 100
sudo journalctl -u mosquitto -n 100 --no-pager
sudo journalctl -u nginx -n 100 --no-pager
sudo journalctl -u "pm2-$(id -un)" -n 100 --no-pager