ROS2 on Mac or Windows Means Docker. That Sentence Costs More Than Most Teams Realize.
ROS2 is Linux-native. That is not a complaint about the design choice — Linux is the right foundation for production robotics. The complaint is about what the development toolchain assumes: that every

Blai Morte
Co-Founder
ROS2 is Linux-native. That is not a complaint about the design choice — Linux is the right foundation for production robotics. The complaint is about what the development toolchain assumes: that every engineer has a Linux machine, that Docker is a transparent abstraction, and that hardware passthrough just works. None of those assumptions hold in practice.
A developer on Reddit put it directly: "I've spent the last nine months trapped in Docker containers." That is not a complaint about Docker. It is a complaint about a workflow where every interaction with real hardware requires navigating container boundaries.
What "Just Use Docker" Actually Means
For a robotics engineer on macOS or Windows, the standard ROS2 workflow is a container that runs Ubuntu, runs ROS2, maps ports, mounts volumes, and hopefully lets you reach your hardware through the host.
The container itself is straightforward. The problems start when you need to do anything that touches the physical world.
USB passthrough is unreliable on macOS. Docker Desktop on Mac runs a Linux VM. USB devices connect to the host macOS kernel, not the VM. Passing a USB device through to the container requires docker run --device or docker-compose device mappings, which do not always work for composite devices, serial-to-USB adapters, or devices that disconnect and reconnect during runtime. A camera that works fine on the host is invisible inside the container, or visible but not accessible, or accessible until the cable wiggles and the device path changes.
Serial ports need explicit mapping. A /dev/ttyUSB0 device on the host does not appear in the container unless you map it explicitly. If the device enumerates as /dev/ttyACM1 on the next plug-in, your launch file breaks. Most teams end up writing udev rules on the host to create stable symlinks, then mapping those symlinks into the container, then debugging why the container still cannot see them.
Real-time behavior does not translate. ROS2 nodes that need deterministic timing — motor controllers running at 1kHz, sensor polling with microsecond jitter requirements — rely on the host kernel's scheduler. Docker shares the host kernel, but the container adds scheduling layers and cgroups that can introduce jitter. A PREEMPT_RT patch on the host helps. Inside the container, the guarantees are weaker and harder to verify.
Network stack mismatch. Docker containers have their own network namespace by default. ROS2's DDS discovery expects to see the host's network interfaces. Bridged networking works for most cases, but host networking mode (--network host) does not exist on Docker Desktop for Mac. You end up mapping ports explicitly, managing DDS participant discovery across NAT boundaries, and debugging why two containers on the same laptop cannot see each other on the same ROS topic.
The Practical Workflow Split
The result is a two-class development environment. Engineers on Linux work on the full stack: simulation, software, and hardware. Engineers on macOS or Windows work on simulation and software, then hand off to a Linux machine or a robot itself to run anything that touches hardware.
That is a workflow boundary introduced by the platform, not by the problem. It adds friction to every iteration. It means a bug that only appears on hardware requires context-switching between machines, between file systems, and between terminal environments. It means the engineer who wrote the code is often not the engineer who debugs it on the robot.
What Teams Actually Do
Teams that need to support mixed OS environments usually end up with one of three approaches:
Approach 1: Full remote development. The robot or a dedicated Linux box on the network runs ROS2. macOS/Windows developers SSH in, edit code over VSCode Remote, and build on the Linux machine. This works but requires stable network access, shared file systems, and a Linux machine that is always on and reachable.
Approach 2: Simulation-only on the host, hardware on the robot. Developers run Gazebo and rqt on their local machine through Docker, but deploy and test on the robot directly. This keeps the hardware boundary clean but means you cannot step through a hardware driver in your local debugger.
Approach 3: Dual-boot or dedicated Linux machine. The most reliable solution and the least convenient. Engineers maintain a separate Linux workstation for ROS2 work, or dual-boot their laptop, or run a dedicated NUC that they SSH into. It works. It also means the "development machine" and the "daily driver" are not the same computer.
None of these are ideal. All of them exist because the ROS2 development toolchain assumes Linux and treats every other platform as an afterthought.
The Hardware Debugging Problem
The worst part is not the setup. It is the debugging loop.
A hardware driver fails in simulation but works on the real robot. Or it works in the container but not on the host. Or it works on one developer's Linux machine but not on another's Docker setup. The failure is often not in the ROS2 code. It is in the boundary between the container and the host: a missing device rule, a permission issue on /dev/tty*, a network namespace that drops multicast packets, a cgroup limit that throttles CPU at the wrong moment.
These are not ROS2 bugs. They are infrastructure bugs that consume ROS2 debugging time because the development environment is more complex than the robot software.
What to Take Away
Linux is the right choice for robotics production. The gap is that the development toolchain makes the cost of non-Linux environments higher than it needs to be. "Just use Docker" is a workaround, not a solution, and it introduces a category of problems — hardware passthrough, real-time scheduling, network discovery — that have nothing to do with robotics and everything to do with container abstractions.
If you are developing on macOS or Windows and working around the constraint with Docker or VMs, you are not doing anything wrong. You are paying a tax that the platform imposes. The question is whether that tax needs to be as high as it is.
Continue reading
Related Posts
Python ROS2 Nodes Start Fine. Then the Callbacks Start Drifting.
Most ROS2 teams start in Python. The API is cleaner. Iteration is faster. rclpy handles the majority of nodes without issue. Then at some point a timer fires late, a high-frequency publisher drops mes
May 26, 2026
Your ROS2 Subscriber Is Not Receiving Messages. No Error. No Warning. Nothing.
Your publisher is running. `ros2 topic echo` shows data flowing out. Your subscriber node is alive. The callback never fires. No error in the log. No warning on the console. Nothing. This is a QoS mis
May 26, 2026
10 Lines in ROS1. 50 in ROS2. The Launch File Problem Nobody Warned You About.
A developer on Reddit described migrating a real project from ROS1 to ROS2. The launch file went from 10 lines to 50. Same nodes. Same topics. Same robot. That is a real measurement from a real migrat
May 25, 2026