OpenWrt Custom Image Builder¶
Build custom OpenWrt firmware images with all packages and configuration baked in for instant deployment.
Overview¶
The Image Builder creates custom sysupgrade images from node snapshots, allowing you to:
- Flash a fresh router to exact mesh configuration in minutes
- Replace failed hardware without reconfiguration
- Create pre-configured images for new nodes
- Build offline (uses local package repository)
Prerequisites¶
- Docker - Image Builder runs in a container
- Node Snapshot - Run
make snapshot NODE=Nfirst - Local Package Repository - Run
make repo-setupfirst
Quick Start¶
# 1. Create snapshot of running node
make snapshot NODE=3
# 2. Build custom image from snapshot
make image-build NODE=3
# 3. Flash the image to router
# Via web UI: System > Backup/Flash > Flash new firmware
# Or via SSH: sysupgrade -v /tmp/mesh-node3-sysupgrade.bin
Commands¶
| Command | Description |
|---|---|
make image-build NODE=N | Build image for node N |
make image-build-all | Build images for all nodes with snapshots |
make image-info | Show Image Builder profiles |
make image-shell | Enter builder container shell |
make image-clean | Remove built images |
How It Works¶
┌─────────────────────────────────────────────────────────────────┐
│ Docker Build Container │
│ │
│ 1. Read snapshot from /snapshots/mesh-nodeN/ │
│ 2. Use overlay/ directory directly (full filesystem copy) │
│ 3. Extract package list (filter base packages/kmods) │
│ 4. Run OpenWrt Image Builder with: │
│ - Profile: dlink_dir-1960-a1 │
│ - Packages: from snapshot (122+ packages) │
│ - FILES: overlay/ directory (complete filesystem) │
│ 5. Output: images/mesh-nodeN-sysupgrade.bin │
│ │
└─────────────────────────────────────────────────────────────────┘
Output Structure¶
images/
├── mesh-node1-sysupgrade.bin # Custom image for node 1
├── mesh-node1-sysupgrade.bin.sha256
├── mesh-node2-sysupgrade.bin # Custom image for node 2
├── mesh-node2-sysupgrade.bin.sha256
└── mesh-node3-sysupgrade.bin # Custom image for node 3
└── mesh-node3-sysupgrade.bin.sha256
What's Included in the Image¶
Each custom image contains the complete overlay filesystem from the running node - everything that differs from factory default OpenWrt:
From Snapshot (overlay/ directory)¶
| Directory | Contents |
|---|---|
/etc/config/ | All UCI configuration (network, wireless, firewall, etc.) |
/etc/ssh/ | SSH keys, authorized_keys, sshd_config |
/etc/shadow | Root password hash |
/etc/passwd, /etc/group | User accounts |
/etc/crontabs/ | Scheduled tasks |
/etc/dropbear/ | Dropbear keys (if used) |
/root/ | Root home directory contents |
| Any other customizations | Scripts, configs, data files |
Installed Packages¶
All packages from the running node, minus base system packages and kernel modules (which are version-specific and included automatically by Image Builder).
Node-Specific Configuration¶
Each image has these values baked in:
| Setting | Node 1 | Node 2 | Node 3 |
|---|---|---|---|
| Hostname | mesh-node1 | mesh-node2 | mesh-node3 |
| LAN IP | 10.11.12.1 | 10.11.12.2 | 10.11.12.3 |
| DHCP Pool Start | 100 | 150 | 200 |
| Gateway Mode | server | client | server |
Flashing the Image¶
Via LuCI Web Interface¶
- Upload image to router:
scp images/mesh-node3-sysupgrade.bin root@10.11.12.3:/tmp/ - Open router web UI:
http://10.11.12.3 - Go to: System > Backup/Flash Firmware
- Click "Flash new firmware image"
- Select
/tmp/mesh-node3-sysupgrade.bin - Uncheck "Keep settings" (configuration is in the image)
- Click "Continue" then "Proceed"
Via SSH (Headless)¶
# Upload image
scp images/mesh-node3-sysupgrade.bin root@10.11.12.3:/tmp/
# Verify checksum
ssh root@10.11.12.3 "sha256sum /tmp/mesh-node3-sysupgrade.bin"
cat images/mesh-node3-sysupgrade.bin.sha256
# Flash (router will reboot)
ssh root@10.11.12.3 "sysupgrade -n /tmp/mesh-node3-sysupgrade.bin"
Note: -n flag discards current settings. The image contains all configuration.
Via TFTP Recovery¶
If router is bricked, use TFTP recovery:
- Download TFTP server (e.g., tftpd-hpa)
- Rename image to
factory.binor device-specific name - Set PC to 192.168.0.1
- Hold reset button while powering on router
- Router will download and flash via TFTP
Troubleshooting¶
Build Fails: "Package not found"¶
The local repository may be incomplete:
# Re-sync repository
make repo-setup
# Check specific package
ls openwrt-repo/packages/mipsel_24kc/*/batctl*
Build Fails: "Snapshot not found"¶
Create a snapshot first:
Image Too Large¶
The D-Link DIR-1960 has 16MB flash. Default images are ~7-8MB. If you've added many packages:
# Check image size
ls -lh images/mesh-node3-sysupgrade.bin
# Review package list
python3 scripts/process-packages.py --snapshot snapshots/mesh-node3 --one-per-line | wc -l
Router Doesn't Apply Configuration¶
Check if UCI defaults script ran:
# After flashing, check if script was deleted (means it ran)
ssh root@10.11.12.3 "ls /etc/uci-defaults/"
# If 99-mesh-config still exists, run it manually
ssh root@10.11.12.3 "/etc/uci-defaults/99-mesh-config"
Wrong Node Configuration¶
If you flash node3 image on node1 hardware:
# Check current hostname
ssh root@192.168.1.1 "uci get system.@system[0].hostname"
# Fix by re-deploying with Ansible
make deploy-node NODE=1
Advanced Usage¶
Dry Run (Preview Build)¶
Custom Package List¶
Edit scripts/process-packages.py to modify BASE_PACKAGES or EXCLUDE_PACKAGES sets.
Debug Container¶
make image-shell
# Inside container:
make info # Show all profiles
make image PROFILE=dlink_dir-1960-a1 PACKAGES="batctl-full" FILES=/tmp/test
Use Official Repository¶
Edit docker/imagebuilder/repositories.conf to use official OpenWrt repos instead of local.
Architecture Details¶
- Target: ramips/mt7621
- Profile: dlink_dir-1960-a1
- Architecture: mipsel_24kc
- OpenWrt Version: 24.10.4
- Image Builder: Pre-compiled packages (no kernel compilation)
Why Image Builder vs Full Buildroot?¶
| Feature | Image Builder | Full Buildroot |
|---|---|---|
| Build time | 2-5 minutes | 2-4 hours |
| Disk space | ~500MB | ~15GB |
| Kernel | Pre-compiled | Compiled |
| Package compatibility | Official repos | Custom vermagic |
| Complexity | Low | High |
Image Builder uses official pre-compiled packages, ensuring compatibility with the official opkg repositories.