OPC UA Server

Expose device data as OPC UA tags that PLCs, SCADA systems, and other industrial clients can read and write.

OPC UA (Open Platform Communications Unified Architecture) is an industrial communication protocol for exchanging data between devices, PLCs, and SCADA systems. The OPC UA Server is an edge container service that publishes tags on the device so industrial clients can read inference results and write values back.

You define the tags, folders, and server settings from the Deployment Manager Configuration tab, then point your OPC UA client at the device.

OPC UA Server is available exclusively for Enterprise customers. Contact the Roboflow sales team to learn more.

Connection Details

Replace <device-ip> with the IP address shown on the device page in Deployment Manager.

PurposeAddress
OPC UA endpointopc.tcp://:4840/opcua/server
Web UI and REST APIhttp://:8092
PortProtocolPurpose
4840OPC UA BinaryIndustrial client connections (PLCs, SCADA)
8092HTTPWeb UI, REST API, documentation

Configuration Source

The "Configuration Source" toggle at the top of the Configure modal controls where the tag and folder configuration comes from. Only one source is ever active on the device at a time.

SourceStored inUse it when
"Web UI"OPCUA_CONFIGYou want to define tags, folders, and server settings inline in the modal. This is the default.
"File"OPCUA_CONFIG_FILEThe configuration is too large for an environment variable, such as a deployment with thousands of tags. Point the server at a path on the device (ex: /data/opcua-config.json).

In "File" mode the server validates at startup that the file exists, is readable, and is not empty, and fails to start with an explicit log entry if any of that is untrue. OPCUA_CONFIG_FILE takes precedence over OPCUA_CONFIG.

Switching sources keeps both the inline values and the file path while the modal is open. On save, the source you did not pick is cleared, so exactly one of the two variables is set. In "File" mode the service card shows the configured file path in place of the inline tag list.

Server Settings

SettingDescription
"Server Name"Human-readable name for the server instance, shown to clients browsing the network.
"Namespace URI"Unique URI identifying the server namespace that tags are registered under. Defaults to http://opcua.roboflow.run.
"Max Sessions"How many OPC UA clients may be connected at once. Defaults to 200.

Every client holds one session, including each inference pod writing tags and each SCADA connection. A session belonging to a client that crashed is only released when the server times it out, so a busy site can hold more sessions than it has live clients. Once the cap is reached, further clients are refused with BadTooManySessions and their tags stop updating. Raise the value if a site runs more concurrent clients than the default. Setting it in the modal writes it into the server config, and OPCUA_MAX_SESSIONS can override it per device for hand-managed deployments.

Folders and Tags

Folders organize tags into a hierarchy. Each folder has a name and an optional description, and appears as a node in the OPC UA address space so clients can browse and discover tags. A tag can belong to a folder or sit at the root level.

Tags define the data points the server exposes. Each tag has these fields:

FieldDescription
"Display Name"Human-readable name shown to OPC UA clients.
"Browse Name"Programmatic identifier used in the address space. Generated from the display name.
"Data Type"The type of value the tag holds.
"Access Level"Whether the tag is ReadWrite or ReadOnly.
"Folder"Which folder the tag belongs to, or Root if unassigned.
"Initial Value"Starting value when the server starts. Validated against the data type.
"Description"Optional label describing what the tag is for.

Data Types

TypeDescriptionExample
BooleanTrue or falsetrue
Int3232-bit signed integer42
Float32-bit floating point3.14
Double64-bit floating point3.14159265359
StringUTF-8 textRunning
DateTimeISO 8601 timestamp2024-01-15T10:30:00Z

These are the types the Configure modal offers. A hand-written configuration file may also use Int16, UInt16, and UInt32, which the server accepts.

Access Levels

ReadWrite tags can be read and written by OPC UA clients as well as the REST API, CLI, and web UI. Use them for control outputs, setpoints, and user-adjustable values.

ReadOnly tags reject OPC UA client writes with BadNotWritable. The REST API, CLI, and web UI can still update the value regardless of access level, so use ReadOnly for sensor values, calculated outputs, and system status that only the device should produce.

Optional Constraints

