Hardware, IoT & Embedded · RTOS

FreeRTOS vs Zephyr: Choosing an RTOS for Your Device

Tradeoffs in tooling, drivers, and ecosystem.

Reading time: ~8–12 min
Level: All levels
Updated:

Picking an RTOS is less about “which kernel is faster” and more about what you can ship and maintain. FreeRTOS vs Zephyr is a classic choice: FreeRTOS is lightweight and widely embedded into vendor SDKs, while Zephyr is a full OS with a modern build system, a unified driver model, and a strong upstream ecosystem. This guide gives you a practical decision process—tooling, drivers, networking, memory footprint, and the traps that make RTOS projects painful.


Quickstart

If you need to decide this week, don’t start by reading feature lists. Start by running two prototypes on your target hardware: (1) one peripheral you care about (UART/I2C/SPI/ADC) and (2) one connectivity path (BLE/Wi-Fi/Ethernet). That small test surfaces 80% of the real differences: drivers, build friction, debugging, and how quickly you can iterate.

Fast decision rules (practical, not philosophical)

  • Choose FreeRTOS if your vendor SDK already uses it, you want a small kernel + your own architecture, or your device is extremely constrained.
  • Choose Zephyr if you want a unified OS stack (drivers + networking + configuration) across multiple boards and you value upstream reuse over custom glue.
  • If you need rapid hardware bring-up on many boards, Zephyr’s board + devicetree model usually scales better.
  • If you need tight control over what’s included (and you’re okay assembling pieces), FreeRTOS stays simple and predictable.

What to measure in your prototype (30–90 minutes)

  • Time to first build + flash (fresh machine, minimal docs)
  • Driver support for your exact peripheral + board variant
  • Debug loop: logs, RTT/UART, stack overflow checks, asserts
  • Power features: sleep states, tickless idle, wake sources
  • Networking: TLS fit, memory usage, stability under load

A quick comparison map (what’s “included”)

Area FreeRTOS (typical reality) Zephyr (typical reality)
Core Kernel primitives (tasks, queues, timers) you integrate into your project RTOS kernel plus OS subsystems (threads, kobjects, services)
Build / config Often via vendor IDE + FreeRTOSConfig.h (or CMake in custom setups) West + CMake + Kconfig + devicetree (consistent across boards)
Drivers Commonly vendor HAL / SDK drivers (varies by chip vendor) Unified driver model with devicetree binding & upstream drivers
Networking Depends on add-ons / vendor stack (common in IoT SDKs) Integrated networking stack options + consistent APIs
Best at Small, focused firmware with maximum flexibility Products that benefit from upstream reuse and multi-board scaling
The “maintenance” lens

Ask: “Who will update this in 18 months?” RTOS choice becomes obvious when you think about patches, new hardware revisions, and what happens when the one engineer who understands the build system goes on vacation.

Overview

This post is a practical guide to choosing between FreeRTOS and Zephyr for embedded and IoT devices. Both can power reliable products. The difference is how much the OS provides out of the box versus what you assemble yourself. If you’re building a sensor node, a smart appliance, an industrial controller, or anything that needs networking and long-term support, the choice affects your team’s speed and your risk profile more than it affects raw scheduler performance.

What you’ll learn

  • How FreeRTOS and Zephyr differ in architecture and ecosystem
  • Which one fits better for vendor SDK workflows vs upstream portability
  • How to evaluate drivers, networking, power, and debug in a prototype
  • Common mistakes that cause “mystery bugs” (and how to prevent them)

Who this is for

  • Solo builders shipping firmware on ESP32 / STM32 / nRF / RP2040-class boards
  • Teams moving from Arduino-style loops to a real RTOS architecture
  • Developers inheriting a codebase and trying to make it maintainable
  • Anyone who needs a clear decision process (not internet debates)
A useful framing

FreeRTOS is often a kernel you embed. Zephyr is often a platform you build on. Many real products use both patterns successfully—what matters is matching the pattern to your constraints.

Core concepts

Before comparing features, align on the concepts that actually drive RTOS success: scheduling behavior, interrupt design, configuration model, and how hardware support is expressed and maintained.

RTOS basics: threads, priorities, and determinism

Determinism is a system property

