# gopcua: a native Go implementation of the OPC/UA binary protocol

> gopcua is a Go library that speaks the OPC/UA binary protocol directly, with a client, a server, security policies and the type system, and it is used in production by industrial software including Telegraf's OPC-UA plugin. The catch is stated plainly by the project itself: the APIs will change, and the version history is a list of breaking changes from v0.5.0 onwards.

**gopcua/opcua** — Native Go OPC-UA library

- Repository: https://github.com/gopcua/opcua
- Stars: 1,071 · Forks: 335
- Language: Go
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/gopcua-opcua

## What OPC/UA actually is, and why a native implementation is hard

gopcua is a native Go implementation of the OPC/UA Binary Protocol. That phrase hides most of the difficulty.

OPC/UA is the industrial interoperability standard, and the topic tags name the domain: golang, ics, opc-ua, protocol and scada. ICS means industrial control systems, which is the world of PLCs, sensors and plant equipment. A device in a factory exposes a hierarchical address space of typed nodes, and a client reads and writes them over a session.

The binary protocol is not a simple request and response protocol. It has sessions with authentication and nonces, security policies that sign and encrypt the transport, certificates that identify the application and the issuer, and a type system where every value carries its own encoding. There are multiple security modes, and the transport can be plain TCP, a secure channel, or a session that runs over a different mechanism.

Doing that natively in Go means implementing the encoding, the session handshake, the certificate handling, the security negotiation and the type system, all in one language with no C dependency. The repository layout shows the scope. There is client.go, client_sub.go, node.go, subscription.go, server.go and config.go at the top level, and then the protocol directories: ua/ for the type system, uacp/ for the binary protocol, uapolicy/ for the security policies, and uasc/ for the secure channel. The id/ directory holds identifier handling, the schema/ directory the address space schema, the monitor/ directory the server monitoring and the samples, and the errors/ directory the error types.

The library supports the current and previous major Go release, and go.mod declares go 1.23. The licence is MIT, and the repository is not archived with the last push on 2026-10-12. The three most recent releases are v0.9.1 on 2026-08-09, v0.9.0 on 2026-06-19 and v0.9.0-rc1 on 2026-06-18.

## The quickstart, and the node id syntax you have to get right

The quickstart is four commands, and it shows both the install and the shape of the API.

```sh
# install library
go get -u github.com/gopcua/opcua

# get current date and time 'ns=0;i=2258'
go run examples/datetime/datetime.go -endpoint opc.tcp://localhost:4840

# read the server version
go run examples/read/read.go -endpoint opc.tcp://localhost:4840 -node 'ns=0;i=2261'

# get the current date time using different security and authentication modes
go run examples/crypto/*.go -endpoint opc.tcp://localhost:4840 -cert path/to/cert.pem -key path/to/key.pem -sec-policy Basic256 -sec-mode SignAndEncrypt
```

The endpoint scheme is the standard OPC/UA one, opc.tcp:// followed by host and port, and 4840 is the conventional default port, which is why the examples point at localhost:4840 where a local server would be running.

The node identifiers are worth pausing on, because they are the part of OPC/UA that surprises newcomers. The datetime example is given a node id of ns=0;i=2258, and the read example uses ns=0;i=2261. That is a namespace index and a numeric identifier, separated by a semicolon, which is the ExpandedNodeId string form. The ns=0 is the standard OPC-UA namespace, which is where the server's own variables live, including the current date and time and the server version, and the i= part is the numeric identifier within it. A string identifier would be written as ns=0;s=SomeName instead.

The security flags show the shape of the client configuration. A certificate and key are supplied as paths, a security policy is named, and a security mode is set. Basic256 with SignAndEncrypt is the configuration that means the session's messages are both signed and encrypted, which is what a production plant network requires and what a lab setup will not.