Numeric types support "Min Value" and "Max Value" to restrict the allowed range, plus "Engineering Units" for a descriptive unit label (ex: °C, PSI, RPM). String types support "Max Length" to cap the character count.

Authentication

The server allows anonymous connections by default, using SecurityPolicy None. Turn on "Require authentication" in the Configure modal to require a username and password from every client.

When the toggle is on you must supply both a username and a password. The password is hashed with bcrypt before it is stored, so the original is never saved and cannot be retrieved later. To change the password on an existing server, enter a new one in the Configure modal, or leave the field blank to keep the current password. Turning the toggle off restores anonymous access.

The credentials are stored as two environment variables, which must be set together or both left unset:

VariableDescription
OPCUA_USERNAMEPlain-text username for client authentication.
OPCUA_PASSWORD_HASHBcrypt hash of the password at salt rounds 10, starting with $2b$10$.

Set credentials through the Configure modal rather than by editing the environment variables. The modal generates the bcrypt hash for you, and setting OPCUA_PASSWORD_HASH by hand requires producing a valid hash externally.

Tag Reporting

The server can report its tags to Roboflow on a schedule, so you can see what it serves without a site visit. Set this in the "Tag Reporting" section of the Configure modal.

SettingDescription
"Send periodic tag reports"Turns reporting on or off. On by default.
"Include tag values"Sends the current value of each tag. Turn it off to send tag counts only. On by default.
"Reporting Interval"Seconds between reports. Defaults to 60.

The settings are stored as environment variables, which the server reads at startup:

VariableDefaultDescription
OPCUA_SNAPSHOT_INTERVAL_SECONDS60Seconds between reports. 0 turns reporting off.
OPCUA_SNAPSHOT_INCLUDE_VALUEStruefalse sends tag counts only.
OPCUA_SNAPSHOT_TTL_SECONDSderivedHow long a report stays current. The modal derives this from the interval and rewrites it every time you save.

A value the server does not accept turns reporting off rather than falling back to the default, so a typo stops reports. The device page shows such a device as not reporting.

If any of these variables is set by reference ({$ref}), the modal cannot edit reporting and leaves it as it is on save. Replace the reference with a literal value to manage reporting from the form.

"Include tag values" governs the periodic report only. The server still writes tag value changes to its container log, which Roboflow collects separately.

Monitoring

The device page shows a live OPC UA status card with the connected client count, sessions in use against "Max Sessions", and the latest tag report. A device on an image older than tag reporting says so in place of the report.

When the server hits the session cap and refuses new clients, the card shows a banner with the session count, the cap, and how many clients were refused. To get an email about it, add an "OPC UA Refusing Clients" alert from the device's "Device Alerts" tab and set how many minutes of refusals to allow first. See Set up Device Alerts.

Web Interface

The server hosts a web interface at http://<device-ip>:8092.

PagePathContents
Dashboard/All configured tags with current values, refreshed every 2 seconds. Any tag's value can be edited through an edit dialog, regardless of access level.
Documentation/docs.htmlServer guide, configuration format reference, and troubleshooting notes.
API Explorer/api/docsInteractive Swagger UI for testing endpoints and viewing request and response schemas.

HTTP API

The REST API gives programmatic access to tag values and server information. All endpoints return JSON. The base URL is http://<device-ip>:8092/api. See Services for the rules shared by all on-device service APIs.

The HTTP API is unauthenticated. OPCUA_USERNAME and OPCUA_PASSWORD_HASH protect the OPC UA binary endpoint on port 4840, not port 8092. Anything that can reach port 8092 can write any tag.

Tag structure is immutable at runtime. Tags and folders come from the OPCUA_CONFIG environment variable, or the file named by OPCUA_CONFIG_FILE for large configurations, and cannot be created, changed, or deleted through this API. Only values can be written.

Read and Write Tag Values

accessLevel governs the OPC UA protocol, not this API. A tag marked ReadOnly rejects writes from OPC UA clients with BadNotWritable but remains writable here, which is how a pipeline publishes a sensor value that industrial clients should only consume.

curl http://<device-ip>:8092/api/tags

curl -X PUT http://<device-ip>:8092/api/tags/tag_temperature/value \
  -H "Content-Type: application/json" \
  -d '{"value": 25.5}'

