5. Firmware and Jenkins¶
The routers run a NINUX OpenWrt firmware built by us: it contains the network packages (BIRD, ZeroTier, uspot, netconsole…), the openwisp-config agent already set up to register by itself with our OpenWISP, the base settings (users, firewall) and, on every device except x86, the luci-theme-ninux LuCI theme with Basic and Advanced modes (chapter 3).
The build is done by the ansible-ninux-openwrt repository: an Ansible role and a Jenkins pipeline that build all the variants and upload them to the OpenWISP firmware upgrader.
flowchart LR
gh["GitHub<br/>ansible-ninux-openwrt"] --> j["Jenkins<br/>NinuxOpenwrt job"]
j --> b["OpenWrt build<br/>(one per target and variant)"]
b --> up["OpenWISP firmware<br/>upgrader (API)"]
b --> rel["GitHub release<br/>(optional)"]
up --> r["routers<br/>(sysupgrade from OpenWISP)"]
1. Jenkins CT¶
Debian 13 CT on Proxmox with nesting=1 (chapter 1).
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 4 cores | 12 cores |
| RAM | 8 GB | 24 GB |
| Disk | 80 GB | 200 GB |
The installation is automatic:
git clone https://github.com/mikysal78/ansible-ninux-openwrt.git
cd ansible-ninux-openwrt
sudo ./setup/install-jenkins.sh
The script installs Java 21, Ansible (pipx), Jenkins LTS, the OpenWrt build
dependencies, the persistent ccache, the tmpfs and the sudo permissions for the
jenkins user. The manual procedure, step by step, is in the
README.
2. Jenkins configuration¶
- Open
http://<CT IP>:8080, enter the initial password (/var/lib/jenkins/secrets/initialAdminPassword), install the suggested plugins and create the administrator. - Additional plugins: Ansible, Timestamper, Build Timeout, Workspace Cleanup.
- Manage Jenkins → Tools → Ansible installations: name
ansible, path of the Ansible executables installed by pipx. - Job: New Item →
NinuxOpenwrt→ Pipeline:- Do not allow concurrent builds, 240 minutes timeout;
- Pipeline script from SCM, Git,
https://github.com/mikysal78/ansible-ninux-openwrt.git, branch*/main, scriptJenkinsfile.
- Vault password in
/var/lib/jenkins/.vault_pass(mode 600, ownerjenkins): it decrypts the secrets inninux.yml.
The job parameters (organization, OpenWrt version, VPN and captive portal variants, upload to OpenWISP…) are described in the README.
Jenkins builds main
The job takes the code from GitHub: firmware changes must be pushed before starting the build.
3. ninux.yml¶
The build configuration is in ninux.yml (from ninux.yml.example):
- OpenWrt version and variants to build (VPN, captive portal);
- for every organization: OpenWISP URL,
management_interface, shared secret for automatic registration (from chapter 3), API token for the upload; - the default passwords of
rootand of theadminservice user (chapter 6).
The openwisp-config agent feed (our fork, chapter 4) is in
config/build.yml, together with the ninux_theme feed of the LuCI theme.
Fixes to the OpenWrt feeds live in
roles/ninux_build_openwrt/files/feed-patches/<feed>/: today there is the
luci-base form.js patch, which removes the RPCError … uci/get … Access
denied from the console of the login page. It comes from LuCI master and is
not in 25.12 yet. If a release makes it useless and the patch no longer
applies, the build stops with a clear error: then remove it.
Secret values are encrypted with ansible-vault, one by one:
ansible-vault encrypt_string --encrypt-vault-id default 'CHANGEME' --name 'shared_secret'
Full examples in the README, section Managing secrets with ansible-vault.
4. Devices and organizations¶
Every router model has its own file in
config/organizations/<org>/<device>.config. For a new model, or a new
organization, follow
Adding devices and organizations.
In the .config of a new device, except x86, also add the LuCI theme line:
CONFIG_PACKAGE_luci-theme-ninux=y
The theme is added to the LuCI themes, it does not replace them: Bootstrap stays in the firmware. On first boot it becomes the default theme; switch back to Bootstrap from System → System → Language and Style, and a firmware upgrade does not change that choice.
5. Firmware upgrader¶
The build uploads every image to OpenWISP with the firmware upgrader API:
- one category per variant (e.g. "Ninux Basilicata - tplink_c2600 - Standard VPN-ZeroTier"), so every router always receives its own variant; the category name stays within 64 characters;
- the image
typefield must match a model known to OpenWISP. Models OpenWISP does not know (e.g. TOTOLINK X5000R, TP-Link C2600, Zyxel NWA50AX Pro) are declared inOPENWISP_CUSTOM_OPENWRT_IMAGESin the openwisp2 playbook: the two lists must stay identical.
After the build, a router upgrade is started from OpenWISP (device page or mass upgrade of the build) or from the app.
x86 border router
For x86 the firmware upgrader only knows the combined-efi (GPT) image,
while our border router boots in BIOS mode with MBR. The border router is
upgraded by hand with the squashfs-combined image of the build: copy
it with scp -O, test it with sysupgrade -T, then sysupgrade. Never
start its upgrade from OpenWISP.
Checklist¶
- The first build ends SUCCESS and the images are in OpenWISP (Firmware → Builds).
- Every device has its own category and the right image.
- A router upgraded from OpenWISP comes back online with the configuration applied.