A “real-time” system is real-time because the worst-case path is bounded: interrupt latency, scheduling latency, and the maximum time you hold critical resources. Your OS can help, but your design can still break it.

  • Keep ISRs short; defer work to threads
  • Bound queue sizes; avoid unbounded work in high-priority threads
  • Watch priority inversion; use proper mutexes/inheritance patterns

Tick, tickless, and power

Many devices spend most time asleep. Tickless idle and clean wake sources matter more than “fast context switch” marketing. Your best win is usually correct sleep configuration + fewer wakeups.

  • Measure wake frequency and timer sources
  • Validate deep-sleep entry/exit paths
  • Confirm clocks/peripherals reinitialize correctly after sleep

Two different philosophies: “bring your own stack” vs “unified OS”

In practice, FreeRTOS is frequently used in a bring-your-own architecture: you choose the HAL/SDK, the networking stack, the TLS library, the file system (or none), and you glue them together. Zephyr leans toward a unified architecture: there’s a consistent configuration system, a common driver model, and upstream conventions for boards and peripherals.

Concept Why it matters How it shows up
Configuration Reproducible builds across machines and boards FreeRTOSConfig.h vs Kconfig + prj.conf
Hardware description Portability and clarity when hardware variants multiply Board files / vendor SDK vs devicetree + overlays
Drivers & APIs How quickly you can integrate sensors/IO and replace parts later Vendor HAL APIs vs Zephyr driver APIs
Upgrades Security fixes and long-term maintenance Pinning versions vs upstream updates + merge strategy

Tooling: what “developer experience” really means on embedded

FreeRTOS: common workflow patterns

  • Often lives inside a chip vendor’s SDK (startup, HAL, drivers included)
  • Configuration commonly centralized in FreeRTOSConfig.h
  • Build may be IDE-first (but can be made CI-friendly)
  • Debug success depends on consistent assert/stack overflow settings

Zephyr: common workflow patterns

  • West for workspace management and builds
  • Kconfig for feature selection (what gets compiled)
  • Devicetree for hardware description (what exists on your board)
  • Consistent board targets and “sample app” structure across vendors
The hidden cost: mixing ecosystems

Many painful projects combine a vendor SDK, custom drivers, a networking stack, and an RTOS without a clear “source of truth” for configuration. Pick a primary ecosystem (vendor-first or upstream-first) and define strict boundaries for everything else.

Step-by-step

Here’s a decision process you can use for real projects. It’s written like a build checklist: define constraints, run two prototypes, and make the choice based on what will be easiest to ship, test, and update.

Step 1 — Write your constraints in plain language

If your constraints aren’t written down, you’ll unconsciously optimize for “what’s familiar” instead of what the device needs. Use this format: “We must fit in X RAM / Y flash, wake within Z ms, and support A/B/C interfaces.”

  • Resources: RAM/flash budget, CPU speed, available debug interfaces
  • Power: sleep depth, wake sources, duty cycle, timing requirements
  • Connectivity: BLE/Wi-Fi/Ethernet, TLS needs, OTA requirements
  • Lifecycle: security patch cadence, certification needs, how long the product lives

Step 2 — Decide what you want “the OS” to own

This is the real FreeRTOS vs Zephyr question. Do you want a kernel and you own the stack, or do you want an OS platform where many choices are standardized?

When “kernel + your stack” is the right fit

  • You already have a stable vendor SDK path you trust
  • You need to keep the firmware minimal and predictable
  • Your product is single-board, single-vendor, and unlikely to expand
  • You want full control over which libraries enter the binary

When a unified OS platform helps

  • You expect multiple boards, revisions, or vendors over time
  • You want consistent configuration and repeatable builds in CI
  • You value upstream drivers and community maintenance
  • You want OS-level patterns for devices, power, and networking

Step 3 — Prototype Zephyr: build, flash, and log on your board

A Zephyr prototype should answer: “Can I build and flash reliably, and do I understand how to express my hardware and configuration?” If this feels smooth, Zephyr tends to scale well as your device grows.

# Zephyr: minimal workspace + build + flash (typical flow)
# Assumes you have the Zephyr SDK/toolchain set up for your host OS.

west init -m https://github.com/zephyrproject-rtos/zephyr.git zephyr-workspace
cd zephyr-workspace
west update
west zephyr-export

# Build a sample for a specific board (replace with your board target)
west build -b nrf52dk_nrf52832 zephyr/samples/basic/blinky

