02-environment merges the old 02-env and 03-containers - both answered the
same question, 'where does this run and how do I reach it', and were split
for no reason.
README.md the two hosts, access, ports, secrets, health
checks, known issues
YAU_Linux_Host_Onboarding.md the Linux host's own brief, copied with a
provenance banner: owned by the host owner, not
maintained here, and its 'save this as CLAUDE.md'
instruction does not apply in this repo
openplc-container.md the container as read from the running host
openplc-compose.yml verbatim copy of the live file
MIGRATION.md moving the container - as-built plus runbook
secrets.local.md.template the shape of the git-ignored secrets file
The README leads with the fact that shapes every decision on that machine:
yau-sls-poc-lin001 is a SHARED, LIVE host running ~28 containers for
several projects behind Caddy and Authelia, and this project owns exactly
one of them. Never restart Caddy or Authelia, never publish on 0.0.0.0,
never put growing data on the 62 GB root disk.
Records four known issues rather than leaving them in anyone's head: the
100 ms scan overruns, the 354 MB migration tarballs still sitting on the
host with the JWT secret inside one of them, the runtime image existing in
no registry, and the missing Editor toolchain.
Not carried across: Host_Documentation.md. ~400 lines on ChirpStack,
Forgejo, EQP licensing and the Telegraf fleet - none of it WRPS, owned
elsewhere, and a stale copy here would be worse than a pointer.
71 lines
4 KiB
Markdown
71 lines
4 KiB
Markdown
# The OpenPLC Runtime container
|
||
|
||
The PLC for this demo runs as a **Docker Compose service** on the shared host
|
||
`yau-sls-poc-lin001` (`10.0.0.17`). It is deployed from its **own** compose file,
|
||
`~/openplc-compose.yml` on that host, not from the host's main stack.
|
||
|
||
`openplc-compose.yml` in this folder is a **verbatim copy of the live file**,
|
||
read from the host on 2026-09-02. The host is canonical for what is running; this
|
||
copy exists so the deployment is reviewable and re-creatable from the repo.
|
||
|
||
> [!WARNING]
|
||
> **This container is live control and the demo is running.** CI Server on
|
||
> `yau-poc-cicore1` (`10.0.0.21`) polls it over Modbus TCP. Do not restart,
|
||
> recreate or update it as a side effect of other work.
|
||
|
||
## As-built — verified on the host 2026-09-02
|
||
|
||
| Item | Value |
|
||
|---|---|
|
||
| Container | `openplc-runtime` |
|
||
| Image | `openplc-runtime-migrated:v4.1.10` (`sha256:1e3bd0e1…a502cbc6`) |
|
||
| Compose file | `/home/azureuser/openplc-compose.yml` · project `azureuser` |
|
||
| Published ports | `10.0.0.17:502->502/tcp`, `10.0.0.17:8443->8443/tcp` |
|
||
| Network | `openplc-net` — **isolated**, not the shared stack network |
|
||
| Volume | `azureuser-openplc-runtime-data` (external) |
|
||
| Capabilities | `SYS_NICE`, `SYS_RESOURCE` |
|
||
| Restart policy | `unless-stopped` · logging `json-file` 10 MB × 3 |
|
||
| Loaded program | `libplc_1786668930820554523.so`, built 2026-08-14 |
|
||
|
||
The image is **local to the host** — it was produced by `docker commit` during the
|
||
migration (see `MIGRATION.md`), not pulled from a registry. It cannot be re-pulled;
|
||
if it is deleted, the compiled program is lost and must be rebuilt and re-uploaded
|
||
through the OpenPLC Editor (`03-plc/DEPLOY.md`).
|
||
|
||
## Why each setting matters
|
||
|
||
| Setting | Reason |
|
||
|---|---|
|
||
| `cap_add: SYS_NICE, SYS_RESOURCE` | Real-time scheduling. **Omitting these breaks the runtime** — always include them in a recreate. |
|
||
| `azureuser-openplc-runtime-data:/var/run/runtime` | All runtime state: `restapi.db` (users + program record) and `.env` (JWT secret). Survives recreate; **do not delete the volume**. Declared `external: true` so Compose uses the restored volume instead of creating an empty one. |
|
||
| `10.0.0.17:502:502` | Modbus TCP for CI Server. **Bound to the LAN address, not `0.0.0.0`.** Modbus has no authentication or encryption, and this host has a public IP — the bind address is the security control. Do not widen it. |
|
||
| `10.0.0.17:8443:8443` | REST API — the control channel (upload, start/stop). Reachable from the LAN so the OpenPLC Editor can drive it; JWT auth is the only thing in front of it, so the same bind rule applies. |
|
||
| `networks: openplc-net` | Deliberate isolation from the host's other ~20 containers. The PLC has no reason to reach them, or they it. |
|
||
| image pinned by tag+digest | Stops a recreate from silently changing the runtime under a tested program. |
|
||
|
||
## Firewall
|
||
|
||
**UFW on this host is inactive** — inbound filtering is entirely the **Azure
|
||
Network Security Group**. There is no host firewall rule for 502 or 8443, and
|
||
adding one is not the mechanism here: 502/8443 are bound to `10.0.0.17`, which is
|
||
not reachable from outside the VNet, and the NSG governs the public interface.
|
||
|
||
## If port 502 refuses connections
|
||
|
||
**Two conditions must both hold**, and neither is a container setting:
|
||
|
||
1. a program is **running** on the runtime, and
|
||
2. the Editor project defines a Modbus **Server** so the runtime's `modbus_slave`
|
||
plugin is enabled — see `03-plc/DEPLOY.md` §A3.
|
||
|
||
Check those before touching the container. A refusal looks like a firewall drop
|
||
but is not one: Docker DNATs the packet to the container, which returns RST
|
||
because nothing is bound inside.
|
||
|
||
## Known issue — PLC scan overruns
|
||
|
||
The runtime logs `[task PLC_TASK] scan overrun` warnings: the 100 ms cycle body
|
||
occasionally exceeds its period and the task runs at a reduced rate. **100
|
||
overruns between 2026-08-19 and 2026-08-28** (roughly one per 4–5 days). Other
|
||
tasks are unaffected and the demo continues to run. Not yet investigated — this
|
||
host is shared, so contention with the other containers is a candidate cause.
|