List all tags

gethttp://device-ip:8092/api/tags

Returns all configured tags with their current values.

Responses
200List of tags with current valuesapplication/json
tagsobject · TagValue[]Optional
Show properties
idstringRequired

Unique tag identifier

nodeIdstringRequired

OPC UA Node ID

displayNamestringRequired

Human-readable display name

dataTypestring · DataType · enumRequired

OPC UA data type

Possible values:BooleanInt16UInt16Int32UInt32FloatDoubleStringDateTime
accessLevelstring · AccessLevel · enumRequired

Tag access level

Possible values:ReadOnlyReadWrite
currentValueone ofRequired

Current tag value

Show properties
booleanOptional
numberOptional
stringOptional
lastUpdatedstringRequired

Timestamp of last value update

folderstring · nullableOptional

Folder name, when the tag is in a folder

get/tags
GET /tags HTTP/1.1
Host: device-ip:8092
Accept: application/json
Response
{
  "tags": [
    {
      "id": "text",
      "nodeId": "text",
      "displayName": "text",
      "dataType": "Boolean",
      "accessLevel": "ReadOnly",
      "currentValue": true,
      "lastUpdated": "text",
      "folder": "text"
    }
  ]
}

Get a single tag

gethttp://device-ip:8092/api/tags/{id}

Returns a single tag with its current value.

Path parameters
idstringRequired

Tag ID

Responses
200Tag with current valueapplication/json
tagobject · TagValueOptional
Show properties
idstringRequired

Unique tag identifier

nodeIdstringRequired

OPC UA Node ID

displayNamestringRequired

Human-readable display name

dataTypestring · DataType · enumRequired

OPC UA data type

Possible values:BooleanInt16UInt16Int32UInt32FloatDoubleStringDateTime
accessLevelstring · AccessLevel · enumRequired

Tag access level

Possible values:ReadOnlyReadWrite
currentValueone ofRequired

Current tag value

Show properties
booleanOptional
numberOptional
stringOptional
lastUpdatedstringRequired

Timestamp of last value update

folderstring · nullableOptional

Folder name, when the tag is in a folder

404Tag not foundapplication/json
errorstringRequired

Error message

get/tags/{id}
GET /tags/{id} HTTP/1.1
Host: device-ip:8092
Accept: application/json
Response
{
  "tag": {
    "id": "text",
    "nodeId": "text",
    "displayName": "text",
    "dataType": "Boolean",
    "accessLevel": "ReadOnly",
    "currentValue": true,
    "lastUpdated": "text",
    "folder": "text"
  }
}

Update tag value

puthttp://device-ip:8092/api/tags/{id}/value

Updates the value of a tag. Tags with accessLevel: "ReadOnly" reject writes over the OPC UA protocol but remain writable through this endpoint.

Path parameters
idstringRequired

Tag ID

Bodyapplication/json
valueone ofRequired

New value to set

Show properties
booleanOptional
numberOptional
stringOptional
Responses
200Updated tag with new valueapplication/json
tagobject · TagValueOptional
Show properties
idstringRequired

Unique tag identifier

nodeIdstringRequired

OPC UA Node ID

displayNamestringRequired

Human-readable display name

dataTypestring · DataType · enumRequired

OPC UA data type

Possible values:BooleanInt16UInt16Int32UInt32FloatDoubleStringDateTime
accessLevelstring · AccessLevel · enumRequired

Tag access level

Possible values:ReadOnlyReadWrite
currentValueone ofRequired

Current tag value

Show properties
booleanOptional
numberOptional
stringOptional
lastUpdatedstringRequired

Timestamp of last value update

folderstring · nullableOptional

Folder name, when the tag is in a folder

400Missing value, unknown tag, type mismatch, or constraint violation. Note that an unknown tag returns 400 here, not 404. Use GET /tags/{id}, which returns 404, to tell an unknown tag from a bad value. application/json
errorstringRequired

Error message

put/tags/{id}/value
PUT /tags/{id}/value HTTP/1.1
Host: device-ip:8092
Content-Type: application/json
Accept: application/json

