Skip to main content

OPC UA Server

This page documents the server route (OPCUA_SERVER)—the broker hosts an OPC UA server and exposes MQTT topics as nodes. External clients connect to the broker. To connect the broker to a remote OPC UA device instead, see OPC UA Client.
The OPCUA_SERVER route makes your broker host an OPC UA server. External clients—SCADA, UaExpert, PLCs—connect to the broker and see your MQTT topics as OPC UA nodes. Use OPCUA when you need to read from a device. Use OPCUA_SERVER when OT systems need to browse and write values that live on your MQTT bus.
Like turning your broker into a shop window. MQTT messages update what’s on display (node values), and customers (OPC UA clients) can look—and, for writable tags, change items that publish back to MQTT.

When to Use This Route

  • SCADA integration — Let a SCADA or HMI browse broker data without custom drivers.
  • OT/IT bridge — Expose IoT sensor streams to plant-floor tools that speak OPC UA.
  • Bidirectional control — Writable tags let clients adjust setpoints; the broker publishes the new value to MQTT.
  • Dynamic discovery — Wildcard topic mappings create nodes automatically as matching topics first publish.

The sections below cover data flow, configuration, persistence, and production patterns.

Quick Start

Map MQTT topics to fixed OPC UA nodes. Each ADD TAG creates one node; publishing to {SOURCE_TOPIC}/{TagName} updates it.
DEFINE ROUTE PlantServer WITH TYPE OPCUA_SERVER
    ADD OPCUA_SERVER_CONFIG
        WITH SERVER_PORT 4840
    ADD MAPPING Line1
        WITH SOURCE_TOPIC "factory/line1"
        ADD TAG Temperature
            WITH DATA_TYPE "FLOAT"
        ADD TAG Counter
            WITH DATA_TYPE "INT32"
  • Publish 23.5 to factory/line1/Temperature → node ns=2;s=PlantServer/Temperature reads 23.5.
  • Publish 1042 to factory/line1/Counter → node ns=2;s=PlantServer/Counter reads 1042.

How Data Flows

DirectionBehavior
MQTT → nodeEach tag binds to an MQTT topic via SOURCE_TOPIC. A publish on that topic sets the node value. The MQTT payload is authoritative—no scaling or rounding is applied inbound. Retained values present before the route starts are seeded at startup.
node → MQTTWhen a tag has WITH WRITABLE true, an external client writing the node publishes the new value back to the same SOURCE_TOPIC, retained. FLOAT and DOUBLE values are rounded to DECIMAL_PLACES (default 2) and clamped by MIN_VALUE / MAX_VALUE. Client writes do not loop back into duplicate node updates.
Wildcard mappingsWildcard SOURCE_TOPIC patterns create nodes on first publish. See Dynamic topic handling.

Server Configuration

All settings live in the OPCUA_SERVER_CONFIG block. Store credentials with GET ENV or GET SECRET. The example below shows a production-oriented config: custom identity, encrypted endpoint, username/password auth, and a persistent certificate store. Clients connect at opc.tcp://<broker-host>:4840/coreflux.
DEFINE ROUTE PlantOpcServer WITH TYPE OPCUA_SERVER
    ADD OPCUA_SERVER_CONFIG
        WITH SERVER_PORT 4840
        WITH ENDPOINT_PATH "/coreflux"
        WITH APPLICATION_NAME "Coreflux Plant Server"
        WITH APPLICATION_URI "urn:plant.example.com:Coreflux:PlantOpcServer"
        WITH NAMESPACE_URI "urn:plant.example.com:opcua"
        WITH ROOT_FOLDER "Plant"
        WITH PKI_DIR "/data/opcua-pki"
        WITH USE_SECURITY true
        WITH SECURITY_POLICY 3
        WITH MESSAGE_SECURITY 2
        WITH ALLOW_ANONYMOUS false
        WITH USERNAME GET ENV "OPCUA_USER"
        WITH PASSWORD GET SECRET "OPCUA_PASSWORD"
        WITH AUTO_ACCEPT_UNTRUSTED_CERTIFICATES false
    ADD MAPPING Tags
        WITH SOURCE_TOPIC "plant/tags"
        ADD TAG Status
            WITH DATA_TYPE "INT32"