# Flash to device (runner depends on board; often "nrfjprog", "openocd", "pyocd", etc.)
west flash

# Optional: open a serial monitor to confirm logs/blinky behavior
# (Your port name varies: /dev/ttyACM0, COM3, etc.)
# screen /dev/ttyACM0 115200
What success looks like (Zephyr)

You can reproduce the build on a second machine, select features via config, and adjust a peripheral through a devicetree overlay without rewriting half the project. That’s the “platform” benefit.

Step 4 — Prototype FreeRTOS: validate your scheduling and communication patterns

A FreeRTOS prototype should answer: “Can I implement the concurrency patterns I need (queues, event groups, timers), and do I have robust debug visibility?” Most FreeRTOS issues are not “kernel bugs”—they’re stack sizing, priorities, or ISR misuse.

/* FreeRTOS: minimal pattern with two tasks + a queue.
 * Use this to validate your scheduling, priorities, and ISR-to-task design.
 * Notes:
 *  - Keep ISRs short: send to a queue (or task notification), do work in a task.
 *  - Turn on configASSERT and stack overflow checks in FreeRTOSConfig.h.
 */

#include <stdint.h>
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

static QueueHandle_t q;

static void SensorTask(void *arg) {
  (void)arg;
  uint16_t sample = 0;

  for (;;) {
    /* Replace with real sensor read (I2C/SPI/ADC). */
    sample = (uint16_t)((sample + 17u) % 1024u);

    /* Send sample to consumer; block briefly if queue is full. */
    (void)xQueueSend(q, &sample, pdMS_TO_TICKS(10));

    vTaskDelay(pdMS_TO_TICKS(20));
  }
}

static void AppTask(void *arg) {
  (void)arg;
  uint16_t sample = 0;

  for (;;) {
    if (xQueueReceive(q, &sample, portMAX_DELAY) == pdPASS) {
      /* Replace with application logic (filtering, control, reporting). */
      printf("sample=%u\r\n", (unsigned)sample);
    }
  }
}

int main(void) {
  q = xQueueCreate(16, sizeof(uint16_t));
  configASSERT(q != NULL);

  xTaskCreate(SensorTask, "sensor", 384, NULL, tskIDLE_PRIORITY + 2, NULL);
  xTaskCreate(AppTask,    "app",    512, NULL, tskIDLE_PRIORITY + 1, NULL);

  vTaskStartScheduler();

  /* If you get here, heap/stack/config is wrong. */
  for (;;) {}
}

What to check in your FreeRTOS prototype

  • Enable configASSERT and stack overflow detection (catch bugs early)
  • Confirm task stack sizes via high-water marks (don’t guess)
  • Validate your interrupt priority configuration (MCU-specific gotcha)
  • Use queues/notifications to move work out of ISRs

Step 5 — Evaluate drivers and hardware description (the “board scale” test)

The fastest way to understand ecosystem differences is to change one thing about hardware and see what breaks: move a UART pin, add an I2C sensor, change an SPI frequency, or disable a peripheral for power. If you expect multiple board revisions, this matters a lot.

Zephyr: devicetree + overlays

Zephyr often expresses hardware as devicetree nodes, then binds drivers to those nodes. You can override board defaults with an overlay file.

FreeRTOS: typically vendor HAL configuration

With FreeRTOS, hardware configuration typically lives in vendor SDK code, pinmux tools, and driver init routines. That can be great when the vendor tooling is strong—but portability depends on your own abstraction.

/* Zephyr devicetree overlay example (app.overlay)
 * Goal: enable I2C0 at 400 kHz and define a sensor on address 0x76.
 * (Exact node names vary by board/SoC; this shows the pattern.)
 */

/ {
  aliases {
    i2c-sensor = &bmp280;
  };
};

&i2c0 {
  status = "okay";
  clock-frequency = <I2C_BITRATE_FAST>;

  bmp280: bmp280@76 {
    compatible = "bosch,bmp280";
    reg = <0x76>;
    label = "BMP280";
  };
};
Why this matters

Hardware revisions are inevitable: pin changes, new sensors, alternate suppliers. A clean hardware description model reduces rewrite risk. If your product roadmap includes variants, treat this as a first-class requirement.

Step 6 — Make the choice using a scored checklist

