Skip to content

VL3 Edge Setup

The Phoenix Contact VL3 UPC 2440 EDGE is a target platform for the Consystence edge runtime. On it, a single DIN-rail device runs both the deterministic control runtime and the Consystence edge runtime, sitting between the field and the site server. This page describes that target installation: the two-process model, the systemd unit that runs the edge runtime, the local configuration and registration flow, and the local gRPC link to the PLCnext controller.

For the broader edge picture, start at the edge overview. For how a PLCnext acts as the field gateway in the border-gateway topology, see the PLCnext border gateway.

In the target installation, the VL3 hosts two cooperating processes, with a clean separation of concerns:

ProcessRoleRuntime
vPLCnext (Virtual PLCnext Control)Deterministic real-time control — state machines, interlocks, alarms, IO mapping. Speaks EtherNet/IP CIP to the field and exposes tags over a local gRPC server.Phoenix Contact PLCnext runtime
consystence-edgePlatform functions — tag collection, store-and-forward, store buffering, upstream streaming, local diagnostics..NET 10, single-silo Orleans

The control logic stays in the PLCnext runtime where it belongs; consystence-edge never issues control commands directly. It reads and writes tags through the PLCnext over a local socket, buffers them, and streams them upstream.

flowchart TB
  subgraph VL3["VL3 UPC 2440 EDGE"]
    EDGE["consystence-edge<br/>.NET 10 / Orleans<br/>store-and-forward (SQLite)"]
    PLC["vPLCnext<br/>Virtual PLCnext Control<br/>CSY control program (C# eCLR)"]
    EDGE -->|"gRPC over UDS<br/>/run/plcnext/grpc.sock"| PLC
  end
  EDGE -->|"gRPC (HTTP/2 TLS)<br/>store-and-forward"| SS["Site server"]
  PLC -->|"EtherNet/IP CIP<br/>TCP 44818"| FIELD["Field IO / ControlLogix rack"]

The edge runtime is not yet publicly released; Consystence provides it through beta engagements. On a systemd host it runs as consystence-edge.service, under a dedicated unprivileged consystence user with filesystem hardening:

[Unit]
Description=Consystence Edge Runtime
After=network-online.target
Wants=network-online.target
[Service]
Type=notify
ExecStart=/opt/consystence/edge/Consystence.Edge
WorkingDirectory=/opt/consystence/edge
Environment=DOTNET_ENVIRONMENT=Production
Restart=always
RestartSec=10
TimeoutStartSec=30
TimeoutStopSec=30
User=consystence
Group=consystence
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/consystence /var/log/consystence
StateDirectory=consystence
PrivateTmp=true
# Resource limits
LimitNOFILE=65536
MemoryMax=512M
[Install]
WantedBy=multi-user.target

Points worth understanding before you deploy:

  • StateDirectory=consystence — systemd creates and owns /var/lib/consystence and, critically, keeps it across a unit replacement or redeploy. This is where the write-once install identity and the offline-licence state persist. Do not delete it between upgrades. Offline licensing degrades gracefully and never gates control writes — see licensing.
  • ProtectSystem=strict + ReadWritePaths — the root filesystem is read-only to the service; only /var/lib/consystence (state, store-and-forward buffer) and /var/log/consystence (logs) are writable.
  • Local diagnostics API — listens on 127.0.0.1:8080 by default, so only the device itself can reach it (use it over SSH). Its address and port come only from the LocalApi section (LocalApi:Address, LocalApi:Port). ASPNETCORE_URLS does not move it, and the edge refuses to start if its configuration contains a Kestrel endpoints section.
  • Restart=always / RestartSec=10 — the edge self-heals after crashes or power events; store-and-forward replays any buffered data on reconnect.

Install and enable:

Terminal window
sudo systemctl daemon-reload
sudo systemctl enable --now consystence-edge.service
sudo systemctl status consystence-edge.service
journalctl -u consystence-edge.service -f

The target design is a minimal bootstrap: an unconfigured edge carries only enough configuration to find and register with the site server, and everything else — the controller/module/channel/tag model, the PLC driver wiring, alarm definitions — is pushed from the server. Server-side push is a target capability, not yet shipping. Today the edge builds its entire runtime model — PLC drivers, poll groups, device instances, command mappings — from its local appsettings.json, with environment-variable overrides (for example Upstream__ServerUrl to point the edge at its site server).

The keys the runtime actually reads, in appsettings.json form:

