TL;DR — OPC UA Part 17: Alias Names
Full spec: reference.opcfoundation.org/Core/Part17/v105/docs · Version 1.05.07 · Published 2026-04-15
NodeOPCUA Implementation: Part 17 support is landing in an upcoming release of NodeOPCUA — server-side
Aliases/TagVariables/Topicsfolders,FindAliasandFindAliasVerbose, and the client-side resolve-and-failover loop described below. This page is the conceptual companion to that release.Companion reading: Part 12 — Global Discovery Server (the "DNS + CA" that hosts the system-wide alias lookup) · ISA-5.1 tag naming vs. explicit OPC UA modelling (why aliases dissolve the naming dilemma).
What Is It?
An AliasName is DNS for your OPC UA address space. Instead of hard-coding "the temperature is ns=3;s=PLC1.DB7.Real42 on server opc.tcp://10.0.0.14:4840", you ask the system "where is TI101?" and it hands back one or more ExpandedNodeIds (NodeId + Server) — resolved at runtime, from whichever server actually holds the data today.
flowchart LR
C["🏢 Client<br/>knows only the tag 'TI101'"]
L["🔎 FindAlias('TI101')<br/>on a local / aggregating / GDS endpoint"]
R["ExpandedNodeId list<br/>ns=3;s=PLC1.DB7.Real42 @ ServerB<br/>(+ redundant copy @ ServerC)"]
N["🌡️ The actual Variable<br/>on ServerB"]
C --> L --> R --> N
style C fill:#1d3557,color:#fff
style L fill:#2d6a4f,color:#fff
style R fill:#6a040f,color:#fff
style N fill:#333,color:#fff
The analogy is the spec's own: "analogous to the way domain names are used as an alias to IP addresses… Like a DNS Server, an OPC UA Server that supports AliasNames provides a lookup Method that will translate an AliasName to a NodeId."
The Problem It Solves
In a small system a client is configured with the exact (ServerEndpoint, NodeId) of every point it needs. That coupling breaks down the moment the system is large, distributed, or dynamic:
| Pain (spec §4 use cases) | Without aliases | With aliases |
|---|---|---|
| Complex, multi-tool engineering (§4.1) | Every config must know each tag's full address, protocol, security | Engineer against agreed names (e.g. ISA tag TI101); resolve addresses later |
| Automatic reconfiguration (§4.2) | Config moves between servers → every client re-configured | Alias re-points; clients just re-FindAlias |
| Cloud / elastic servers (§4.3) | Servers spin up or split by load → addresses churn | Name is stable; NodeId+Server resolved on demand |
| Tiny devices (§4.4) | A simple device cannot host a lookup service | An aggregating server or GDS provides names on its behalf |
| Device replacement (§4.5) | New device = new NodeIds = re-map everywhere | Add the same alias on the replacement; clients unaffected |
The GDS already tells you which servers exist and how to connect (Part 12) — but historically not which tags live inside them. Part 17 fills exactly that gap.
The Information Model — Just Five Things
flowchart TD
OBJ["Objects Folder"] --> A["📁 Aliases (well-known)<br/>AliasNameCategoryType + LastChange"]
A --> TV["📁 TagVariables (well-known)<br/>aliases → Variables only"]
A --> TP["📁 Topics (well-known)<br/>aliases → PublishedDataSets only"]
TV --> AL["🏷️ AliasNameType 'TI101'<br/>BrowseName IS the alias"]
AL -->|AliasFor| VAR["🌡️ Variable ns=3;s=..."]
AL -.->|AliasFor redundant| VAR2["🌡️ Variable on redundant server"]
style A fill:#2d6a4f,color:#fff
style TV fill:#1d3557,color:#fff
style TP fill:#1d3557,color:#fff
style AL fill:#6a040f,color:#fff
AliasNameType— an Object whose BrowseName is the alias. No properties, no value; the name carries the meaning. It has at least oneAliasForreference to the real Node(s). Rule: the BrowseName is immutable — to "rename" you delete and re-add, which mints a new NodeId, so aggregators can detect the change.AliasNameCategoryType— aFolderTypesubtype that groups aliases and hosts the lookup Methods. Nestable into hierarchies. OptionalLastChange(VersionTime) property = cache-invalidation signal.AliasForreference — aNonHierarchicalReferencessubtype linking alias → target. Inverse nameHasAlias(the target need not reference back). The target can be any NodeClass, including nodes on another server.- Well-known instances (static NodeIds, so they aggregate cleanly):
Aliases(root, under Objects, whereLastChangeis mandatory),TagVariables(aliases pointing only at Variables),Topics(aliases pointing only atPublishedDataSets — Part 14). AliasNameDataType— what a lookup returns:{ AliasName: QualifiedName, ReferencedNodes: ExpandedNodeId[] }. Always at least one referenced node.
The Core Operation — FindAlias
This is 90% of what clients use. It is a plain OPC UA Call on an AliasNameCategory instance:
FindAlias(
[in] String AliasNameSearchPattern, // wildcards per Part 4 "Like" operator, e.g. "TI1%"
[in] NodeId ReferenceTypeFilter, // AliasFor or a subtype; restricts by ReferenceType
[out] AliasNameDataType[] AliasNodeList // ordered best-match-first
);
Key semantics that clients must handle:
- A search returns a list, and each alias can reference a list of nodes. The same tag may live on several servers (redundancy, aggregation). "Clients should use the first usable entry" — the server orders them by preference (health, load-balancing, off-loading small devices). Fall through to the next on failure.
- Wildcards follow Part 4's
LikeFilterOperator —%,_,[ ]."TI1%"findsTI101,TI102, and so on. - The alias namespace is ignored for comparison — a client compares alias strings, never
(ns, name)pairs. - Resolve the server: when the call went to a GDS or aggregating server, read the target's
ServerUri/ ServerArray to turn theExpandedNodeIdinto a real connection. That is the last hop before a normal browse, read, or subscribe.
FindAliasVerbose — same call, richer answer
An optional extension returning AliasNameVerboseDataType, which adds ServerUris[] (per referenced node) and AliasNameCategoryId — so you do not need a second round-trip to learn which server and which category each hit came from.
Configuration methods (optional facet)
AddAliasesToCategory(names[], targetNodes[], targetServers[], refType)— parallel arrays; a name repeated N times maps to N targets. Targets may be on remote servers, and the server need not verify their existence (Uncertain_ReferenceOutOfServer).DeleteAliasesFromCategory(names[], targetNodes[])— can only delete aliases defined locally. An aggregator cannot delete aliases it merely pulled from an upstream server.
The Four Deployment Topologies
This is where the practical value lives — the same client code (FindAlias → connect → use) works against all four:
flowchart TB
subgraph one["A · Inside one Server"]
direction TB
S1["Server hosts both the Aliases folder<br/>and its own Variables"]
end
subgraph agg["B · Aggregating Server"]
direction LR
AG["Aggregator<br/>aliases point at nodes<br/>in other servers"]
AG --> U1["Server 1"]
AG --> U2["Server 2"]
end
subgraph stand["C · Standalone Alias Server"]
direction LR
ST["Pure lookup box, owns no data<br/>for devices too small to host aliases"]
ST --> D1["Device A"]
ST --> D2["Device B"]
end
subgraph gds["D · GDS — auto-aggregating"]
direction LR
G["GDS at a well-known endpoint<br/>merges aliases from every server<br/>exposing the 'Alias' capability"]
G --> R1["Server X"]
G --> R2["Server Y"]
end
one ~~~ agg ~~~ stand ~~~ gds
style one fill:#1d3557,color:#fff
style agg fill:#1d3557,color:#fff
style stand fill:#1d3557,color:#fff
style gds fill:#2d6a4f,color:#fff
- A · Single server — the server names its own tags. The simplest case.
- B · Aggregating server —
AliasFortargets live in other servers; the aggregator may return the underlying node or a replicated copy in its own address space. - C · Standalone alias server — owns no process data at all; a pure directory for devices that cannot host aliases themselves, or for retro-fitting names onto already-deployed servers.
- D · GDS — the big one. A GDS that supports aliases automatically merges the
TagVariablesandTopicsof every server registering with theAliascapability into one master tree at a well-known endpoint. This is the system-wide DNS: a client points at one address and resolves any tag anywhere.
GDS Behaviour (Annex C) — Why It Is the Payoff
The GDS turns aliases from a per-server convenience into a network-wide service. Required automatic behaviour:
| Event | GDS does | Client impact |
|---|---|---|
| Server registers (C.2) | Merges its aliases into the master list; identical alias names from different servers are combined (multiple ExpandedNodeIds under one name); adds its ServerUri to ServerArray | New tags appear automatically |
| Server unregisters (C.3) | Removes that server's aliases and AliasFor refs; keeps an alias alive if other servers still back it; drops the ServerUri | Stale entries clean themselves up |
| Client ↔ server link fails (C.4) | Client returns to the GDS and requests a fresh AliasNodeList for the tag | Built-in failover: pick the next ExpandedNodeId — no reconfiguration |
Redundancy falls out for free: a sensor wired to two servers publishes TI101 from both → the GDS reports two ExpandedNodeIds → the client fails over between them.
Push or pull? Who initiates?
The single most-asked question. The answer: alias data always moves by PULL; only the trigger can be a push, and even then the push carries no alias content.
flowchart LR
S["🖥️ Alias-capable Server"] -->|"1 · Push: RegisterServer2 (Part 12)<br/>'I exist + Alias capability'"| G["🌐 GDS / Aggregator"]
S -.->|"2 · Signal only: LastChange tick<br/>(subscribe = pull · Annex D multicast = push)"| G
G ==>|"3 · PULL: Browse the Aliases folder<br/>→ read the actual alias nodes"| S
style S fill:#6a040f,color:#fff
style G fill:#2d6a4f,color:#fff
| Trigger | Direction | What crosses the wire | GDS then… |
|---|---|---|---|
| Registration (C.2) | Push — server RegisterServer2 advertises the Alias capability | "I exist and have aliases" | pulls the whole alias tree once |
LastChange subscribe/poll | Pull — GDS holds a session, subscribes to each AliasNameCategory.LastChange | a VersionTime tick | pulls the changed category |
| PubSub multicast (Annex D, provisional) | Push — server Publishes, GDS Subscribes | only {categoryId, LastChange} — no alias content | pulls (browses) the flagged category |
So the model is push-to-notify, pull-to-fetch: the notification only says "category X changed at T, come look"; the GDS always browses to get the actual aliases. Because all aliases sit under the mandatory Aliases folder, that browse is trivial. The PubSub option exists purely for scale — holding a LastChange subscription to thousands of servers means thousands of sessions; a fire-and-forget multicast heartbeat avoids that.
Resync, Collisions and Lifecycle
Resync keys entirely off the persisted LastChange:
- Newer than cache → browse and diff that category. Index 0 of every frame is the root
Aliases, whoseLastChangerolls up all children — an unchanged root means you can skip everything. - Older than cache (server reset or rollback) → discard the entire cached subtree and re-browse from scratch.
- Missed messages self-heal: PubSub is best-effort, so a delta frame can be lost — periodic key frames carry the full state and correct it. Never rely on deltas alone.
- Liveness: no keep-alive for a configurable timeout → the GDS drops all of that server's aliases; a frame with a new
ApplicationUri→ auto-discover a new server.
Collisions are merges, not conflicts — the same name on many servers is the redundancy feature, not an error:
- Same alias on servers B and C → one alias name, multiple
ExpandedNodeIds;FindAliasorders them best-first and the client fails over. Namespace is ignored when comparing alias names. - Categories merge on the matching Name part of the BrowseName. Well-known categories have static NodeIds so they merge cleanly; a locally-namespaced category cannot be proven identical, so the GDS may keep two instances under different namespaces.
- Exact duplicates (same alias, target, and server) are silently ignored.
- Unregister is collision-aware: an alias or category backed by several servers survives until the last backing server unregisters; otherwise only that server's
AliasForreference andServerUriare pruned. - Deletion asymmetry: a GDS or aggregator cannot delete aliases it pulled upstream (
DeleteAliasesFromCategory→Bad_InvalidState); they vanish only when the owning server drops them and the next resync notices.
The spec is deliberately loose about lifecycle: re-read cadence and GDS persistence are implementation choices, not normative.
How often does the GDS re-read? Event-driven, never on a fixed clock. Re-reads are triggered by a server (re-)registering (registration is itself periodic under Part 12, so it doubles as a heartbeat), a LastChange tick if subscribed, a PubSub frame (Annex D), or an optional periodic full reconciliation as a backstop. Each trigger causes a targeted browse of the flagged category — not a blind full re-pull.
Server up and down:
- Clean shutdown → unregister → prune (the alias survives if other servers still back it).
- Crash, no goodbye → detected via registration lapse (Part 12) or keep-alive timeout (Annex D). Until then
FindAliasmay hand out the dead node — client-side C.4 failover absorbs it. Stale entries are tolerated by design. - Recovery → re-register → re-pull; the persisted
LastChangedecides incremental diff versus full flush.
GDS persistence — split aliases in two. Aliases pulled from upstream are a cache: the source of truth is each owning server, so on restart the GDS re-registers everyone and re-pulls; persistence is only an optimisation. Aliases owned locally (created via AddAliasesToCategory) make the GDS the source of truth, so it must persist those. While the GDS is down only resolution stops — owning servers are unaffected. The real mitigation for the single point of failure is a redundant GDS, not disk.
Does the GDS need a live subscription to every server? No — and that is the point. Serving a FindAlias needs no connection to the target at all: it returns ExpandedNodeId + ServerUri from cache, and the client connects.
| Model | Standing GDS↔server connection | Fit |
|---|---|---|
| PubSub multicast (Annex D) | None — connectionless notify; transient browse only on change | Thousands of servers; best-effort, key-frame self-heal |
LastChange subscription | One session per server | Reliable; does not scale to thousands |
| Poll on re-registration | Transient — connect, browse, drop | Coarse freshness, zero standing sessions |
Keeping Aggregators Fresh — LastChange and the PubSub Extension
An aggregator or GDS must know when an upstream server's aliases changed. Two mechanisms:
- Polling
LastChange— eachAliasNameCategory(mandatorily the rootAliases) carries aLastChangeVersionTime. Subscribe to it; if the value you read is older than your cache, flush the whole cached subtree and re-browse. It works, but one session per upstream server does not scale to thousands. - PubSub multicast notification (Annex D, provisional) — an opt-in extension where each alias-capable server becomes a tiny Publisher on a configurable multicast address, with a fixed
DataSetClassId(65880051-7e5b-4a96-ae47-e0ef4704b924). It emits key frames (the full array of categoryLastChangetimestamps, index 0 always the rootAliases), delta frames (only changed categories) and keep-alives (whose absence auto-removes the server, and whose newApplicationUriauto-discovers a new one). Because delivery is not guaranteed, aggregators must not rely on delta frames alone. Annex D is explicitly marked as having limited implementation and may see breaking changes.
Security and Access Control — Anonymous or Protected?
Part 17 defines no alias-specific security model. Aliases are ordinary Nodes and Methods, so they inherit the full OPC UA security stack — and whether they are reachable anonymously is a server configuration choice, not something the spec fixes. The spec neither mandates protection nor forbids open access; it does anticipate access control by giving every alias Method a Bad_UserAccessDenied result code. See also Part 18 — Role-Based Security.
Three independent layers decide who gets what:
| Layer | Governs | Can it be anonymous / open? |
|---|---|---|
| Session / transport | Endpoint SecurityPolicy + MessageSecurityMode (None/Sign/SignAndEncrypt) + user token (Anonymous / UserName / Certificate) | Yes — a server may expose an anonymous SecurityMode=None endpoint that answers FindAlias, or require an authenticated, encrypted session |
| Authorization | RolePermissions / UserRolePermissions / AccessRestrictions on the alias Nodes (Part 3) | The server's choice — read (FindAlias) and write (Add/Delete) are gated independently |
| The target node | The target server's own security, applied when you actually Read or Subscribe | Independent — resolving an alias yields only a NodeId + ServerUri, never a value |
The critical point: an alias lookup returns an address, not data. Exposing FindAlias broadly does not expose the values behind it — the target node stays protected by its own server's security. This is exactly why treating FindAlias like public DNS is often reasonable.
Practical stance:
FindAlias/FindAliasVerbose(read) — often safe to expose broadly, even anonymously, since it leaks only addresses. But tag names themselves can be reconnaissance-sensitive — decide per deployment.AddAliasesToCategory/DeleteAliasesFromCategory(write) — restrict to engineering roles; these mutate the directory.- GDS endpoints — secured per Part 12; the GDS authenticates to registered servers and to clients at its well-known endpoint.
- PubSub notification (Annex D) —
SecurityModeis configurable (None / Sign / SignAndEncrypt) with aSecurityGroupIdand an SKS (Part 14); a signing-only header layout exists precisely for integrity without encryption on the multicast channel.
⚠️ Because nothing in Part 17 mandates protection, an implementer can leave aliases wide open or lock them down. Treat alias exposure as a deliberate security decision, not a default.
Practical Client Recipe
The whole point is that this loop is address-independent:
1. Decide your lookup endpoint (config): local server | aggregating server | GDS.
2. Call FindAlias("TI101", AliasFor) -> AliasNameDataType[].
3. Take the first entry; take its first ReferencedNode (ExpandedNodeId).
4. Resolve its ServerUri via the ServerArray -> real endpoint.
5. Connect (reuse the session if it is the same server) -> Browse/Read/Subscribe as normal.
6. On failure: fall through to the next ReferencedNode / next alias entry,
or re-call FindAlias on the GDS for a fresh list (Annex C.4 failover).
Gotchas worth internalising:
- Never assume one result. Both the alias list and each alias's node list can have more than one entry. Design for it up front.
- Aliases are immutable strings. A "rename" is delete-then-add and yields a new NodeId for the alias node — do not cache alias NodeIds as identity; cache the name.
TagVariablesvsTopicsare typed: TagVariables resolve to Variables (client/server read and subscribe), Topics resolve toPublishedDataSets (PubSub configuration). Pick the right category for what you are wiring.- Ignore the alias's namespace when matching names.
LastChangeis your cache key. Older-than-cached means purge and re-browse.- Deletion is local-only. An aggregator or GDS cannot delete upstream-owned aliases.
Where Part 17 Sits
| It gives you | It does not give you |
|---|---|
A name → NodeId (+Server) lookup (FindAlias) | Any change to how you read or subscribe once resolved — it is normal OPC UA after that |
A standard place (Aliases / TagVariables / Topics) to publish names | A mandated nomenclature — ISA TI101 is just an example; naming is your (or a companion spec's) choice |
| System-wide resolution via GDS aggregation | A replacement for GDS discovery and certificates (Part 12) — it complements them |
| Redundancy and failover as a natural consequence | Guaranteed delivery of change events (the PubSub path is best-effort) |
Bottom line: Part 17 decouples what a client wants ("give me TI101") from where it currently lives. In a static two-node setup it is overkill; in a large, redundant, cloud-elastic, or multi-vendor plant it is the difference between reconfiguring every client when anything moves — and not.
Official References
Specification (OPC Foundation online reference, v1.05):
- Part 17 — Alias Names: reference.opcfoundation.org/Core/Part17/v105/docs
- Part 12 — Discovery and Global Services (GDS): reference.opcfoundation.org/GDS/v105/docs
- Part 4 — Services (
Call,LikeFilterOperator): reference.opcfoundation.org/Core/Part4/v105/docs - Part 14 — PubSub (
PublishedDataSet, DataSet frames): reference.opcfoundation.org/Core/Part14/v105/docs - Part 3 — Address Space Model: reference.opcfoundation.org/Core/Part3/v105/docs
Conformance units (profiles.opcfoundation.org):
AliasName FindAliasVerbose: profiles.opcfoundation.org/conformanceunit/5869AliasName Configuration Support: profiles.opcfoundation.org/conformanceunit/5874
Nomenclature note: the
TI101example tag follows ANSI/ISA-5.1, Instrumentation Symbols and Identification — Part 17 references it only as an illustration and mandates no naming scheme of its own. For the design argument behind that, read the companion piece: ISA-5.1 tag naming vs. explicit OPC UA modelling.
TL;DR generated from OPC 10000-17 v1.05.07 (2026-04-15). For normative wording, including exact result codes, conformance units, and table definitions, always consult the official specification.