Network-as-Code for managing and deploying OpenWrt firmware for my homelab network infrastructure.
As network configuration grows, managing everything directly on the router becomes impractical. All infrastructure depends on stable networking, so a safer and more repeatable workflow is required.
This repository provides a single source of truth for network configuration, enabling controlled, reproducible firmware builds and deployments.
- Reproducible builds – Pinned OpenWrt version with explicit target, subtarget, and profile
- Verified ImageBuilder source – Fetches OpenWrt ImageBuilder with fixed Nix hash
- Secrets handling – Decrypts SOPS files only during local build runtime
- Template rendering – Generates UCI configs using Gomplate at build time
- Safe deployment – Keeps sysupgrade as an explicit manual step
- Pre-commit quality gates – Unified local formatting and linting before commits
- Automated updates – Weekly check for new OpenWrt releases with PR + issue
apply (check-update -> check -> build -> sysupgrade)
- check-update – Warn if a newer OpenWrt release exists
- flake check – Validate formatting and fixture-based UCI rendering
- build – Render secrets locally and build firmware image
- sysupgrade – Flash router and reboot
Warning
Sysupgrade will reboot your router and reset existing config (sysupgrade -n).
Note
nix run .#build requires readable SOPS secret files in secrets/.
.sops.yaml configures encryption for secrets/*.sops.yaml.
Default secret and output paths resolve from the Git worktree root, not the
current shell directory.
-
Prepare configuration. Files under
files/are copied into/etc/in the final image. -
Enter the development shell, or ensure all dependencies are installed:
nix develop- Optional runtime overrides
env:
ROUTER_HOST: 10.10.0.1
ROUTER_USER: root
ROUTER_PORT: '22'
NETWORK_SECRET: /path/to/network.sops.yaml
WIRELESS_SECRET: /path/to/wireless.sops.yaml
ADGUARDHOME_SECRET: /path/to/adguardhome.sops.yaml
NETLAB_ROOT: /path/to/netlab
BUILD_OUTPUT_DIR: /path/to/build-output- Build firmware
nix flake check
nix run .#buildEncrypt or update a secret file with:
sops --encrypt --in-place secrets/adguardhome.sops.yaml- Deploy firmware
nix run .#sysupgradeOr run the full pipeline with one command:
nix run .#apply- Cleanup is automatic on commit via pre-commit hook (generated artifacts).
flake.nix
├─ apps → apps/build.nix
└─ checks → apps/test.nix
apps/build.nix
├─ build → render secrets + build firmware
└─ fetchurl → pinned OpenWrt ImageBuilder source
apps/deploy.nix
└─ sysupgrade → deploy image to router
apps/check-update.nix
└─ check-update → detect new OpenWrt release
apps/apply.nix
└─ apply → check-update -> check -> build -> sysupgrade
apps/test.nix
├─ formatting → treefmt-nix
└─ uci → fixture render + UCI syntax validation
secrets/*.sops.yaml
└─ decrypted at runtime only
templates/*.tmpl
└─ rendered into files/etc/config/{network,wireless} and files/etc/adguardhome/adguardhome.yaml
files/*
└─ included in firmware (/etc/* on device)
tests/fixtures/*
└─ non-secret datasource fixtures for syntax checks
.pre-commit-config.yaml
└─ cleanup, formatting, and lint hooks
.github/workflows/openwrt-update.yml
└─ scheduled update detection and PR/issue creation
validate-uci.yml– Runsnix flake check(formatting + UCI validation)openwrt-update.yml– Weekly OpenWrt release check with automated PR + issue
Install and enable hooks once per clone:
nix develop --command pre-commit installRun all hooks manually:
nix develop --command pre-commit run --all-files