Connection and identity

SERVER_PORT
integer
default:"4840"
TCP port the server listens on (opc.tcp). Each server route claims its port process-wide—two routes cannot share the same port.
ENDPOINT_PATH
string
Optional path appended to the base address, for example /coreflux. Default: empty.
APPLICATION_NAME
string
default:"CorefluxBroker_OpcUaServer"
Application display name and certificate common name (CN).
APPLICATION_URI
string
default:"urn:{host}:Coreflux:{route}"
Application URI. Must match the certificate SAN URI. Changing this regenerates the certificate.
NAMESPACE_URI
string
default:"urn:coreflux:opcua:{route}"
Namespace URI for exposed nodes.
ROOT_FOLDER
string
default:"route name"
Root folder under Objects where tags are organized.
PKI_DIR
string
Directory for own, issuer, trusted, and rejected certificate stores. Defaults to the OPC Foundation PKI layout. In Docker, mount this path as a volume so certificates persist across restarts. See Certificate Management.

Security

USE_SECURITY
boolean
default:"true"
Offer a signed or encrypted endpoint.
This defaults to true to comply with OPC Foundation specifications, which require secure endpoints. For testing purposes, you might need to set it to false.
SECURITY_POLICY
integer
default:"0"
Security policy offered when USE_SECURITY is true:
ValuePolicy
0None
3Basic256Sha256
4Aes128_Sha256_RsaOaep
5Aes256_Sha256_RsaPss
MESSAGE_SECURITY
integer
default:"0"
Message security mode. Requires SECURITY_POLICY ≥ 1 when using Sign or SignAndEncrypt.
ValueMode
0None
1Sign
2SignAndEncrypt
ALLOW_NO_SECURITY
boolean
default:"false"
When USE_SECURITY is true, also offer a None/None endpoint. Use for testing only.

Authentication

ALLOW_ANONYMOUS
boolean
default:"true"
Offer anonymous login. Set to false for production and configure USERNAME/PASSWORD or ALLOW_CERTIFICATE_AUTH.
USERNAME
string
Username for password-based login. Use GET ENV or GET SECRET.
PASSWORD
string
Password for password-based login. Use GET ENV or GET SECRET.
ALLOW_CERTIFICATE_AUTH
boolean
default:"false"
Offer X.509 certificate user authentication.
AUTO_ACCEPT_UNTRUSTED_CERTIFICATES
boolean
default:"false"
Automatically trust connecting client certificates. When false, untrusted client certs are placed in the rejected store.
PERSISTENCE
boolean
default:"false"
Persist served node values across broker restarts. See Data persistence & recovery.
An application instance certificate is auto-generated (self-signed) on first start under PKI_DIR/own. If you change APPLICATION_URI, the certificate is regenerated to match.

Tag and Mapping Configuration

Tags reuse the same vocabulary as the OPC UA client route, but several settings carry different meaning on the server side.

Server vs. client semantics

SettingClient (OPCUA)Server (OPCUA_SERVER)
SOURCE_TOPICTopic where read values are publishedBidirectional binding — MQTT sets the node; client writes publish back here
WRITABLEBroker may write the remote deviceExternal clients may write the node
DESTINATION_TOPICWrite-command topicNot used on server routes
ADDRESSRequired remote NodeIdOptional explicit NodeId; default s={ROOT_FOLDER}/{TagName}
EVERYPoll intervalIgnored — updates are event-driven from MQTT
QUERY eventsOn-demand read/writeNot supported
SCALING / OFFSET / DEADBANDApplied to reads from the deviceNot applied inbound — MQTT payload is authoritative
DECIMAL_PLACES / MIN_VALUE / MAX_VALUEApplied to readsApplied when a client write is published to MQTT
STORE TO / triggersSupported on client routesNot supported
On server routes, route status Connected means the endpoint is listening—not that a remote session is established.

Explicit NodeId

By default, a tag named Temperature under route PlantServer with ROOT_FOLDER defaulting to the route name maps to ns=2;s=PlantServer/Temperature. Override with ADDRESS:
DEFINE ROUTE PlantServer WITH TYPE OPCUA_SERVER
    ADD OPCUA_SERVER_CONFIG
        WITH SERVER_PORT 4840
    ADD MAPPING Line1
        WITH SOURCE_TOPIC "factory/line1"
        ADD TAG Temperature
            WITH ADDRESS "ns=2;s=Custom/Line1/Temp"
            WITH DATA_TYPE "FLOAT"

