In short: rtos vs bare metal is a choice about how firmware shares the CPU. Bare-metal code runs a single super-loop with interrupt handlers and the least overhead; an RTOS adds a scheduler, tasks, and timing services so concurrent, deadline-driven work stays organised. Pick bare-metal for simple, tight, resource-starved designs and an RTOS once you have several independent activities with real-time deadlines.
The phrase rtos vs bare metal describes the two dominant ways to structure embedded firmware on a microcontroller. Bare-metal firmware talks directly to the hardware with no operating system underneath it: your main() sets up peripherals, then spins forever in a loop, servicing work and reacting to interrupt service routines (ISRs). A Real-Time Operating System (RTOS) is a small kernel linked into your application that provides tasks (independent threads of execution), a scheduler that decides which task runs, and primitives such as queues, semaphores, mutexes, and software timers to coordinate them.
The key difference is not raw speed, it is concurrency management. Bare-metal makes you hand-weave every activity into one control flow. An RTOS lets you write each activity as if it owned the processor, and the kernel multiplexes them onto the single core by switching context many times a second.
Scheduling is the heart of the comparison. In a bare-metal super-loop, tasks run in the fixed order you coded them, and a slow function delays everything after it. You get cooperative timing by construction, which is simple but brittle: adding one blocking call can push an unrelated deadline past its limit.
A typical RTOS uses priority-based preemptive scheduling. Each task has a priority; the scheduler always runs the highest-priority task that is ready, preempting lower ones the instant a higher one becomes ready (for example, when an ISR posts to a queue). This gives strong determinism for the urgent work: a high-priority task's response time is bounded regardless of what the low-priority background code is doing.
Determinism is what the "real-time" in RTOS means. It does not promise your code is fast, only that worst-case timing is bounded and analysable. Hard real-time systems (motor control, airbag deployment) must never miss a deadline; soft real-time systems (a UI refresh) tolerate occasional slips. An RTOS gives you the scheduling tools to reason about these bounds; bare-metal makes you enforce them by discipline.
When an RTOS switches tasks, it saves the current task's CPU registers (its context) onto that task's stack and restores the next task's registers. Every task therefore needs its own stack, sized for its deepest call chain plus any nested ISR usage. This is the main memory cost of an RTOS:
Bare-metal also wins on interrupt latency in the simplest designs, because there is no kernel critical section between the hardware event and your handler. A well-tuned RTOS keeps these windows short, but they exist.
Neither side is monolithic. Bare-metal structures range from a naive polling super-loop, through a foreground/background pattern (ISRs set flags, the loop acts on them), to a time-triggered cyclic executive where a timer tick runs tasks in fixed slots for predictable timing without a full kernel.
RTOS schedulers also vary:
A practical rule: start bare-metal and move to an RTOS when complexity crosses a threshold. Reach for an RTOS when you have three or more independent activities with their own timing, when a TCP/IP, USB, or BLE stack expects threads, when you need blocking-style APIs with timeouts, or when a single blocking operation (a slow flash write, a sensor that stalls) would otherwise wreck unrelated deadlines.
Stay bare-metal when the device does essentially one job, when RAM/flash is tiny, when ultra-low power demands the CPU sleep between a handful of interrupts, or when certification cost favours the smallest possible code to audit.
A common failure mode in bare-metal designs is the accidental long path: a delay loop or busy-wait that starves everything else. The fix is usually to make every loop iteration non-blocking (state machines instead of delay()), which is effectively re-implementing a tiny cooperative scheduler by hand — a strong hint that an RTOS has become the cleaner answer.
Real projects and industry tool flows, with certification.
Explore CourseTron Courses →In the field, FreeRTOS (now under AWS) dominates 32-bit microcontrollers and ships inside vendor toolchains like STM32CubeIDE and the ESP-IDF. Zephyr is the Linux Foundation's feature-rich RTOS with a built-in device-tree-driven driver model. ThreadX (Eclipse ThreadX / formerly Azure RTOS) is popular where small, deterministic, safety-certified kernels are needed. For bare-metal, vendors supply HAL/LL libraries (ST's HAL, Microchip Harmony, Nordic nRFX) plus CMSIS on Arm Cortex-M so you drive peripherals and the NVIC directly. Debuggers such as SEGGER Ozone and tools like Tracealyzer or Percepio let you visualise RTOS task switches and spot priority inversions or stack overflows — problems that simply do not exist in a single-stack bare-metal build.
No. It adds scheduling overhead, so raw throughput is usually slightly lower. What it provides is bounded, predictable response time for high-priority work, plus a cleaner structure for concurrent activities.
Priority inversion is when a high-priority task is blocked waiting for a mutex held by a low-priority task that itself gets preempted by a medium task, so the high task waits indefinitely. RTOS mutexes solve it with priority inheritance, temporarily boosting the low task's priority. Bare-metal has no shared mutexes, so the issue does not arise in the same form.
For single-purpose devices, severe RAM/flash limits (small 8/16-bit MCUs), deep low-power designs that sleep between a few interrupts, or certification scenarios where minimising auditable code matters more than structural convenience.
Estimate the deepest call chain plus worst-case local variables and any nested ISR usage, add margin, then measure. Most RTOSes offer stack high-water-mark APIs; fill the stack with a known pattern and check how much was touched under stress testing.
Related: FreeRTOS tasks and scheduling · the VLSI Glossary · all courses.