- ZB.MOM.WW.Overview appsettings.Docker.json: application "BTDB Manager", instance manager at http://zb-btdb-manager:9877 (HasActiveRole false); docker/README.md fleet row. - CLAUDE.md BTDB row and sister-project-history/btdb.md §7/§9: docker/ layout, the dacpac + SQL CLR side-project build, scadabridge-mssql as the BT host. Claude-Session: https://claude.ai/code/session_015dYqtaTxgf6NK2KuwriMSg
3.9 KiB
docker/ — containerised overview dashboard
Runtime-only image, the HistorianGateway pattern: publish on the host (which has the Gitea feed
credentials), copy the output into mcr.microsoft.com/dotnet/aspnet:10.0. Docker never restores.
# from ZB.MOM.WW.Overview/
dotnet publish src/ZB.MOM.WW.Overview/ZB.MOM.WW.Overview.csproj \
-c Release -o docker/publish -p:UseAppHost=false
cd docker && docker compose up -d --build
open http://localhost:5320
docker/publish/ is build output and is gitignored.
Why the container, and not just dotnet run
The dev rigs publish only some node ports to the host:
| Fleet | Health endpoint | Reachable from the Mac host? |
|---|---|---|
| ScadaBridge central-a/b | container :5000 |
yes — localhost:9001 / :9002 |
| ScadaBridge site-* (6) | container :8084 |
no — not published |
| OtOpcUa central-1/2 | container :9000 |
no — only Traefik :9200, load-balanced across the pair |
| OtOpcUa site-* (4) | container :8080 |
no — not published |
| HistorianGateway | container :5220 |
yes — localhost:5220 |
| BTDB Manager | container :9877 |
yes — localhost:9877 |
| MxAccessGateway | windev :5130 |
yes, with the VPN up |
So a host-run dashboard can only see part of the fleet. This stack joins the three rig networks
(otopcua-dev_default, scadabridge-net, zb-historiangw_default, all declared external) and
addresses every node by container DNS name, which is how it sees all of them. BTDB Manager
(~/Desktop/BTDB/docker) sits on scadabridge-net too, because its BT database lives on
ScadaBridge's infra MSSQL container, so no fourth network is needed for it.
Ports above were established by probing the running rigs on 2026-07-24, not read off the
compose files — the two disagree. In particular OtOpcUa's site nodes serve health on :8080, not
the :9000 their compose anchor implies, because their explicit Kestrel listeners for LocalDb sync
re-bind the primary HTTP port.
Start the rigs before this stack. The networks are external, so bringing the dashboard up or
down never disturbs a running rig.
Registry shape
Bound from the Overview section; appsettings.Docker.json is the worked example.
"Overview": {
"PollIntervalSeconds": 10, // sweep cadence; per-app and per-instance overrides allowed
"TimeoutSeconds": 3, // per-request timeout
"StaleAfterSeconds": 45, // must exceed the poll interval, or every card reads Stale
"Applications": [
{
"Name": "ScadaBridge",
"ManagementLabel": "CentralUI", // label on each instance's management link
"Instances": [
{
"Name": "central-a",
"Group": "central", // one Akka cluster = one group; leader + split-brain
// detection are scoped to it. Omit for standalone apps.
"BaseUrl": "http://host:5000", // /health/ready and /health/active are probed under this
"ManagementUrl": "http://localhost:9001/", // followed by the OPERATOR'S browser, so it
// must be host-reachable, not container DNS
"HasActiveRole": true // false for single-instance gateways: no Active badge,
// and /health/active is never probed
}
]
}
]
}
appsettings.json ships the tuning defaults with an empty Applications list, so a deployment
that supplies no registry fails at startup naming the missing key rather than booting into an empty
dashboard.
Configuration traps
- Never set
ASPNETCORE_URLSalongsideKestrel__Endpoints__*. Declaring both puts Kestrel into explicit-endpoints mode and one of them is silently discarded. - No
env_file, no secrets, no login. The dashboard reads anonymous health endpoints and holds no credentials. That is a requirement of the design, not an omission.