Writable tag parameters

For writable tags, configure outbound formatting the same way as on the client route:
WRITABLE
boolean
default:"false"
Allow OPC UA clients to write this node. Writes publish to the tag’s SOURCE_TOPIC.
DECIMAL_PLACES
integer
default:"2"
Decimal places applied to FLOAT/DOUBLE values when a client write is published to MQTT.
MIN_VALUE
double
Minimum value after a client write. Values below this are clamped.
MAX_VALUE
double
Maximum value after a client write. Values above this are clamped.
PERSISTENCE
boolean
Per-tag override of the route persistence default. Precedence: tag → mapping → route. See Data persistence & recovery.

Supported data types

Use the same DATA_TYPE values as the OPC UA client route—for example BOOLEAN, INT32, FLOAT, DOUBLE, and STRING. Tags can also expose one-dimensional arrays or struct objects—see Array and struct nodes. For statically defined scalar tags, set DATA_TYPE explicitly. Dynamic nodes infer type from the first MQTT payload (see below).

Dynamic Topic Handling

Wildcard mappings let you expose many MQTT topics without listing each one as an ADD TAG. When a topic matches the pattern for the first time, the route creates a new OPC UA node automatically.

How it works

  1. Define a mapping with a wildcard SOURCE_TOPIC—for example sensors/+/value or plant/#.
  2. Do not add ADD TAG entries under that mapping. The wildcard pattern alone drives node creation.
  3. On the first publish to a matching topic, the route creates an OPC UA node and sets its value from the payload.
  4. Later publishes to the same topic update that node. High-frequency updates use an equality check so unchanged values do not spam change notifications.
Dynamic nodes are read-only. OPC UA clients cannot write them back to MQTT. Use static ADD TAG definitions with WRITABLE true when you need bidirectional control.

Topic → node mapping

The NodeId string identifier mirrors the full MQTT topic path:
MQTT topic (first publish)OPC UA NodeId
sensors/press1/valuens=2;s=sensors/press1/value
sensors/tank2/valuens=2;s=sensors/tank2/value
plant/line3/motor/rpmns=2;s=plant/line3/motor/rpm
Under Objects, folder nodes are created from topic segments so clients can browse a hierarchy that matches your MQTT layout—for example sensorspress1value.

Supported wildcards

PatternMatchesExample
+ (single level)Exactly one topic segmentsensors/+/value matches sensors/press1/value but not sensors/line1/temp/value
# (multi-level)Zero or more segments at the endplant/# matches plant/line1/temp and plant/line1/motor/rpm
Combine static and dynamic mappings in the same route. Static ADD TAG entries under a non-wildcard SOURCE_TOPIC behave as fixed nodes; wildcard mappings handle everything else that matches.

Type inference

Dynamic nodes have no DATA_TYPE in the route definition. The broker infers the OPC UA type from the first payload and locks it for the lifetime of that node:
First MQTT payloadInferred OPC UA type
Numeric (42, 23.5)Double
true or false (case-insensitive)Boolean
Anything elseString
If a later publish cannot be converted to the pinned type, the node keeps its previous value and the broker logs an error. Publish a representative value first—do not send a placeholder string if you expect numeric updates later.

Example

This mapping exposes every sensors/<device>/value topic as it appears on the bus:
DEFINE ROUTE SensorGateway WITH TYPE OPCUA_SERVER
    ADD OPCUA_SERVER_CONFIG
        WITH SERVER_PORT 4840
    ADD MAPPING Sensors
        WITH SOURCE_TOPIC "sensors/+/value"
Sequence:
  1. Route starts; no dynamic nodes exist yet.
  2. Device publishes 45.2 to sensors/press1/value → node ns=2;s=sensors/press1/value appears with value 45.2 (Double).
  3. Device publishes false to sensors/alarm1/value → node ns=2;s=sensors/alarm1/value appears with value false (Boolean).
  4. UaExpert refresh shows both nodes under the sensors folder. Further publishes update values in place.