After both prototypes, pick the RTOS that wins on the constraints you wrote in Step 1. This table is designed to be scored quickly. Mark each row with “FreeRTOS wins / Zephyr wins / tie” and write a one-line reason. That one-line reason becomes your decision record.

Decision axis FreeRTOS tends to win when… Zephyr tends to win when…
Footprint control You need the smallest possible kernel and only the features you pick You still need control, but accept a platform model to gain upstream reuse
Vendor SDK integration Your vendor’s official path is FreeRTOS-based and well-supported Your board is supported upstream and you want consistent workflows across vendors
Multi-board / variants You’ll maintain your own HAL abstraction layer You expect many variants and want OS-level hardware description conventions
Tooling & CI You already have stable tooling/IDE and reproducible builds You want a standardized build/config stack that works similarly everywhere
Networking stack needs You rely on a vendor-provided or curated networking path You want integrated options and consistent APIs/configuration
Team familiarity Your team is already effective with FreeRTOS patterns Your team benefits from an upstream-first OS workflow and shared conventions

Step 7 — Lock your architecture boundaries

Once you choose, set boundaries early. This prevents the “Franken-firmware” outcome where every layer has two competing sources of truth. Keep the rules short and enforce them in code review.

If you chose FreeRTOS

  • Define a minimal platform layer: clocks, GPIO, timers, comms, logging
  • Keep RTOS primitives behind clear modules (no queues everywhere)
  • Standardize ISR-to-task pattern (notifications/queues) across the codebase
  • Make memory/stack usage visible in CI (fail on regressions)

If you chose Zephyr

  • Use Kconfig and devicetree as your configuration source of truth
  • Prefer upstream drivers/APIs; avoid vendor forks unless necessary
  • Organize app code as modules; keep board-specific logic in overlays
  • Pin versions and update deliberately (treat updates like feature work)

Common mistakes

Most “RTOS bugs” are architecture bugs: priorities, stacks, interrupts, and configuration drift. Here are the common failure modes—and the fixes that save days.

Mistake 1 — Treating ISR code like normal code

Long ISRs, blocking calls, or heavy logging inside interrupts cause latency spikes and unstable behavior.

  • Fix: keep ISRs minimal; signal a thread via queue/notification and return fast.
  • Fix: measure worst-case interrupt latency early (before features pile up).

Mistake 2 — Guessing task stack sizes

Stack overflows look like random corruption, not a clean crash—especially on microcontrollers without memory protection.

  • Fix: enable stack overflow checks and asserts.
  • Fix: track stack “high water mark” and set sizes from measurements.

Mistake 3 — Priority inversion and “always highest priority” thinking

Assigning the highest priority to everything makes the system brittle and can starve critical background work.

  • Fix: define priority bands (ISR-deferred, control loop, comms, housekeeping).
  • Fix: use proper mutex patterns; keep critical sections small.

Mistake 4 — Configuration drift across environments

Builds that “work on one laptop” often hide mismatched flags, missing features, or implicit vendor IDE settings.

  • Fix: make builds reproducible in CI; pin toolchains and dependencies.
  • Fix: keep configuration centralized (FreeRTOSConfig.h or Zephyr Kconfig/prj.conf) and reviewed.

Mistake 5 — Over-customizing too early

Forking OS code or replacing core subsystems early increases upgrade pain and makes security fixes harder.

  • Fix: start with upstream defaults; only customize after you have a measured need.
  • Fix: document every non-standard change with a “why” and a rollback plan.

Mistake 6 — Not testing power until the end

Power bugs appear when you combine peripherals, sleep states, and interrupts—not in hello-world.

  • Fix: create a power test mode early (sleep/wake loop + logging).
  • Fix: verify clocks and peripherals recover correctly after deep sleep.
The “it works until it doesn’t” pattern

RTOS systems often look stable during demos and then fail in long runs. Add a soak test early: run for hours, vary inputs, stress the network, and watch memory and queue usage.

FAQ

Is Zephyr “heavier” than FreeRTOS?

Often, yes—because Zephyr is typically used as a fuller OS platform. But “heavier” is not automatically bad. What matters is whether the included subsystems reduce your custom glue and maintenance. If your product needs drivers, networking, logging, and configuration consistency, Zephyr’s footprint may be worth it. If you need a minimal binary and you’ll build the stack yourself, FreeRTOS usually stays smaller and simpler.

