wrps-demo-kit/02-environment/openplc-container.md
Clio Liu 2b8f88d01f feat(env): hosts, access, ports and the PLC container
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.
2026-09-02 17:24:09 +10:00

71 lines
4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 45 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.