The examples directory is extensive and gives a good map of the protocol surface: accesslevel, browse, crypto, datetime, discovery, endpoints, history-read, method, monitor, read, regread, server, subscribe, translate, trigger, udt and write. Those names are the OPC/UA services, and the presence of browse, read, write, history-read, subscribe and method covers most of what a client does, while translate and udt cover the type conversion and user defined type layers.

## The breaking change list is the API documentation

The most useful section in this repository is the one titled List of Breaking Changes and noteworthy issues, because for a library at version 0.9 it tells you exactly what a minor version bump costs you.

The v0.9.0 entry is the most recent and the most invasive. Node operations now run against a Client interface instead of the concrete *Client type, and code that constructs nodes against the concrete client type must be updated. The v0.8.0 entry changed the ResolveEndpoint signature, so it requires a context and returns the remote address as a url.URL instead of a *net.TCPAddr.

The v0.7.2 entry is a story about process rather than design. The wrong branch was tagged, the tag was deleted, the version was retracted, and the changes were moved to v0.7.3 and v0.7.4 instead. That is what a retract directive in go.mod is for, and the go.mod file records it, retracting v0.7.2 with the comment tagged the wrong branch, along with v0.2.5 and v0.2.4 which point at an issue.

The v0.7.0 entry changed the node attributes for the server to use ua.DataValue instead of ua.Variant, which is a semantic change rather than a signature change, and the v0.6.0 entry made the SelectEndpoint function in the client return an error. The v0.5.1 entry made NewClient return an error, and the v0.5.0 entry required that all Client methods have a context.

That last one is the most consequential of the lot, and it is the reason the pattern of the list reads as a push toward a more conventional Go API. A library that requires a context on every method is a library that composes with cancellation and deadlines, and the cost is that every call site in an existing program changes. It also means the error-returning constructors arrived gradually, from v0.5.1 onward, which is the standard way to get there without a single enormous break.

So the practical reading of this list is: a project on gopcua that wants to move a minor version should expect to touch call sites, and should budget for it. The list is explicit enough to plan against, which is more than most pre-1.0 libraries give you.

## The disclaimer, the maintainers and the sponsorship

The project states its own stability position in a section titled Disclaimer, and it is worth reading exactly.

We are still actively working on this project and the APIs will change. However, you can safely assume that we are aiming to make the APIs as stable as possible since the code is in use in several large scale production environments.

That is a two-part statement, and the second part is the interesting one. The project is telling you that the churn is deliberate and bounded by a commitment to stability, and the justification for that commitment is adoption, not aspiration. Several large scale production environments are running this code, so a change that breaks them is a change someone will report before it ships.

The maintainer situation is documented too. Jonathan Camp, who publishes as kung-foo, and Jeremy Theocharis agreed to be the new maintainers, with the full post linked in the discussions section. A library reaching for a second generation of maintainers is normal and healthy, and the point of recording it is continuity for anyone evaluating the project.

The sponsorship is the other continuity signal. The project lists organizations that sponsor it by supporting the active committers, with Northvolt, evosoft and Intelecy AS named. Sponsorship does not guarantee a maintainer, but for a protocol library with this many interacting specifications, it is how the work gets done.

The users list is separate from the sponsors and lists organisations running gopcua in production: Strateos, united manufacturing hub, Wolfram Manufacturing Technologies and IOTech System. The last slot in that table is an invitation, with a note to open a pull request to include a logo.

Then there is the downstream dependency list, which is the strongest argument for the library existing at all. Telegraf, the InfluxData plugin-driven agent, uses gopcua to extract data from OPC-UA servers and insert it into InfluxDB, supporting both polling and subscribing. And benthos-umh, built on the benthos stream-processing framework, uses it to extract data and forward it to MQTT or Kafka brokers, currently supporting polling only. A general purpose metrics agent and a stream processor both depending on this library is what makes it a protocol implementation rather than a personal project.

## The Makefile shows a test strategy built around real servers

The Makefile is where this project's engineering priorities become visible, and the first thing to notice is that the default target runs five things.