{
  "value": true
}
Response
{
  "tag": {
    "id": "text",
    "nodeId": "text",
    "displayName": "text",
    "dataType": "Boolean",
    "accessLevel": "ReadOnly",
    "currentValue": true,
    "lastUpdated": "text",
    "folder": "text"
  }
}

A write is rejected with 400 when the value is missing, the type does not match the tag's dataType, or it falls outside a configured minValue, maxValue, or maxLength. Writing to a tag that does not exist also returns 400, not 404. GET /tags/{id} does return 404, so use it if you need to distinguish an unknown tag from a bad value.

Configuration Endpoints

Read-only views of the running configuration, useful for discovering tag IDs and their constraints before writing.

Get full configuration

gethttp://device-ip:8092/api/config

Returns the complete server configuration, including server settings, folders, and tag definitions. Read-only.

Responses
200Full configurationapplication/json
versionintegerRequired

Configuration version

serverobject · ServerConfigRequired
Show properties
portintegerRequired

OPC UA binary endpoint port

namespacestringRequired

OPC UA namespace URI

serverNamestringRequired

Server display name

maxSessionsintegerRequired

Maximum concurrent OPC UA sessions. Connections get their own ceiling: the server allows twice this many connections, so a client that reconnects before its old session times out is not refused at the transport layer.

foldersobject · FolderDefinition[]Required
Show properties
idstringRequired

Unique folder identifier

namestringRequired

Folder display name

descriptionstring · nullableOptional

Folder description

tagsobject · TagDefinition[]Required
Show properties
idstringRequired

Unique tag identifier

nodeIdstringRequired

OPC UA Node ID

browseNamestringRequired

OPC UA browse name, the programmatic identifier

displayNamestringRequired

Human-readable display name

descriptionstring · nullableOptional

Tag description

dataTypestring · DataType · enumRequired

OPC UA data type

Possible values:BooleanInt16UInt16Int32UInt32FloatDoubleStringDateTime
folderIdstring · nullableOptional

ID of the parent folder

accessLevelstring · AccessLevel · enumRequired

Tag access level

Possible values:ReadOnlyReadWrite
initialValueone ofRequired

Initial value when the server starts

Show properties
booleanOptional
numberOptional
stringOptional
engineeringUnitsstring · nullableOptional

Engineering units (ex: degC, kPa)

minValuenumber · nullableOptional

Minimum allowed value, numeric types only

maxValuenumber · nullableOptional

Maximum allowed value, numeric types only

maxLengthinteger · nullableOptional

Maximum string length, String type only

get/config
GET /config HTTP/1.1
Host: device-ip:8092
Accept: application/json
Response
{
  "version": 1,
  "server": {
    "port": 1,
    "namespace": "text",
    "serverName": "text",
    "maxSessions": 1
  },
  "folders": [
    {
      "id": "text",
      "name": "text",
      "description": "text"
    }
  ],
  "tags": [
    {
      "id": "text",
      "nodeId": "text",
      "browseName": "text",
      "displayName": "text",
      "description": "text",
      "dataType": "Boolean",
      "folderId": "text",
      "accessLevel": "ReadOnly",
      "initialValue": true,
      "engineeringUnits": "text",
      "minValue": 1,
      "maxValue": 1,
      "maxLength": 1
    }
  ]
}

Get all folders

gethttp://device-ip:8092/api/config/folders

Returns all folder definitions.

Responses
200List of foldersapplication/json
foldersobject · FolderDefinition[]Optional
Show properties
idstringRequired

Unique folder identifier

namestringRequired

Folder display name

descriptionstring · nullableOptional

Folder description

get/config/folders
GET /config/folders HTTP/1.1
Host: device-ip:8092
Accept: application/json
Response
{
  "folders": [
    {
      "id": "text",
      "name": "text",
      "description": "text"
    }
  ]
}

Sessions and Health

Once maxSessions is reached, further clients are refused with BadTooManySessions, so compare sessionCount against maxSessions for headroom.

A 200 from /subscriptions with a zeroed session list and maxSessions omitted means the OPC UA server is not running. Treat it as an outage rather than a warm-up: if the endpoint answers at all, startup has already finished or failed. Confirm with /health, which reports status: "unhealthy" and opcuaServer: "stopped".

