Skip to main content

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:

  1. Created once. Exactly one underlying OPCUAServer instance per logical server, no matter how many messages flow through the boot Function.
  2. Updated live. Setting a new value changes what clients read without restarting the server, dropping sessions, or rebinding the port.
  3. 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:

  1. The config is canonicalised (functions, live cert-manager instances, and forceRebuild are excluded) and hashed.
  2. Adoption — if the hash matches an entry whose handle is still running, the existing handle is returned. onPopulate is not invoked. No port fight, no rebuild.
  3. Rebuild — if the hash differs (or forceRebuild: true), the previous server is shut down before the new one binds.
  4. 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/exit hooks.
  • 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 fieldIdentity-hashed (change ⇒ rebuild)Live-mutable on a running server
port, endpoint, applicationName, productUri
nodesets
securityPolicies, securityModes, allowAnonymous
users
onPopulateexcluded from hashruns only on rebuild
forceRebuildexcluded from hashper-call control flag
Variable valuesn/a✓ via setValueFromSource
Address-space additions after populaten/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 — only bootstrapServer is 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({...}) from global.get("sterfive").
  • Long-lived references (UA variables, the handle) are stored in flow.context(), not in local const/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.