Most IoT devices arrive as locked boxes. The firmware might lean on Linux, BusyBox, and a hundred other GPL-licensed bits, but the manufacturer hands you no source, no toolchain, and no way to change what you bought. This isn’t just sloppy engineering—it’s a licence violation. Putting together a GPL-compliant IoT device, whether from scratch or by reworking an existing design, is a quiet act of technical self-respect. Here I’ll walk through the architecture calls, the bill of materials, and the compliance plumbing you need to do it right.

Why GPL Compliance in Embedded Systems Is Harder Than It Looks
Desktop and server software have tidy ways to ship source code. Embedded devices bring constraints that fight the GPL’s demands every step of the way: tiny flash storage, locked bootloaders, proprietary radio firmware, and toolchains that only play nice on Ubuntu 9.04. Compliance isn’t dumping a tarball on some forgotten FTP server. It’s giving people the exact corresponding source for the binaries on the device, plus the scripts and instructions to rebuild and reinstall a modified version.
Companies mess this up all the time. The Software Freedom Conservancy’s copyleft work has turned up cases where vendors shipped GPL’d U-Boot and Linux kernels but kept back the board support package (BSP) you need to produce a bootable image. A builder with principles treats the BSP, device trees, and any weird compilation flags as part of the “Corresponding Source.”
Choosing Your Silicon: Start With the Bootloader
Your GPL obligations kick in the moment the CPU runs its first instruction. A lot of IoT-grade SoCs—especially from Mediatek, Allwinner, and Broadcom—lean on proprietary first-stage bootloaders burned into mask ROM. You can’t swap those out. But you can pick a chip where the secondary program loader (SPL) and U-Boot are fully buildable from source.
If you’re starting from a blank sheet, look at the NXP i.MX 6ULL or the Texas Instruments AM335x series. Both have solid mainline U-Boot and Linux kernel support. Steer clear of silicon that demands a signed proprietary blob just to reach U-Boot—that’s a legal and practical mess from day one. If you have to use a blob, say for Wi-Fi calibration data, park it on its own storage partition and spell out where it came from in your compliance offering. The point isn’t zero blobs; it’s zero hidden blobs.

Selecting an Operating System: Buildroot vs. Yocto
Your OS layer is where most GPL components live—the kernel, C library, coreutils, and networking stack. Two build systems rule the embedded Linux world: Buildroot and Yocto/OpenEmbedded. Both can turn out GPL-compliant outputs, but they ask for different kinds of discipline.
Buildroot: Simpler, With a Compliance Trap
Buildroot works off a single configuration file and builds the whole thing from source. Spitting out a source archive is easy: make legal-info gives you a legal-info/ directory with licence texts and source tarballs for each package. The trap? Buildroot’s default legal-info output doesn’t always grab the exact toolchain version you used. If you ship a cross-compiled binary, the GPL (through the GCC Runtime Library Exception, or the full GPL for pieces like libgcc) might force you to hand over the matching toolchain source. Add a step to archive your Buildroot source tree and the external toolchain source, if you leaned on one.
Yocto: Complete, but Needs Archiving Scripts
Yocto’s layer structure means your compliance archive has to include every layer you touched, plus the exact revisions from your repo manifest or kas configuration. The archiver.bbclass can handle source archiving for you, but you’ve got to set it up to capture both patched and original sources. A typical slip-up is archiving only the upstream source and leaving out the .bbappend files that apply local patches. Those patches count as part of the Corresponding Source if they change GPL-licensed code.
The Radio Problem: Wi-Fi, Bluetooth, and the Firmware Boundary
Wireless connectivity is where compliance gets really sticky. Most Wi-Fi/Bluetooth modules come with proprietary firmware that runs on a separate microcontroller. The Linux kernel talks to this firmware through a clean interface. The legal read—backed by the kernel’s firmware_class docs and the Free Software Foundation’s GPL FAQ—is that firmware loaded onto a different processor isn’t a derivative work of the kernel, as long as the interface is a hardware boundary.
So you can drop, say, the Texas Instruments WL18xx firmware blob onto your device’s rootfs without the GPL kicking in for that blob. But you still have to follow the licence of the kernel driver that loads it—and that driver is GPLv2. In practice, you’ve got to provide the driver source and any kernel changes. For the firmware blob itself, ship it with its own licence text (often just a simple redistribution grant). Spell out the processor boundary clearly in your compliance doc.
Even better, grab a module with an open-source firmware stack if you can. Espressif’s ESP32 series, for instance, can run the open-source ESP-AT firmware or be programmed straight with the ESP-IDF framework—most of it is Apache 2.0 licensed. That sidesteps the blob question for the main wireless job.
Building the Compliance Delivery System
A GPL-compliant IoT device needs three things from you: a written offer, the actual source, and build instructions. The written offer—a line in the device’s manual or settings menu—tells people how to get the source. The cleanest way these days is a direct HTTPS download link on your site, kept alive for at least three years after the last unit of that model ships.
The source archive should be one compressed tarball with:
- The exact Linux kernel source tree, including your
.configand any out-of-tree patches. - The U-Boot source tree with your board configuration.
- The Buildroot or Yocto source trees, complete with all custom packages, layers, and configuration files.
- A
READMEthat lists the build host distribution and version (like “Ubuntu 22.04 LTS”), the packages you need, and the step-by-step commands to produce a bootable SD card or flash image. - Any scripts required to sign or package the image for the device’s bootloader.
Test your archive on a regular schedule. Spin up a fresh virtual machine and run your documented build steps. If the resulting binary doesn’t match the factory image checksum, your archive is incomplete. The GPL doesn’t insist on bit-for-bit reproducibility, but it does demand that the source produce a functionally equivalent binary. A build that falls over is a compliance bug.

