The companion suite to php-opcua/uanetstandard-test-suite: pre-configured OPC UA servers for the service sets and scenarios the UA-.NETStandard reference implementation does not cover. Same shape (single docker compose up, GitHub Action, GHCR-published images), different ports, explicitly non-overlapping with the main suite.
Today it ships two servers — open62541 with the NodeManagement service set enabled (because UA-.NETStandard does not implement AddNodes / DeleteNodes / AddReferences / DeleteReferences over the wire), and an open62541 build with all RSA security policies + Anonymous and Username/Password authentication (an open62541-backed mirror of uanetstandard-test-suite port 4843, useful when the .NET stack is not available or when you want to validate against a second implementation). The repository is structured around docker-compose.yml so new servers drop in as new services (Prosys Simulation Server, Eclipse Milo, node-opcua, …) without changing the action contract.
Why a separate repo:
uanetstandard-test-suiteis the OPC Foundation reference stack — accurate, thorough, and the right default for 90% of client testing. But every reference implementation has blind spots (NodeManagement is UA-.NETStandard's most visible one), and any CI that wants to cover those corners needs a second server. Keeping the "extras" in their own versioned suite avoids bloating the main one and lets each member of the family move at its own cadence.
| Port | Service (compose name) | Upstream | What it tests |
|---|---|---|---|
| 24840 | open62541-nm |
open62541 v1.4.8 built with UA_ENABLE_NODEMANAGEMENT=ON, runtime ci_server |
AddNodes / DeleteNodes / AddReferences / DeleteReferences over the wire, plus anonymous full-access AccessControl so the tests don't fight permissions |
| 24841 | open62541-all-security |
open62541 v1.4.8 built with UA_ENABLE_ENCRYPTION=OPENSSL, custom example server |
Mirror of uanetstandard-test-suite port 4843 (opcua-all-security): every RSA security policy the build supports, every security mode (None/Sign/SignAndEncrypt), Anonymous + Username/Password authentication. Credentials match the upstream suite (admin/admin123, operator/operator123, viewer/viewer123, test/test). Self-signed RSA-2048 server certificate generated on first boot. Permissive test posture: accepts any client certificate (no trust list check) and allows plain-text passwords over SecurityPolicy#None — mirrors uanetstandard-test-suite port 4845 (opcua-auto-accept) behaviour so test clients work without server-side pre-trust. |
Ports 24840/24841 were picked to sit well clear of the 4840-4849 range uanetstandard-test-suite reserves. Future services will follow the same rule (no overlap with 4840-4849 or between each other).
Every industrial environment is different and the extras you'll want in your CI are different too: Prosys-style address spaces, Siemens S7-over-OPC gateways, PubSub over MQTT brokers, vendor-specific ExtensionObject payloads. Fork this repository and add what you need.
The layout was designed to be extended:
- Drop a new
<service-name>/Dockerfileinto the repo root - Append a
services:entry indocker-compose.ymlusing theimage: ghcr.io/<owner>/extra-test-suite/<service>:${TAG:-latest}+build: context: ./<service-name>dual pattern - If you publish it as a GitHub Action, add a wait step in
action.ymlexposing an output for the new endpoint - Bump the minor version and tag
The publish workflow builds every service in docker-compose.yml on tag push — no per-service changes required. If you build something generally useful, open a PR so the upstream suite can pick it up.
If the single open62541 server covers your NodeManagement testing needs (as it does for php-opcua/opcua-client), jump straight to the Quick Start or the CI Integration section.
docker compose up -dThat's it. The open62541-nm container is running on opc.tcp://localhost:24840 with anonymous full-access AccessControl and all four NodeManagement services enabled, and open62541-all-security is on opc.tcp://localhost:24841 advertising every RSA policy with anonymous and username/password authentication.
# Stop everything
docker compose downrestart: unless-stopped means the container survives host reboots, exactly like uanetstandard-test-suite. You start it once, forget it, and your integration tests assume it's there.
opc.tcp://localhost:24840 # open62541 — NodeManagement
opc.tcp://localhost:24841 # open62541 — all RSA security policies + anonymous/userpass
open62541-nm ships without security. open62541-all-security generates a self-signed RSA-2048 certificate on first boot (DER format, with a subjectAltName URI:urn:open62541.server.application) — the client must trust this certificate to use any Sign or SignAndEncrypt endpoint. The certificate lives in the container at /certs/; mount that path as a volume if you need it to persist across docker compose down cycles.
If you need a certificate-auth variant or a specific cert lifecycle, add a dedicated service in the compose file (see Fork It).
This repository is also a reusable GitHub Action. Add a single step to your workflow and every server in the compose file is pulled from GHCR and started:
steps:
- uses: actions/checkout@v4
- uses: php-opcua/extra-test-suite@v1.0.0
- run: vendor/bin/pest --group=integration # or the equivalent in your stackThe composite action runs docker compose pull && docker compose up -d with a CI-specific override that disables restart: (the runner VM is ephemeral — any restart policy would be moot). Port 24840 is part of this suite's versioned contract: your tests hardcode the endpoint the same way they hardcode the UA-.NETStandard ports 4840-4849.
Pin the version for reproducibility:
- uses: php-opcua/extra-test-suite@v1.0.0
with:
image-tag: v1.0.0 # pull this exact image from GHCR
startup-timeout: '30' # seconds, per-service TCP waitFor a real-world example, see the integration job in php-opcua/opcua-client.
# First time: build from source (no published image yet)
docker compose up -d --build
# After v1.0.0 is published to GHCR — faster, no local compilation
docker compose pull
docker compose up -d
# Endpoint: opc.tcp://localhost:24840
# Probe: php-opcua/opcua-client/scripts/prosys-nodemanagement-smoke.phpStop / restart / inspect:
docker compose down
docker compose restart open62541-nm
docker compose logs -f open62541-nm- Server stack: open62541 v1.4.8, pinned via
ARG OPEN62541_REFin the service Dockerfile, built withUA_ENABLE_NODEMANAGEMENT=ON,UA_ENABLE_SUBSCRIPTIONS=ON,UA_ENABLE_METHODCALLS=ON,UA_ENABLE_HISTORIZING=ON,UA_NAMESPACE_ZERO=FULL - Runtime binary:
ci_server(most permissive of the upstream examples; fallback chain in the entrypoint if upstream renames it) - Base image:
debian:bookworm-slimmulti-stage (build + runtime) - Orchestration: Docker Compose v2
- Registry:
ghcr.io/php-opcua/extra-test-suite/<service>
Semver, applied to the whole repo — all published images share the same tag.
- Major bump: breaking change to the action's inputs/outputs or to the service port map
- Minor bump: a new service is added, or a server's default binary / configuration changes in a user-visible way
- Patch bump: upstream version bump, dependency refresh, internal cleanup
For bug reports, feature requests, or suggestions for new servers to add (Prosys, Milo, node-opcua, …), open an issue on GitHub Issues.
This project was built in part with the assistance of Claude (Anthropic). The AI contributed to code generation, documentation writing, and architecture decisions. All outputs were reviewed and validated by the author.
This project is licensed under the MIT License.