maxSessions is omitted rather than zeroed in that case so a consumer computing maxSessions - sessionCount does not read an unknown cap as a full one.

List active sessions and subscriptions

gethttp://device-ip:8092/api/subscriptions

Returns the connected OPC UA clients and the tags each one is monitoring. Compare sessionCount against maxSessions for headroom before clients start being refused with BadTooManySessions.

A 200 with a zeroed session list and maxSessions omitted means the OPC UA server is not running. Treat it as an outage rather than a warm-up and confirm with /health. maxSessions is omitted rather than zeroed so a consumer computing maxSessions - sessionCount does not read an unknown cap as a full one.

Responses
200Sessions and their subscriptionsapplication/json
sessionCountintegerRequired

Number of active sessions

maxSessionsintegerOptional

Configured maximum concurrent sessions, for headroom against sessionCount. Omitted when the OPC UA server is not running.

subscriptionCountintegerRequired

Total number of subscriptions across all sessions

sessionsobject · Session[]Required
Show properties
sessionIdstringOptional
sessionNamestringOptional
clientDescriptionstringOptional
connectedAtstringOptional
lastContactTimestringOptional
subscriptionCountintegerOptional
subscriptionsobject · Subscription[]Optional
Show properties
subscriptionIdintegerOptional
publishingIntervalnumberOptional
monitoredItemCountintegerOptional
monitoredItemsobject · MonitoredItem[]Optional
get/subscriptions
GET /subscriptions HTTP/1.1
Host: device-ip:8092
Accept: application/json
Response
{
  "sessionCount": 1,
  "maxSessions": 1,
  "subscriptionCount": 1,
  "sessions": [
    {
      "sessionId": "text",
      "sessionName": "text",
      "clientDescription": "text",
      "connectedAt": "text",
      "lastContactTime": "text",
      "subscriptionCount": 1,
      "subscriptions": [
        {
          "subscriptionId": 1,
          "publishingInterval": 1,
          "monitoredItemCount": 1,
          "monitoredItems": [
            {
              "nodeId": "text",
              "displayName": "text",
              "samplingInterval": 1
            }
          ]
        }
      ]
    }
  ]
}

/subscriptions is missing from the spec the device serves at /api/openapi.json. The route is live regardless, and it is documented here.

/health returns 200 whether or not the OPC UA server is running. Check status and opcuaServer rather than the status code.

Health check

gethttp://device-ip:8092/api/health

Returns the health status of the OPC UA server and API.

Responses
200Health status. Returns 200 even when the OPC UA server is stopped, with `status` set to `unhealthy`.application/json
statusstring · enumRequired

Overall server health status

Possible values:healthyunhealthy
opcuaServerstring · enumRequired

OPC UA server status

Possible values:runningstopped
uptimeintegerRequired

Server uptime in seconds

tagCountintegerRequired

Number of configured tags

errorstringOptional

Why the OPC UA server is not running. Present only when opcuaServer is "stopped".

get/health
GET /health HTTP/1.1
Host: device-ip:8092
Accept: application/json
Response
{
  "status": "healthy",
  "opcuaServer": "running",
  "uptime": 3600,
  "tagCount": 5
}

CLI

The opcua-cli tool gives terminal access to the server for debugging, scripting, and quick tag operations when the web UI is not reachable.

docker exec -it opcua-server opcua-cli
docker exec opcua-server opcua-cli <command>
CommandDescription
list, lsList all tags with current values
read Read a specific tag value
write Write a value to a tag
statusShow server health and uptime
clientsList connected OPC UA clients
clients --detailedList clients with subscription details
exportExport the configuration as JSON
docker exec opcua-server opcua-cli read Temperature
docker exec opcua-server opcua-cli write Temperature 25.5
docker exec opcua-server opcua-cli clients --detailed

Logging

Set LOG_LEVEL on the service to control verbosity. See Update Device Configuration for where to edit service environment variables, and View Device Logs for reading the output.

LevelDescription
DEBUGDetailed diagnostic information
INFOGeneral operational messages (default)
WARNPotential issues that do not stop operation
ERRORErrors that affect functionality

Values are case-insensitive, and an unrecognized value falls back to INFO.