The all target is fmt, test, integration, selfintegration and examples. Formatting is gofmt over the tree. The unit tests run with -count=1, which disables the test cache so that all tests are run every time, and with -race. The comment at the top of the file explains the -count=1 choice, which is the kind of detail that only matters if you have been caught by a stale cache.

The integration targets are the interesting part. There are two of them, and they differ in what they test against. The integration target runs go test with the integration build tag over./tests/python, and selfintegration runs the same over./tests/go. So there is a Python-based test suite and a Go-based one, both run against a real server under the integration tag.

The examples target builds every example into a build/ directory, which means the example programs are compiled in CI rather than being documentation that rots.

The python test suite explains the install-py-opcua target, which is pip3 install opcua. That installs the Python `opcua` package, which the./tests/python integration suite runs against. Testing a Go client against a second independent implementation is a much stronger check than testing it against itself, and that is a real quality signal for a protocol library.

The lint target is staticcheck, and the gen target regenerates the stringer code, installing the stringer tool, deleting the existing *_gen.go files and running go generate over the tree. The repository has a connstate_strings_gen.go file at the top level, which is what stringer produces, and the generate.sh and generate.go files are the entry points for the rest of the code generation.

The release target uses goreleaser with a clean flag, and it takes the GitHub token from the macOS keychain with security find-generic-password, which means the release process is run by a maintainer on a Mac rather than in CI. There is a.goreleaser.yml at the repository root to configure it.

So the testing story is layered: unit tests with the race detector on every commit, integration tests against a real server in two languages, a cross-implementation comparison, and a build of every example. For a library whose entire job is byte-level protocol correctness, that is the test matrix you would want.

## Conclusion

gopcua fits a Go team building industrial connectivity, telemetry or a SCADA integration, who wants the protocol implemented natively rather than through a C binding, and who can absorb API churn at upgrade time. It is the wrong choice if you need a stable 1.0 API contract, because the project states the APIs will change, and the wrong choice if you need every optional OPC/UA service implemented, since the supported feature list is not exhaustive in the documentation. Before adopting, read the breaking change list for each minor version you are skipping, set up a test against a real server, and check the tested platforms table for whether your PLC vendor is on it, because that list is the project's own guidance on how well the library is exercised.

## FAQ

### How do I install gopcua and read a value from an OPC/UA server?

Run go get -u github.com/gopcua/opcua, then point one of the examples at an endpoint. The read example takes the endpoint and a node id, for example go run examples/read/read.go -endpoint opc.tcp://localhost:4840 -node 'ns=0;i=2261', where ns=0 is the standard namespace and i=2261 is the server version node.

### Is the gopcua API stable?

Not yet. The project states it is still actively working on the project and the APIs will change, while aiming for as much stability as possible because the code runs in several large scale production environments. The README keeps a list of breaking changes per version, including v0.5.0 requiring a context on all Client methods and v0.9.0 moving node operations onto a Client interface.

### How does gopcua handle OPC/UA security?

The crypto examples take a certificate and key path, a security policy and a security mode, for example -sec-policy Basic256 -sec-mode SignAndEncrypt. The repository has a uapolicy directory for the security policies and a uasc directory for the secure channel implementation.

### Which projects already depend on gopcua?

Telegraf uses it to extract data from OPC/UA servers and insert it into InfluxDB, supporting both polling and subscribing, and benthos-umh uses it to extract data and forward it to MQTT or Kafka brokers. The project also lists organisations running it in production, including Strateos, united manufacturing hub, Wolfram Manufacturing Technologies and IOTech System.

## Sources

- [gopcua/opcua on GitHub](https://github.com/gopcua/opcua)
- [Issues](https://github.com/gopcua/opcua/issues)
- [License: MIT](https://github.com/gopcua/opcua/blob/main/LICENSE)
- [README](https://github.com/gopcua/opcua/blob/main/README.md)
- [Releases](https://github.com/gopcua/opcua/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gopcua-opcua