{
"Edge": {
"Id": "edge-01",
"Site": "your-site",
"TenantCode": "your-tenant"
},
"Upstream": {
"ServerUrl": "https://site-server.example.internal:5001",
"BufferPath": "/var/lib/consystence/buffer.db",
"BufferMaxBytes": 536870912,
"ForceHttp3": false
},
"Plcs": [
{
"PlcId": "plcnext-001",
"Name": "PLCnext VL3 (local)",
"Driver": "plcnext-grpc",
"Address": "localhost"
}
]
}
KeyMeaning
Edge:IdThe only required setting — the edge’s identity. Bootstrap fails (Edge:Id is required in configuration) if it is missing, and the value is what the edge sends the server as its hardware ID at registration. The target design is an activation flow that enrols the device by its write-once install identity, with Edge:Id remaining a display label — not yet shipped. (The licensing identity is that separate write-once install ID, which the edge mints at first boot; the account-signed licence is bound to it. See licensing.)
Edge:Site, Edge:TenantCodeThe site code and tenant slug the edge reports itself under.
Upstream:ServerUrlThe site server’s edge listener, the one port on which the site server serves edges (for example https://site-server.example.internal:5001). The listener is off until EdgeListener:Port is set on the site server; set it there with HTTPS configured (see edge listener), and allow only your edge hosts to reach that port.
Upstream:BufferPathSQLite store-and-forward buffer, under the writable /var/lib/consystence state directory. Buffers tag data when the upstream link drops and replays on reconnect.
Upstream:BufferMaxBytesDisk cap on the store-and-forward buffer (default 536870912 — 512 MiB). On overflow the oldest data is dropped first; drops are counted and reported on the heartbeat, and edge health degrades while overflowing. Size it for the longest outage the site must ride out.
Upstream:ForceHttp3Opt-in HTTP/3 (QUIC) upstream; default false. The site server does not serve HTTP/3, so leave it off; gRPC then runs over HTTP/2 (TLS when Upstream:ServerUrl is https).
Plcs, PollGroups, Instances, CommandMappingsThe full PLC driver wiring, poll schedule, device instances, and command tag writes — all local today. See PLC communication.
Edge:ConfigVersionThe command-catalogue version this edge’s local config corresponds to. Reported to the site server on the command channel; commands are rejected fail-closed if it does not match the version the site activated for this edge.

The local diagnostics API binds only from the LocalApi section (default 127.0.0.1:8080). To reach it from a management network, set LocalApi:Address to that interface’s IP address and LocalApi:TokenFile to a file holding an access token of at least 32 characters. Requests from other hosts then need Authorization: Bearer <token> on the /api/* routes.

  1. The VL3 powers on and joins its IT-side network.
  2. consystence-edge reads its local configuration, takes its identity from Edge:Id, and registers with the site server at Upstream:ServerUrl, presenting that id.
  3. The site server answers APPROVED, PENDING or REJECTED. Until the answer is APPROVED, the edge retries, every five seconds by default; once approved, the edge appears in the site’s edge directory.
  4. Nothing is pushed after registration — the edge runs on the configuration it already holds locally.

One codebase is published as two server artifacts, Site and Cloud; the site server is the Site artifact. See the server model for how sites sit within a tenant.

consystence-edge reads and writes field data through the co-located PLCnext, not by talking to the field directly. The link is gRPC over a Unix Domain Socket at /run/plcnext/grpc.sock — the PLCnext gRPC server (RSC/Data Access services) also listens on TCP 41100, but the local UDS path is preferred for lowest latency and no network stack.

This link is one driver in the edge’s driver layer. The relevant drivers:

DriverUse
plcnext-grpcOn the VL3, the link to the co-located PLCnext. Reads/writes PLCnext GDS/PORT variables over gRPC-over-UDS (over TCP where the PLCnext is remote or containerised).
ethernet-ipDirect EtherNet/IP CIP from the edge to a Rockwell ControlLogix. On the VL3 in the border-gateway topology, the PLCnext owns the field link instead.
modbus-tcpRoadmap. Modbus TCP as a runtime acquisition driver is not yet available on the edge.
stubIn-memory driver for testing and simulation.

The PLCnext, in turn, speaks EtherNet/IP CIP (TCP 44818) to the ControlLogix rack or field IO. So the data path is: field → PLCnext (CIP) → consystence-edge (gRPC/UDS) → site server (gRPC over HTTP/2 TLS). The edge never speaks CIP itself in this topology.

PLC driver configuration lives in the edge’s local appsettings.json — the Plcs, PollGroups, and Instances sections shown above — so today you provision the link by editing the edge’s own config. Server-pushed driver config after registration is the target design, not current behaviour.

In the target installation the VL3 sits at the IT/OT boundary, with two interfaces: eth0 on the IT side (upstream to the site server) and eth1 on the OT side (the field network). Segment the two before going live, so that the OT side reaches the PLCnext and field devices and the IT side reaches the site server. Network security sets out a reference firewall policy for the edge host.

The same .NET 10 edge runtime runs on an Nvidia Jetson Orin in our workshop today, with the same appsettings.json configuration model. There, one edge reads a physical ControlLogix directly over EtherNet/IP and a virtual PLCnext over gRPC on TCP. The Orin is not a standalone ML appliance; it is an edge host with a GPU. See Orin setup for what differs from the VL3 installation.