====== OPC UA stack feature comparison: what the four open-source stacks implement ====== Four open-source OPC UA stacks are realistic candidates for an industrial product: **S2OPC**, **open62541**, **node-opcua** and **python-asyncua**. This page compares them across the capabilities most often asked about when specifying a device or a gateway: security policies, PubSub and TSN, historical access, alarms, companion specifications, embedded targets and licensing. Every cell below was read in the stack's own source tree at a pinned release. Nothing here comes from a vendor feature chart. This is the overview. We maintain a much more granular comparison behind it (the full per-specification-Part matrix and a scored rubric), and how to get at those is at the [[technology:industry40:opcua:stack-comparison:feature-matrix#going_deeper_than_this_page|bottom of this page]]. ===== How was this verified? ===== Each capability was checked by reading the source at a named tag, then confirmed against the project's release history rather than a single snapshot. **Baselines.** S2OPC ''S2OPC_Toolkit_1.7.3'' (''b4c5c7d63'', 2026-06-08) · open62541 ''v1.5.5'' (''3bdeed5df'', 2026-06-28) · node-opcua ''v2.175.6'' (''e233d9061'', 2026-07-30) · python-asyncua ''v2.0.1'' (''f0e48e3f'', 2026-06-29). Read 2026-07-30. Two rules make the difference between this and a search-and-summarise exercise, and both were earned the hard way while building it: * A type identifier is not an implementation. Every one of these stacks generates its address-space types from the OPC Foundation nodesets, so a specification feature's //name// appears in all four whether or not anything implements it. Each entry below was followed to the code that runs, or recorded as absent. * A feature can outlive the name you would search for. Two capabilities on this page had been renamed, folded into neighbouring files, or had their build flag replaced by library auto-detection, so a search at the current release found nothing while the feature was still shipping. Negative findings here were walked back through each project's full history for that reason. ===== Feature overview ===== ^ Capability ^ S2OPC 1.7.3 ^ open62541 v1.5.5 ^ node-opcua v2.175.6 ^ asyncua v2.0.1 ^ ^ Security policies //(Part 2)// | ✅ classic set | ✅ classic set **plus ECC** | ✅ classic set | ✅ classic set | ^ PubSub //(Part 14)// | ✅ UADP over UDP, raw Ethernet, MQTT | ✅ as S2OPC, plus JSON encoding | ❌ | ⚠️ UADP over UDP | ^ TSN transmit scheduling //(ETF, ''SO_TXTIME'')// | ✅ | ✅ | ❌ | ❌ | ^ Alarms & conditions //(Part 9)// | ✅ server and client | ✅ server | ✅ full alarm type hierarchy | ⚠️ events and refresh only | ^ Historical access //(Part 11)// | ⚠️ raw read, off by default, storage delegated | ✅ pluggable historian backend | ✅ raw read, replaceable historian | ✅ pluggable backend, SQLite included | ^ Certificate management, GDS //push// //(Part 12)// | ✅ | ❌ | ✅ | ❌ | ^ Companion specs and nodeset tooling | ⚠️ utility scripts | ✅ nodeset compiler | ✅ 30+ companion models pre-generated | ⚠️ runtime XML loader | ^ Embedded targets | ✅ FreeRTOS, Zephyr, PikeOS | ✅ FreeRTOS, Zephyr | ❌ needs a Node.js runtime | ❌ needs CPython | ^ Licence | Apache-2.0 //permissive// | MPL-2.0 //file-scoped copyleft// | MIT //permissive// | LGPL-3.0-or-later //ship as a separate, relinkable library// | ✅ implemented · ⚠️ present but scoped, as stated in the cell · ❌ not present at the pinned release ===== Specifics worth knowing ===== Short factual notes that a category-level table cannot carry, and that are easy to get wrong from a feature search: * Aggregates (Part 13) are implemented only by node-opcua. The other three carry the generated aggregate type identifiers, which any feature search will find. The identifiers are not an implementation. * **HistoryUpdate is refused by all four.** In the two cases where the code says why, it is a deliberate policy rather than an unfinished path. * node-opcua's security-policy enumeration lists the PubSub AES-CTR policies. No crypto factory resolves them; a request for either is rejected as unsupported. * ECC security policies (nistP256/384, brainpoolP256r1/P384r1) exist only in open62541 among these four. * open62541 declares a historical-event read hook, and no backend in the tree implements it. Reading event history there is work you do yourself. * python-asyncua binds ConditionRefresh, and no handler for Acknowledge or Confirm. The alarm nodes exist; the acknowledgement round trip is yours to complete. * S2OPC is the only one of the four that has dropped the deprecated Basic128Rsa15 policy. Stricter, and a problem the day you must talk to old equipment. * Both C stacks do ETF (''SO_TXTIME'') transmit scheduling, which is what lets a publisher be placed inside a TSN transmission window. What we checked is that the machinery is in the library and that each project's TSN sample drives it. Whether the library's own publisher wires it for you, without you writing that part, is a question for the project. ===== What the licences mean in practice ===== The four span the range, and the difference only bites once a design has hardened. In outline, and subject to the disclaimer at the foot of this page: * **MIT and Apache-2.0** are permissive. Attribution and notice obligations; no obligation to publish anything of your own. * **MPL-2.0** is copyleft scoped to the file. Changes to the project's own files carry a publication obligation; code you write alongside it does not. * **LGPL-3.0-or-later** is the one that constrains how you //ship//. It is satisfied comfortably where the library is a separate, replaceable dependency the user could relink; it needs looking at properly where it would be frozen into an image the customer cannot relink. ===== How current is this? ===== Every one of these projects is under active development, and this page is a reading taken on 2026-07-30 at the releases named above. Treat it as a map of where the differences //were//, accurate enough to shortlist with and not a substitute for checking the tag you will actually ship. Two things we would say plainly about that, having just spent two passes on it: * **Absences age fastest.** A ❌ here means we read the tree and did not find it, not that the project will not have it next quarter. Two of the capabilities on this page were added upstream between our first pass and our second. * **Names age too.** Twice while building this we found a feature still shipping under a name that no longer existed, because the file had been renamed or its build flag replaced by auto-detection. If your own check comes back empty, check the history before concluding anything. For anything load-bearing (a capability a design depends on, or an absence you are planning around), go to the project's own repository and release notes at your target version. We are happy to do that with you; we are not a substitute for it. ===== Going deeper than this page ===== The table above is deliberately a category-level view of the capabilities that come up most often. Behind it we maintain two larger documents, both current as of the baselines named above: * **The full per-Part matrix** — every specification Part we checked, including the rows where all four stacks behave identically, with the file and line number behind each verdict. This is the document you want when you are //specifying// a device rather than choosing a stack: it answers "does candidate X do exactly this" rather than "which is better". * **The scored rubric** — our in-house evaluation instrument, applied to the stacks under consideration and weighted for the deployment you are actually building. The weighting is the substance of it, and it changes with the target: a bounded-memory device and an application-tier gateway do not score the same stack the same way. Both are available on request, and we will walk you through them. If you are running a stack selection now, the faster route is usually to tell us the constraints (target hardware, whether PubSub or TSN is in scope, historian requirements, certification obligations) and let us apply the rubric to your shortlist rather than to ours. [[https://aeicor.com/contact/|Talk to us about a stack evaluation]] ===== Related ===== * [[technology:industry40:opcua:stack-comparison:start|Stack comparison — summary and guidance]] * [[technology:industry40:opcua:stack-comparison:assessment-2020|The 2020 assessment]] — the first version of this comparison, kept as a dated historical record * [[technology:industry40:opcua:start|OPC UA]] * [[use-cases:condition-monitoring|Condition monitoring]] — where the historical-access differences decide the design //**Notes and disclaimers.** Every cell on this page was read in the named project's own source tree at the release stated above, on 2026-07-30, and is not a vendor claim; line and file references drift between releases. A ❌ means we read the tree and did not find it, at that release, not that the capability does not exist elsewhere or will not arrive. These projects are under active development, so for anything a design depends on, verify against the project's own repository at the version you will ship. Licence descriptions are a plain-language summary for orientation only, not legal advice and not a complete statement of terms. Check the current licence text with the respective project, and take your own advice on how it applies to how you intend to ship.//