Server lifecycle
This page explains what bootstrapServer({...}) does for you behind the scenes, so you can reason about its behaviour across redeploys, message storms, and process exit.
The three properties a Function-node server needs
A reliable OPC UA server in Node-RED satisfies three properties:
- Created once. Exactly one underlying
OPCUAServerinstance per logical server, no matter how many messages flow through the boot Function. - Updated live. Setting a new value changes what clients read without restarting the server, dropping sessions, or rebinding the port.
- Shut down cleanly. Stopping the flow, redeploying, or stopping the Node-RED process releases the port and any subscribed clients.
The pattern shown in Creating an OPC UA server — a single await bootstrapServer({...}) call from global.get("sterfive").bootstrap — gives you all three out of the box. The helper owns the registry, the config-hash–driven adoption, and the process-level shutdown hooks.
How bootstrapServer keeps state across messages
A Function-node body is not a one-shot script — it runs on every input message, and any const/let declared inside it is re-created each time. bootstrapServer keeps the long-lived state outside the body, in a per-process registry keyed by an ownerKey (default "default").
On each call:
- The config is canonicalised (functions, live cert-manager instances, and
forceRebuildare excluded) and hashed. - Adoption — if the hash matches an entry whose handle is still running, the existing handle is returned.
onPopulateis not invoked. No port fight, no rebuild. - Rebuild — if the hash differs (or
forceRebuild: true), the previous server is shut down before the new one binds. - First boot — a fresh server is initialised,
onPopulate(addressSpace, exposed)is awaited, then the server is started. If anything throws, the partially-built server is cleaned up so Node-RED's next tick doesn't crash on a half-initialised state.
Process-level disposal is registered once on first call: SIGINT, SIGTERM, and exit all trigger a graceful shutdown of every registered server.
Why you don't need node.on("close", ...) for basic flows
With bootstrapServer:
- Process exit is covered by the helper's
SIGINT/SIGTERM/exithooks. - Full / modified-flow deploys still re-run the boot Function. Because the config hash matches, the call adopts the existing handle and returns immediately — no teardown is needed unless the config genuinely changed.
- Genuine config change on redeploy triggers an automatic teardown-then-rebuild inside
bootstrapServer.
You only need node.on("close", ...) if you want to drive an explicit shutdown from the flow — see Stopping the server explicitly.
Live updates: setValueFromSource
setValueFromSource(variant) writes a new DataValue into the variable's address-space slot. The next read or monitored-item sample sees the new value. No restart, no session drop, no client reconnect. This is the only API you need for "expose a sensor reading" use cases.
If you have an external system that polls aggressively, set minimumSamplingInterval on the variable so the OPC UA layer rate-limits sampling.
Construction-time vs. live-update options
Some options can only be set when the server is built; others can be changed at runtime.
bootstrapServer config field | Identity-hashed (change ⇒ rebuild) | Live-mutable on a running server |
|---|---|---|
port, endpoint, applicationName, productUri | ✓ | ✗ |
nodesets | ✓ | ✗ |
securityPolicies, securityModes, allowAnonymous | ✓ | ✗ |
users | ✓ | ✗ |
onPopulate | excluded from hash | runs only on rebuild |
forceRebuild | excluded from hash | per-call control flag |
| Variable values | n/a | ✓ via setValueFromSource |
| Address-space additions after populate | n/a | ✓ via namespace.addVariable etc. |
To change a hashed option, simply update the config in the boot Function and redeploy the tab — the helper rebuilds for you. For an explicit programmatic teardown, see Stopping the server explicitly.
What the palette manages on your behalf
A few pieces of state are deliberately kept outside the Function-node API so that the runtime stays consistent across all OPC UA nodes:
- The underlying
OPCUAServer— onlybootstrapServeris exposed. This keeps the registry and adoption logic authoritative. - The certificate store (
OPCUACertificateManager) — the palette wires a single shared instance for every OPC UA client and server node, so the trust store is consistent across the whole runtime. See Security policies and certificates. - The user manager — authentication is configured declaratively through the
users:array. See User authentication.
Summary checklist
When reviewing your own flow:
- The boot Function uses
await bootstrap.bootstrapServer({...})fromglobal.get("sterfive"). - Long-lived references (UA variables, the handle) are stored in
flow.context(), not in localconst/let. - Variable updates use
setValueFromSource. - Construction-time options (port, nodesets, security, users) live in the boot Function only — never in Update nodes.
- If you want explicit teardown from a flow, the Stop Function calls
handle.shutdown(timeoutMs)and clears the cached handle. Otherwise rely on process-exit hooks.
If all five boxes are checked, your flow satisfies the three properties at the top of this page.