Repository navigation
Server Discovery
Milo 1.2.0-RC1, Java 17. API baseline 69cb25087804.
The thermostat now has a secured endpoint from Server security and an access policy from Access control. Before a client can open a Session there, it has to learn which endpoints the server offers and how each one is secured. This page covers what a Milo server advertises through the discovery services, how to check that list from a client, and what your application must do itself to register with a Local Discovery Server (LDS).
Discovery is a small set of services a client can call before it has a Session. FindServers returns application descriptions and discovery URLs. GetEndpoints returns endpoint descriptions, including security policy, mode, certificate, and user-token choices. Neither service creates an authenticated Session. A client uses the answer to choose an endpoint and then connects to it as usual. It can call both services on an unsecured SecureChannel even when the path offers no None endpoint, which is how it learns about the secured endpoints in the first place.
Every Milo server answers these two services for itself, from the endpoints you configure. An LDS is a separate registry that servers register with, so clients can find several servers through one well-known address. A Milo server is not an LDS registry or a multicast discovery implementation, so FindServersOnNetwork, RegisterServer, and RegisterServer2 return Bad_ServiceUnsupported on the default server even though their message types exist.
Configure advertised hosts and ports as in Server configuration, then start the server. From a client, the SDK's discovery helper fetches the endpoint list for a reachable discoveryUrl. Import org.eclipse.milo.opcua.sdk.client.DiscoveryClient, org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription, java.util.List, and java.util.concurrent.TimeUnit.
List<EndpointDescription> endpoints =
DiscoveryClient.getEndpoints(discoveryUrl).get(10, TimeUnit.SECONDS);The result is a list of endpoint descriptions, not an established connection. The static helper owns its temporary transport and disconnects it when the request completes or fails. Select an endpoint from the list, validate it, and establish a Session as described in Client connecting.
The list depends on the URL you ask with. Default discovery prefers advertised endpoints whose hostname matches the requested URL, and if none match, it falls back to all profile-compatible endpoints. GetEndpoints excludes URLs ending in /discovery, and the static helper filters by the transport profile inferred from the URL scheme. An unexpected URL can therefore be a fallback advertisement, not evidence of successful routing.
FindServers returns this server and any explicitly configured additional application descriptions. To supply them, override ServiceSets.createDiscoveryServiceSet(...) to return new DefaultDiscoveryServiceSet(server, supplier) when constructing the server. See the linked OpcUaServer constructor and discovery service APIs.
At this baseline, the SDK has no ready-to-use conforming LDS registration client, so registration is application work. The server API has no automatic LDS registration or renewal lifecycle. DiscoveryClient.registerServer(RegisteredServer) sends the request asynchronously, but it cannot establish conforming authenticated LDS registration, and there is no high-level RegisterServer2 lifecycle.
The gap is authentication. DiscoveryClient supplies no application certificate identity and uses an insecure peer validator, while OPC UA requires both registration services to use a SecureChannel that authenticates the client. RegisterServer also requires the registered ServerUri to match the channel certificate's application URI. These are protocol requirements, not optional LDS preferences. See RegisterServer and RegisterServer2.
To register, use an independently maintained registration implementation or a separately developed authenticated transport integration, and validate it against the actual LDS, not a fake responder. An application implementing registration needs this sequence.
- Provision and start its actual server endpoints. Build a
RegisteredServerwith stable ServerUri, ProductUri, localized names, Server application type, reachable discovery URLs, any applicable semaphore-file path, andIsOnline=true. - Connect to the configured LDS with its required trust and security policy. Send the registration and check the service result. Do not mark the server registered just because the socket connected.
- For manually launched servers, renew at a configurable interval of at most ten minutes while accepting connections. After a failure, retry after about one second, then back off toward the normal interval, as RegisterServer specifies. Keep one renewal owner, use bounded requests with retry and backoff, and update changed discovery URLs deliberately. Monitor rejection and expired registration separately from local server health.
- During shutdown, stop new renewal tasks, send
IsOnline=falsewith the same ServerUri while the LDS connection is usable, then disconnect and close owned schedulers. Treat an unavailable LDS as a cleanup failure or a logged failed deregistration, according to application policy, and do not block local shutdown forever.
A semaphore file matters only if the LDS can see the same filesystem and the protocol and deployment use it. A local path in another container or machine is not a remote liveness signal. Removing a process or file does not replace a tested renewal and deregistration design.
Advertise destinations clients can route to. An LDS can accept registration for an endpoint that clients still cannot reach, and a server can answer GetEndpoints on its own host while clients elsewhere cannot. Check DNS, NAT mappings, certificate hostnames, firewall rules, and the difference between bind and advertised addresses. Verify discovered URLs from the client's network, not only from the server host.
On the client, the future returned by getEndpoints can fail with transport or service errors, and the bounded wait can time out. Separate these stages when diagnosing failures.
| Observation | Next check |
|---|---|
| Discovery connection fails | Listener, discovery URL and path, DNS, TCP routing, firewall. |
| Empty or unexpected endpoint list | Advertised endpoints, requested profile filters, certificate and provider compatibility. |
| Endpoint discovered but secure connection fails | Certificate trust, SAN and application URI, policy, mode, and provider support. |
| Session activation fails | User token policy, credentials, identity validator. |
| Server runs but disappears from LDS | Renewal results, LDS expiry, registration identity, lifecycle owner. |
A local GetEndpoints check covers Milo discovery and address advertisement. It does not cover an external LDS product, secure registration policy, multicast discovery, or routing across your network. Reverse Connect changes how the connection is established, but these checks still apply.
Clients can now find the server and choose a secured endpoint. The last server topic, Historical access, shows how to answer history requests when your application stores past values.
Related: Server SDK · Client connecting · Reverse Connect · Deployment
User guide for Milo 1.2.0-RC1 at source 69cb25087, 2026-10-12. Home · Getting started · Examples · Troubleshooting · Release notes
- Home
- Start here
- Client guide
- Server guide
- Feature guides
- Reference
- Operations
- Examples
- Release notes
- Contributing