Retained messages already on the broker when the route starts are seeded the same way as for static tags—matching topics create nodes at startup.

Dynamic node limits

Dynamic nodes are not removed while the route is running. They persist until the route is removed or the broker restarts. Plan wildcard patterns carefully in high-churn environments where topic names change frequently.

Array & Struct Nodes

Scalar tags expose one value per node. Use an array tag when MQTT carries a list of values on one topic, or a struct tag when the payload is a JSON object with named fields. OPC UA clients then browse and write the same shape—no extra config block beyond the tag settings below.

Array tags

An array tag turns a JSON list on MQTT into a single OPC UA array node. Publish a list; the client sees one node with multiple elements. With WRITABLE true, a client edit publishes the full list back to the same topic, retained.
DATA_TYPE
string
Set to "ARRAY" for list values.
ARRAY_ELEMENT_TYPE
string
default:"INT16"
Type of each element: BOOL, INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT, DOUBLE, or STRING.
ARRAY_SIZE
integer
Number of elements in the array. Set this to match your payload length. If omitted, the dimension is unspecified.
Define the tag, then publish a JSON array to {SOURCE_TOPIC}/{TagName}:
DEFINE ROUTE PlantServer WITH TYPE OPCUA_SERVER
    ADD OPCUA_SERVER_CONFIG
        WITH SERVER_PORT 4840
    ADD MAPPING Line1
        WITH SOURCE_TOPIC "factory/line1"
        ADD TAG Levels
            WITH DATA_TYPE "ARRAY"
            WITH ARRAY_ELEMENT_TYPE "DOUBLE"
            WITH ARRAY_SIZE 3
            WITH WRITABLE true
What happens:
  • Publish [1.5, 2.5, 3.5] to factory/line1/Levels → node ns=2;s=PlantServer/Levels shows three values.
  • Payloads wrapped as {"value": [1.5, 2.5, 3.5]} are accepted—the broker unwraps the list automatically.
  • A client write to the array node publishes the updated list back to factory/line1/Levels.

Struct tags

A struct tag maps a JSON object to a folder-like node with one child per field. Field names in MQTT must match the names you declare in STRUCT_FIELD_DEFINITIONS.
DATA_TYPE
string
Set to "STRUCT" for object payloads.
STRUCT_FIELD_DEFINITIONS
string
Comma-separated field list in name:type form—for example bRunning:BOOL,iRpm:INT,sState:STRING. If you reuse a definition from an ADS or EtherNet/IP route (name:offset:type:size), the server uses the name and type only; offset and size are ignored.
DEFINE ROUTE PlantServer WITH TYPE OPCUA_SERVER
    ADD OPCUA_SERVER_CONFIG
        WITH SERVER_PORT 4840
    ADD MAPPING Line1
        WITH SOURCE_TOPIC "factory/line1"
        ADD TAG Motor
            WITH DATA_TYPE "STRUCT"
            WITH WRITABLE true
            WITH STRUCT_FIELD_DEFINITIONS "bRunning:BOOL,iRpm:INT,sState:STRING"
What happens:
  • Publish {"bRunning": true, "iRpm": 1450, "sState": "RUN"} to factory/line1/Motor → clients see Motor.bRunning, Motor.iRpm, and Motor.sState under the struct node.
  • With WRITABLE true, a client writing one field to OPC-UA (for example 1600 to Motor.iRpm) merges into the current struct and publishes the full object back to MQTT factory/line1/Motor—for example {"bRunning": true, "iRpm": 1600, "sState": "RUN"}.
  • Fields that were never set by MQTT and were not written by the client are left out of that publish.
DECIMAL_PLACES, MIN_VALUE, and MAX_VALUE apply to scalar writable tags only—not to individual struct fields.

Security Examples

For lab or trusted networks where encryption is not required:
DEFINE ROUTE DevServer WITH TYPE OPCUA_SERVER
    ADD OPCUA_SERVER_CONFIG
        WITH SERVER_PORT 4840
        WITH ALLOW_ANONYMOUS true
    ADD MAPPING Demo
        WITH SOURCE_TOPIC "demo/tags"
        ADD TAG Value
            WITH DATA_TYPE "FLOAT"

Certificate Management

