
Embedded teams have long treated CI/CD as a web-team luxury. Firmware builds require specific toolchains, hardware-in-the-loop testing is expensive, and OTA updates carry risk that cloud deployments never face. But the principles of GitOps (declarative configuration, version-controlled state, and automated reconciliation) translate remarkably well to embedded development.
Here is how we apply GitOps patterns to firmware pipelines for IoT device fleets.
The Core GitOps Loop for Embedded
The standard GitOps loop (git push triggers build, deploy, and reconcile) maps to embedded as follows:
- Declarative firmware configuration: Device configuration (Wi-Fi credentials, server URLs, feature flags) is stored as YAML in the same repository as the firmware code. A merge request changes the config, not a manual SSH session.
- Automated build matrix: Each commit triggers a matrix build across target MCU families. Compiler version, RTOS configuration, and board variant are parameters in the build spec, not environment variables on a developer machine.
- Artifact signing and versioning: Every build produces a signed firmware image with a unique semantic version derived from the git tag. The build pipeline generates a software bill of materials (SBOM) as a build artifact.
- Staged OTA rollout: The deployment controller watches the firmware registry for new versions and manages rollout across device cohorts: 1% canary, 10%, 25%, 100%. If a device reports failure, the controller automatically stops the rollout and reverts the cohort.
- Observability-driven reconciliation: Devices report their running firmware version as part of the telemetry stream. If a device falls behind its declared target version, the reconciliation loop schedules an OTA update. If a device crashes after update, it rolls back to the previous known-good image.
Tooling Choices
- Build system: CMake with a toolchain file per target. West (Zephyr) or idf.py (ESP-IDF) wrappers for vendor SDKs.
- Artifact storage: S3-compatible bucket with versioning enabled. Firmware images are immutable objects keyed by git commit hash + target triple.
- OTA server: Simple REST API or managed service (Eclipse hawkBit, AWS IoT Device Management). The controller compares desired vs reported state and triggers updates.
- CI/CD: GitHub Actions or GitLab CI. Self-hosted runners with the required toolchains pre-installed.
Common Pitfalls
- Treating OTA as a file copy: OTA for firmware is transactional: you need version checks, integrity verification, fallback images, and power-loss recovery. A GitOps controller must understand device state, not just push files.
- Ignoring fleet heterogeneity: Production IoT fleets rarely have identical hardware. Your build matrix must account for MCU revisions, radio module variants, and sensor configurations. Model these as build dimensions, not parallel repos.
- Reconciliation without back-off: If a device is offline, the controller should not retry indefinitely. Exponential back-off with a maximum interval prevents network storms when devices reconnect after a power outage.
At Altirratech EU, we build GitOps pipelines for IoT device fleets in production. If your team is transitioning from manual firmware updates to automated OTA, we can help design the pipeline.