Can I use vendor SDK drivers with either RTOS?

Typically yes. Many real products pair FreeRTOS with vendor SDK drivers, and Zephyr can also integrate vendor components in some cases. The important part is choosing one “primary” model for configuration and initialization. Mixing two driver worlds without boundaries is what creates hard-to-debug conflicts (clock init, interrupt priorities, power states).

Which one is better for networking and IoT features?

It depends on your vendor and constraints. Zephyr tends to offer a more integrated OS approach (consistent configuration and APIs), while FreeRTOS projects often rely on vendor or curated stacks. For IoT devices, test the exact combination you need: TLS handshake memory usage, reconnection behavior, and long-run stability under poor connectivity.

How do I avoid RTOS “random crashes” in production?

Treat observability as a feature: enable asserts, add stack usage tracking, keep ISRs short, and build a soak test. Most “random” crashes are stack overflow, race conditions, or priority problems. Catch them early with strict debug settings and by measuring stack/heap/queue usage over time.

Do I need tickless idle?

If you run on batteries or care about power, tickless idle (or equivalent low-power scheduling) is usually a must. But it only helps if your timers and wake sources are configured correctly and your peripherals truly allow deep sleep. Measure current draw and wake frequency—don’t assume.

What’s a safe way to decide if both seem fine?

Choose the one that best matches your long-term maintenance model: vendor-first (often FreeRTOS in SDKs) versus upstream-first (often Zephyr). If your team expects multiple boards and wants a consistent build/config story, Zephyr is frequently easier to scale. If your product is tightly tied to one vendor SDK and needs a minimal kernel, FreeRTOS is frequently the pragmatic choice.

Cheatsheet

Use this as a quick “RTOS selection and setup” checklist. If you can confidently check most of these boxes, you’re in a good place.

Decision checklist (pick based on reality)

  • I wrote resource + power + connectivity constraints (numbers, not vibes)
  • I built and flashed a minimal app on the real board (twice, on a fresh machine)
  • I validated one core peripheral and one connectivity path
  • I tested logging + asserts + basic debugging workflow
  • I identified where drivers will come from (vendor SDK vs upstream)
  • I can explain how we’ll update dependencies for 18–24 months

Architecture checklist (avoid future pain)

  • ISRs are minimal; heavy work is in threads
  • Priorities are planned (bands), not “everything high”
  • Task stacks are measured and monitored
  • Queues/notifications have bounded sizes and backpressure behavior
  • Power mode is tested early with a dedicated soak test
  • Config is centralized and reproducible in CI

Quick “choose this if…” summary

If you say… Lean toward… Because…
“We’re deep in a vendor SDK and need minimal change.” FreeRTOS Lower integration risk and simpler core when the SDK path is stable
“We’ll have multiple boards/variants and want consistency.” Zephyr Unified build/config/hardware description reduces long-term friction
“We need tight footprint control and a very focused firmware.” FreeRTOS Kernel-first approach keeps you in control of what ships
“We want upstream reuse for drivers and OS services.” Zephyr Platform model can reduce custom glue and make upgrades more systematic

Wrap-up

FreeRTOS vs Zephyr is ultimately about choosing a development model. If you want a small, well-known kernel that fits neatly into vendor SDKs and you’re comfortable assembling your own stack, FreeRTOS is a strong and pragmatic choice. If you want a consistent, upstream-first platform with unified configuration, board support, and a driver model that scales across variants, Zephyr often pays off as the project grows.

The best next step is simple: take your target board and run the two prototypes from Quickstart—one peripheral, one connectivity path. Then make the choice based on what you can build, debug, and update confidently.

Next actions (10 minutes)
  • Write constraints as numbers (RAM/flash/power/wake)
  • Pick a “must work” peripheral and connectivity feature
  • Run a clean build/flash on your actual board
  • Record the decision with one sentence: “We chose X because Y.”

Quiz

Quick self-check (demo). This quiz is auto-generated for hardware / iot / embedded.

1) What’s the most practical way to decide between FreeRTOS and Zephyr for a real device?
2) Which pairing best matches how configuration is commonly expressed?
3) What is a common root cause of “random” RTOS crashes on microcontrollers?
4) Which statement best describes the typical ecosystem difference?