A secured OPC UA server connection requires two certificates that trust each other: the broker’s application instance certificate (identifies and secures the server) and each client’s certificate (must be in the server’s trusted store when using certificate authentication). For client-side trust workflows when the broker dials out to remote servers, see OPC UA Client.

PKI directory layout

Both client and server routes store certificates in four folders under PKI_DIR:
FolderHoldsFile form
ownThis server’s certificate (own/certs) and private key (own/private)*.der + *.pfx
issuerTrusted CA / issuer certificates for chain validation*.der
trustedTrusted client certificates (trusted/certs)*.der
rejectedClient certificates seen during a handshake but not yet trusted*.der

Where certificates live

When PKI_DIR is not set, it defaults to %LocalApplicationData%/OPC Foundation/pki:
OSDefault path
macOS~/Library/Application Support/OPC Foundation/pki
Linux$XDG_DATA_HOME/OPC Foundation/pki or ~/.local/share/OPC Foundation/pki
Windows%LOCALAPPDATA%\OPC Foundation\pki
Relocate the store to a path you control and mount it as a volume in Docker:
ADD OPCUA_SERVER_CONFIG
    WITH SERVER_PORT 4840
    WITH PKI_DIR "/data/opcua/pki"
In Docker, the default PKI location is ephemeral—the broker regenerates its certificate on every restart and clients that trusted the previous one will reject the new one. Always set PKI_DIR to a mounted volume.

Inspecting and exporting certificates

