creating-an-opcua-server
Good news, you can already create an OPC UA server using Node-RED, and you don't need a special module for that.
This tutorial shows how to create an OPC UA server directly in a Node-RED Function node using only scripts. It also lets you inject configuration values (port, endpoint, namespace, and a variable value) from other nodes so you can change the server behavior dynamically.
What you will build
- A Node-RED flow that starts an OPC UA server from a Function node.
- A dynamic variable that reads its value from
msg.value. - A simple way to configure the server from an Inject/Change node.
Prerequisites
- Node-RED installed and running.
node-opcuaavailable in the Node-RED runtime.
Install the package in your Node-RED user directory (or wherever your runtime is installed):
npm install node-opcua
Step 1 — Create the flow
Create a basic flow with these nodes:
- Inject (or Change) node to set the configuration
- Function node to start the server
- Debug node to show status
Wire them like this:
Inject/Change → Function → Debug
Step 2 — Configure the Inject or Change node
Set the following properties in the message:
msg.port(example:4840)msg.endpoint(example:my-opcua-server)msg.namespace(example:http://my-opcua-server)msg.value(optional, example:42.5)
If you prefer, you can use a Change node to set these fields, or use a Function node that returns the message.
Step 3 — Add the Function node code
Paste the following code into the Function node:
// Create a node-opcua server using only scripts
// Variables are injected from other nodes (msg.port, msg.endpoint, msg.namespace, etc.)
const opcua = require("node-opcua");
// Check if required variables are provided
if (!msg.port || !msg.endpoint || !msg.namespace) {
node.error("Missing required variables: port, endpoint, or namespace");
return;
}
// Create OPC UA server
const server = new opcua.OPCUAServer({
port: msg.port || 4840, // Default port if not provided
nodeset_filename: [
opcua.nodesets.standard // Load standard nodeset
],
serverInfo: {
applicationUri: `urn:${msg.endpoint}`,
productUri: "MyOPCUAServer",
applicationName: { text: "MyOPCUAServer", locale: "en-US" },
gatewayServerUri: null,
discoveryProfileUri: null,
discoveryUrls: [],
isOnline: true
}
});
// Define a namespace
const namespace = server.engine.addressSpace.getOwnNamespace();
// Add a variable to the server (example: a dynamic variable)
const variable = namespace.addVariable({
organizedBy: "RootFolder",
nodeId: "s=MyDynamicVariable",
browseName: "MyDynamicVariable",
dataType: "Double",
value: {
get: function () {
return new opcua.Variant({ dataType: opcua.DataType.Double, value: msg.value || 0.0 });
}
}
});
// Start the server
server.start(function (err) {
if (err) {
node.error("Server failed to start: " + err.message);
} else {
node.send({ payload: `OPC UA Server running on port ${msg.port}, endpoint: ${msg.endpoint}` });
}
});
// Handle shutdown
node.on("close", function () {
server.shutdown(1000, function () {
node.log("Server shut down");
});
});
return null; // Prevents sending an output message immediately
Step 4 — Deploy and test
- Click Deploy in Node-RED.
- Trigger the Inject node.
- You should see a status message in the Debug panel.
If the server starts correctly, the Function node will emit a message like:
OPC UA Server running on port 4840, endpoint: my-opcua-server
Step 5 — Update the variable dynamically
To update the variable exposed by the server, send a new message with msg.value to the Function node. The variable MyDynamicVariable will return the updated value on read.
Notes and tips
- The server shuts down gracefully when the flow stops or is redeployed.
- You can extend the address space with more variables, objects, or methods using the same
namespace.addVariable()pattern. - For production deployments, consider adding security policies and certificate handling.
Next steps
Would you like help extending this server with:
- Custom objects and methods
- User authentication
- Security policies and certificates
- A structured namespace layout