Two open-source OPC UA server stacks dominate serious embedded use: open62541 (C, MPL-2.0) and S2OPC (C, Apache-2.0, Systerel). They are not interchangeable: open62541 leads on features and ecosystem (full PubSub including subscriber and TSN reference work, released ECC security policies, companion-spec tooling) while S2OPC leads on the determinism and assurance axes (configuration-bounded resource model, formal-methods pedigree, CSPN security certification). This page compares them criterion by criterion, from source-level evidence, and ends with concrete “choose X when…” guidance.
If you are not building an embedded device, other open-source SDKs may fit better: node-opcua (TypeScript/Node.js, for fast prototyping and dashboards), opcua-asyncua (Python, for test tooling and scripting), UA .NET Standard (C#, the OPC Foundation reference stack, at home in Windows/.NET shops), Eclipse Milo (Java). For firmware on MCU/RTOS/embedded-Linux targets, the realistic open-source shortlist is the two C stacks below.
We scored both stacks against an in-house rubric of 26 criteria in 8 categories (~100 points), built for engagements with device OEMs. Every score is backed by a file-level citation in the stack's own repository or documentation: no vendor claims, no hearsay.
| Category | What it asks | Leader |
|---|---|---|
| Deterministic memory model | Static pools vs heap; caps on growth; instrumentation | S2OPC (bounded-by-configuration) |
| Overload behavior & caps | Are all unbounded paths capped, with defined degradation? | S2OPC |
| Security (ECC + cert lifecycle) | Policy coverage incl. ECC; runtime cert/trust-list management; fuzzing | open62541 (ECC released; S2OPC has GDS push) |
| PubSub maturity | Writer, subscriber/Reader path, TSN evidence | open62541 (full 15/15 class) |
| Companion-spec / nodeset tooling | Nodeset compiler, shipped companion-spec practice, UA FX | open62541 (neither has UA FX yet) |
| Certification evidence | OPC F certification currency; independent security evaluation | S2OPC (CSPN; both OPC F certs are 2017-profile era) |
| Footprint & WCET evidence | Reproducible ROM/RAM numbers; bounded execution | open62541 (narrowly — documented <100kB targets + toggles) |
| License / commercial | Firmware-linking fit; sustainability | S2OPC (Apache-2.0 vs MPL-2.0; both commercially supported) |
Strengths. Every resource that matters is bounded at compile time via an overridable constants set (sopc_toolkit_config_constants.h: sessions, secure channels, subscriptions, notification queues, republish queues…), with cap-consistency statically checked at build. The services layer is generated from a B-method formal model, with Frama-C / TIS static analysis on top. Server-side GDS push certificate management (ServerConfiguration, TrustList, CertificateGroup methods) is implemented, which is rare in open source. Two maintained crypto backends (mbedTLS, CycloneCRYPTO). The ANSSI CSPN security certification (scoped to v1.3.1, with BSI mutual recognition) is the only independent security evaluation on either side. Apache-2.0 licensing is friction-free for shipped firmware. Maintained FreeRTOS, Zephyr and PikeOS ports plus a NANO_PROFILE strip-down switch.
Limits to know. A claim you will hear is that S2OPC is “static memory”. Source inspection says something more precise: the limits are set at compile time over a platform heap, not held in static pools. SOPC_Malloc wraps malloc/pvPortMalloc/k_malloc on every port, so operational paths still allocate; worst-case usage is bounded by configuration, not free of allocation. There are no ECC security policies yet, only the modern RSA set. The PubSub subscriber exists, but its configuration is static and agreed in advance, and there are no published footprint numbers with a reproducible build config.
Strengths. The feature and ecosystem reference among open-source stacks. ECC security policies are released (nistP256/384, brainpool, since v1.5.4; note that the FEATURES table lags the code, so check the crypto plugin sources). PubSub is complete: UADP + JSON encodings, UDP/raw-Ethernet/MQTT transports, a DataSetReader path configurable at runtime, and the best public TSN evidence of any open-source stack (ETF transmit scheduling via SO_TXTIME in the POSIX Ethernet event loop, arch/posix/eventloop_posix_eth.c:590-607; four real-time publisher/subscriber examples and two published TSN measurement papers shipped in examples/pubsub_realtime/). One thing to know if you are shopping on TSN capability: open62541 used to ship an AF_XDP receive path, and the current releases do not. The history is worth reading correctly, because the online documentation that describes it is not wrong, just old:
plugins/ua_pubsub_ethernet_xdp.c (721 lines), enabled by UA_ENABLE_PUBSUB_ETH_UADP_XDP. Present through v1.2.10 (2024-08-09).0d4ca033a (2021-01-04) folded the plugin into ua_pubsub_ethernet.c and replaced the build flag with automatic libbpf detection (CMakeLists.txt: “bpf library not found. XDP functionalities will not be available.”). A search for the old flag finds nothing here and concludes wrongly.d816b5bb6 (2023-06-16), “refactor(pubsub): Remove the old PubSub connection API”, deleted the entire legacy transport plugin layer in one pass (4442 deletions: the Ethernet, UDP, multicast and MQTT plugins together). UDP, Ethernet and MQTT were reimplemented on the new event-loop connection manager, which is where the SO_TXTIME path cited above lives. XDP was not carried across.XDP is therefore a transport that was never reimplemented rather than a feature anyone abandoned, so the question worth putting to the maintainers is whether an event-loop XDP connection manager is planned, not whether XDP “works”.
Also in its favour. A mature Python nodeset compiler against the full OPC Foundation NodeSet corpus, with shipped companion-spec examples. Continuous fuzzing on Google oss-fuzz, CI gated on static analysis, documented LTS and vulnerability management. Famously fine-grained compile toggles, a documented <100 kB RAM/ROM minimization path, and FreeRTOS/lwIP/Zephyr ports in-tree.
Limits to know. The memory model is plain heap on every path (UA_malloc aliases malloc); caps are runtime struct fields, and two of them ship unlimited: maxRetransmissionQueueSize and maxEventsPerNode are both set to 0 /* unlimited */ in plugins/ua_config_default.c:490,492. Nothing defines the order in which the server degrades under overload, so exhaustion surfaces as a per-call BadOutOfMemory. A single event loop carries everything, so there is no workload isolation between services, subscriptions and I/O. No independent security evaluation (CSPN or 62443 class). MPL-2.0 is manageable copyleft, scoped to the file, but it does carry disclosure obligations on modified files.
| Your situation | Choose |
|---|---|
| PubSub/TSN is the workload; you need subscriber + writer today | open62541 |
| ECC security policies required now (spec 1.05 policies) | open62541 |
| Heavy companion-spec / information-model work | open62541 |
| Procurement demands independent security evaluation (CSPN-class) | S2OPC |
| RTOS target where bounded worst-case memory must be argued | S2OPC |
| Certificate lifecycle via GDS push, on-device, today | S2OPC |
| Copyleft-free firmware licensing required | S2OPC (Apache-2.0) |
| Broad ecosystem, MQTT/JSON northbound, Linux-class target | open62541 |
| UA FX (Parts 81–84) device support | Neither yet — no UAFX code in either tree |
Both OPC Foundation certifications on record are against 2017-era profiles (S2OPC: Micro Embedded Device Server; open62541: Standard Server, v1.4 example server). Those are older baselines than the current CTT 1.05.06, which restructured conformance units and extended ECC and UA FX coverage. Whichever stack you pick, plan your own conformance run against the current CTT.
Commercial embedded SDKs add long-term support contracts, pre-certification artifacts and vendor accountability, at license cost: Matrikon FLEX (C++, flat-rate licensing per developer, spanning MCU to multicore), Unified Automation High Performance SDK (C99, single-thread architecture, royalty-free deployment per developer license), Softing dataFEED OPC UA SDKs (C++/embedded, sold on a maintenance contract). These characteristics are drawn from vendor documentation. Licensing structures, supported versions and certification status all change, so check the vendor's current documentation before evaluating or specifying one. The open-source pair above is fully sufficient for L&D, PoCs and interop testing, and, engineered carefully, for production devices.
These scores come from an in-house rubric we built for work with device OEMs: 26 criteria across 8 categories, roughly 100 points total, with anchored scoring so each point means the same thing across stacks. We applied it at source-inspection and documentation depth against the pinned commits above. There are no runtime benchmarks at this stage, and the table says so rather than leaving gaps. Read the category subtotals, not the total: the two stacks were built for different failure models, and the total obscures that.
Category leaders at a glance: S2OPC leads on memory determinism, overload behaviour and certification evidence; open62541 leads on security policies, PubSub maturity and companion-spec tooling. Licensing favours S2OPC (Apache-2.0 vs MPL-2.0 file-scoped copyleft).
This page is deliberately short on rows: it carries only the criteria that separate the two stacks. If you are specifying a device rather than choosing a stack, you want the rows where both behave the same too, and those are in the feature comparison, which covers all four open-source stacks rather than these two. A granular per-Part version, source-verified cell by cell including the rows where the answer is the same everywhere, is available on request.
The full per-criterion scorecard with file:line source evidence is in Stack comparison: full scorecards — available on request or after sign-in. If you want the rubric applied to your own shortlist (including commercial candidates), get in touch.
Baseline: open62541 v1.5.5 (tagged 2026-06-28) and S2OPC_Toolkit_1.7.3 (tagged 2026-06-08), re-verified against the tags on 2026-07-30. The first pass was run against development commits ahead of both tags; every criterion was re-checked at the tag and no verdict or category score changed. ECC status, PubSub maturity and certification profiles shift between releases, so treat this as a starting point rather than a final answer and check the release notes before committing to either stack.