Enforcing Copyleft in the Physical World
An IoT device often ships with a locked bootloader. Even if you hand over perfect source code, a user can’t install a modified kernel if the SoC’s secure boot chain refuses to run unsigned images. GPLv3 tackles this with its “Installation Information” requirement, but the Linux kernel stays explicitly GPLv2-only, which says nothing about that.
Don’t treat this as a loophole. A principled device gives the owner the keys. On i.MX processors, you can burn the user’s own public key into the one-time-programmable (OTP) fuses during manufacturing. When that’s not practical, offer a documented way to turn off secure boot completely, even if it means opening the case. Put it in the compliance README. The whole idea is to make the four freedoms real, not just a theory.
Licence Auditing With SPDX and FOSSology
Before you ship anything, run a licence audit. The Software Package Data Exchange (SPDX) standard lets you produce a machine-readable bill of materials for your entire firmware image. Tools like FOSSology scan your source tree and flag every licence, copyright holder, and possible conflict. This catches blunders like a GPLv3 library accidentally linked into a GPLv2-only userspace program.
Generate an SPDX report and tuck it into the device’s documentation. The GPL doesn’t strictly ask for this, but it shows you’re acting in good faith and helps downstream users figure out their own obligations if they redistribute the device or its firmware. For a small builder, all this paperwork can feel heavy, but it’s the same discipline that stops supply-chain chaos before it starts.
Case Study: A LoRaWAN Environmental Sensor
Let’s ground this with a real project. You’re building a solar-powered LoRaWAN sensor that reports soil moisture and temperature. The hardware is an STM32WL55 microcontroller—a single-chip design with an integrated LoRa radio. The firmware sits on FreeRTOS (MIT-licensed) and the STM32WLxx HAL (BSD-3-Clause). No Linux, no GPL code at all. Is compliance a breeze? Easier, sure, but it doesn’t handle itself.
The GNU ARM Embedded Toolchain includes GPL-licensed pieces like libgcc. The runtime library exception covers most uses, but you still need to provide the toolchain source if you distribute binaries that touch certain parts of the library. More importantly, if you use an STM32CubeMX-generated project, check the licence on every middleware stack. Some ST audio or graphics libraries are proprietary and need a separate redistribution agreement. Your compliance document has to separate the open-source components from the proprietary ones, giving the source for the first and the licence terms for the second.
The compliance offering for this sensor is a ZIP file with the FreeRTOS kernel source, your application code, the STM32Cube firmware package (released under a BSD licence that allows redistribution), a text file with the GNU Toolchain download URL, and build instructions that name the GNU ARM Embedded Toolchain version you used. Since there’s no GPL code, you aren’t forced to offer the complete toolchain source yourself—a written offer linking to the official ARM developer site covers the runtime library exception. But if you used any GPL’d utility on the build host that ends up baked into the binary, you’ve got to include its source directly.
Frequently Asked Questions
Do I need to provide source code for the bootloader if it’s in read-only memory?
Yes, if the bootloader holds GPL-licensed code and you distribute the device. The GPL applies to the distribution of the object code, no matter the storage medium. Even if users can’t practically change the mask ROM, you still have to provide the corresponding source for the GPL portions. The physical wall doesn’t let you off the hook.
Can I use a GPL-licensed library in a proprietary IoT application?
Almost never. Linking a GPL library to your proprietary code creates a combined work that has to be licensed under the GPL as a whole. The LGPL (Lesser General Public License) is built to allow linking from proprietary apps, as long as you follow its rules (dynamic linking, letting users swap the library). For IoT firmware that’s a single static binary, even LGPL libraries often pull the trigger on full source obligations unless you design the firmware to permit shared-library replacement.
What happens if I accidentally ship a device without the source offer?
You’re in breach the moment it goes out the door. The copyright holders of the GPL code (often thousands of individual kernel developers) can demand you fix it. The usual fix is to get into compliance fast by publishing the missing source and extending the source offer. Repeated or deliberate violations can get your licence yanked, meaning you lose the right to distribute the GPL code at all. For a commercial product, that can mean a recall or a panicked firmware rewrite.
Is a written offer on a paper insert enough, or do I need a web link?
A paper insert with a physical address and a written offer good for three years works legally under GPLv2 Section 3(b). But for an IoT device that might get resold or tossed from its original box, a web link in the device’s admin interface or settings menu is a lot more practical and kind to the user. Plenty of companies do both: a paper insert inside the box and a permanent link baked into the firmware.