# The server's own certificate and private key
ls "$PKI_DIR"/own/certs/*.der
ls "$PKI_DIR"/own/private/*.pfx

# Inspect subject, validity, and application URI SAN
openssl x509 -inform der -in "$PKI_DIR"/own/certs/<name>.der -noout -subject -dates -ext subjectAltName

# Export public cert for clients to trust
openssl x509 -inform der -in "$PKI_DIR"/own/certs/<name>.der -out server-public.pem
Give clients the public certificate (own/certs/*.der). The private key never leaves the broker.

The server’s own certificate

On first start the broker auto-generates a self-signed server certificate (CN={APPLICATION_NAME}, O=Coreflux; default APPLICATION_NAME is CorefluxBroker_OpcUaServer) in PKI_DIR/own. Its SAN URI matches APPLICATION_URI (default urn:{host}:Coreflux:{route}):
DEFINE ROUTE PlantServer WITH TYPE OPCUA_SERVER
    ADD OPCUA_SERVER_CONFIG
        WITH SERVER_PORT 4840
        WITH APPLICATION_NAME "AcmePlantServer"
        WITH APPLICATION_URI "urn:acme:opcua:plant-server"
        WITH SECURITY_POLICY 3
        WITH MESSAGE_SECURITY 2
        WITH PKI_DIR "/data/opcua/pki"
  • Keep APPLICATION_URI and the certificate SAN in sync. Strict clients refuse the handshake on a mismatch; changing APPLICATION_URI regenerates the certificate to match.
  • Clients trust this server by adding PKI_DIR/own/certs/<name>.der to their trusted list—the same workflow as trusting a server certificate on the client route.

Enable certificate user authentication

Offer the X.509 user token so clients authenticate with a certificate instead of—or alongside—username and password:
DEFINE ROUTE CertServer WITH TYPE OPCUA_SERVER
    ADD OPCUA_SERVER_CONFIG
        WITH SERVER_PORT 4840
        WITH USE_SECURITY true
        WITH ALLOW_ANONYMOUS false
        WITH ALLOW_CERTIFICATE_AUTH true
When ALLOW_CERTIFICATE_AUTH true, the application instance certificate doubles as the user identity—the client’s certificate must be trusted and must carry a valid application URI SAN.

Trust connecting client certificates

When a client connects with certificate authentication, its certificate must be in the server’s trusted store:
1

First connect

The client’s certificate lands in PKI_DIR/rejected/certs.
2

Move to trusted

mv "$PKI_DIR"/rejected/certs/<client-cert>.der "$PKI_DIR"/trusted/certs/
3

Reconnect

The client connects successfully.
For testing only, set WITH AUTO_ACCEPT_UNTRUSTED_CERTIFICATES true to auto-trust every connecting client certificate.

Supply your own server certificate

The server route has no CLIENT_CERT_PATH-style parameter. To run with a CA-issued certificate instead of the auto-generated one, place it in the own store before first start, with a subject matching CN={APPLICATION_NAME}, O=Coreflux and a SAN URI matching APPLICATION_URI:
mkdir -p "$PKI_DIR"/own/certs "$PKI_DIR"/own/private
openssl x509 -in server-cert.pem -outform der -out "$PKI_DIR"/own/certs/server.der
openssl pkcs12 -export -inkey server-key.pem -in server-cert.pem \
  -out "$PKI_DIR"/own/private/server.pfx -passout pass:
If a valid matching certificate is already present, the broker uses it instead of generating one.

Rotation and renewal

  • Auto-generated certificates — Delete files in PKI_DIR/own/certs and PKI_DIR/own/private, then restart the route; the broker regenerates.
  • Changing APPLICATION_URI — Regenerates the certificate; re-trust it on every client.
  • Plan rotation before expiry—an expired application instance certificate fails the handshake on both ends.

Data Persistence & Recovery

By default a server route holds node values in memory only—a broker restart resets them (nodes return to BadWaitingForInitialData until the next publish). Set WITH PERSISTENCE true on OPCUA_SERVER_CONFIG to snapshot served values and restore them on startup.
DEFINE ROUTE PlantServer WITH TYPE OPCUA_SERVER
    ADD OPCUA_SERVER_CONFIG
        WITH SERVER_PORT 4840
        WITH PERSISTENCE true
    ADD MAPPING Nodes
        WITH SOURCE_TOPIC "plant/line1"
        ADD TAG Setpoint
            WITH DATA_TYPE "FLOAT"
            WITH WRITABLE true
        ADD TAG Temperature
            WITH DATA_TYPE "FLOAT"
The same ownership-aware policy as the Modbus TCP Server route applies:
NodeOwnerDefaultOn restart
WRITABLE trueClient-written setpoint lives only in the serverPersistedRestored authoritatively—wins over retained MQTT; returns a good value instead of BadWaitingForInitialData
Read-only / MQTT-fedFed from live MQTTNot persistedRecovered from live MQTT (retained message / next publish)
Client-written setpoints survive a restart; live telemetry always reflects the current MQTT source.

Per-tag and mapping overrides

WITH PERSISTENCE true|false on an individual ADD TAG—or a whole ADD MAPPING—overrides the route default:
DEFINE ROUTE OverrideServer WITH TYPE OPCUA_SERVER
    ADD OPCUA_SERVER_CONFIG
        WITH SERVER_PORT 4840
        WITH PERSISTENCE true
    ADD MAPPING Control
        WITH SOURCE_TOPIC "plant/line1"
        ADD TAG Setpoint
            WITH DATA_TYPE "FLOAT"
            WITH WRITABLE true
        ADD TAG LastGoodTemp
            WITH DATA_TYPE "FLOAT"
            WITH PERSISTENCE "true"
    ADD MAPPING Scratch
        WITH SOURCE_TOPIC "plant/scratch"
        WITH PERSISTENCE "false"
        ADD TAG TestpointA
            WITH DATA_TYPE "FLOAT"
            WITH WRITABLE true
        ADD TAG TestpointB
            WITH DATA_TYPE "INT32"
            WITH WRITABLE true
  • Setpoint — restored authoritatively to the last client-written value.
  • LastGoodTemp — served from snapshot until plant/line1/LastGoodTemp next publishes.
  • Scratch mapping tags — never snapshotted; start cold on every restart.

Caching slow or infrequently-updated sources

MQTT-fed nodes are not persisted by default. When a source updates infrequently—an hourly energy total, a shift-latched value—force-persist with WITH PERSISTENCE "true" so the last received value is re-served on cold start until the next live publish overrides it.
DEFINE ROUTE MeterServer WITH TYPE OPCUA_SERVER
    ADD OPCUA_SERVER_CONFIG
        WITH SERVER_PORT 4840
        WITH PERSISTENCE true
    ADD MAPPING Meters
        WITH SOURCE_TOPIC "plant/energy"
        ADD TAG TotalEnergy
            WITH DATA_TYPE "DOUBLE"
            WITH PERSISTENCE "true"
        ADD TAG DailyPeak
            WITH DATA_TYPE "DOUBLE"
            WITH PERSISTENCE "true"
After a restart, a client browsing TotalEnergy or DailyPeak reads the last published value—a good value rather than BadWaitingForInitialData—until the next publish refreshes it.
Persistence applies only to explicitly-declared ADD TAG nodes. Wildcard/dynamic nodes created from MQTT topic patterns are not snapshotted—a slow source behind a wildcard mapping still starts cold.

Storage & durability

  • Values are written through a non-blocking background worker on every change (rapid updates coalesce to the latest value).
  • The snapshot lives in an encrypted-at-rest database under the broker data directory.
  • A snapshot is keyed to the route’s tag definitions by a fingerprint. If addresses or types change between runs, the stale snapshot is discarded rather than replayed into reshaped nodes.

Verify Your Server

1

Deploy the route

Add the route definition to your broker and confirm route status shows Connected (the endpoint is listening).
2

Connect with an OPC UA client

Use UaExpert or any OPC UA browser. Connect to opc.tcp://<broker-host>:<SERVER_PORT> (append ENDPOINT_PATH if configured). When using security, trust the server certificate—export it from PKI_DIR/own/certs or follow the trust workflow in Certificate Management.
3

Confirm MQTT → node

Publish a test value to a bound topic—for example factory/line1/Temperature. Refresh the client and confirm the node value updates.
4

Confirm node → MQTT

For a writable tag, write a value from the OPC UA client. Subscribe to the tag’s SOURCE_TOPIC with MQTT Explorer or any MQTT client and confirm the retained publish arrives.

Limitations

Each OPCUA_SERVER route binds exclusively to its SERVER_PORT. A second route on the same port fails to start and reports a red route status.

Troubleshooting

  • Port in use — Another process or server route already uses SERVER_PORT. Choose a different port or stop the conflicting service.
  • PKI directory not writable — Ensure the broker can write to PKI_DIR (or the default PKI location).
The application instance certificate regenerates when PKI_DIR is ephemeral—for example, an unmounted Docker volume. Mount PKI_DIR as a persistent volume so client trust stores remain valid across restarts.
  • Client cert not trusted — Move it from PKI_DIR/rejected/certs to PKI_DIR/trusted/certs, or set AUTO_ACCEPT_UNTRUSTED_CERTIFICATES true for testing only
  • Client cert has no application URI SAN — The connecting client must present a certificate with a valid URI in Subject Alternative Name
APPLICATION_URI must match the certificate SAN URI. Leave both at their defaults, or change them together so they stay in sync.
  • Confirm the tag has WITH WRITABLE true.
  • Subscribe to the tag’s SOURCE_TOPIC and check for the retained publish after the write.
  • Verify MIN_VALUE / MAX_VALUE are not clamping the written value away from what you expect.
ALLOW_ANONYMOUS is false. Connect with the configured username and password, or use certificate authentication if enabled.
The SOURCE_TOPIC has not published yet (or no retained value). Publish the topic, enable route persistence for client-written setpoints, or force-persist a slow MQTT-fed source with WITH PERSISTENCE "true". See Data persistence & recovery.

Best Practices

Mount PKI_DIR as a persistent volume so server certificates survive broker restarts. Ephemeral storage regenerates the application instance certificate on every boot and breaks client trust stores. See Certificate Management.
Set ALLOW_ANONYMOUS false on plant networks and require username/password or certificate authentication. Anonymous endpoints are appropriate for lab or isolated development only.
Each server route binds exclusively to its SERVER_PORT. Document which route owns which port to avoid startup conflicts when adding routes.
Prefer GET ENV and GET SECRET over literals in route definitions. Never commit passwords or client credentials to version control.
Align SECURITY_POLICY and MESSAGE_SECURITY with what your SCADA or HMI supports. If the client cannot negotiate encryption, enable ALLOW_NO_SECURITY for testing only—not in production.
Set WITH PERSISTENCE true on OPCUA_SERVER_CONFIG so client-written values survive a broker restart instead of returning BadWaitingForInitialData. See Data persistence & recovery.

Next Steps

OPC UA Client Route

Connect to remote OPC UA servers and poll device data into MQTT.

Industrial Routes Overview

Compare protocols and choose the right industrial route for your architecture.
Last modified on July 3, 2026