Environment
- OpenPLC Runtime:
v4.1.10, image ghcr.io/autonomy-logic/openplc-runtime@sha256:1650a50d1a3e9c75cdd548c06866fc328aac1268af9b83c0313a4ab96500325d, revision bf82b1b661fd95c9899969f629d692b77b4e1454, built 2026-08-11T20:23:44.547Z (latest available at time of report)
- Architecture:
arm64
- Host: Debian 13(ARM64 controller), Docker
- OpenPLC Editor:
OpenPLC.Editor-4.2.11.AppImage on Ubuntu (desktop, used only to compile/upload)
Docker run command:
docker run -d \
--name openplc-runtime \
--restart unless-stopped \
--publish 8443:8443 \
--publish 1502:502 \
--publish 14840:4840 \
--cap-add SYS_NICE \
--cap-add SYS_RESOURCE \
--cap-add IPC_LOCK \
--ulimit memlock=-1:-1 \
--memory 2g \
--memory-swap 2g \
--pids-limit 256 \
--security-opt no-new-privileges:true \
--log-driver json-file \
--log-opt max-size=5m \
--log-opt max-file=2 \
--volume /mnt/data/openplc/runtime:/var/run/runtime \
"$OPENPLC_IMAGE"
--cap-add IPC_LOCK and --ulimit memlock=-1:-1 were added specifically to try to fix this issue (see "What we already tried" below) — they fixed a separate mlockall failed: Cannot allocate memory error but did not fix the crash described here.
Summary
As soon as a compiled PLC program contains any Python-language Function Block (POU-level, created via Editor → "+" → Function Blocks → Language: Python), the PLC scan task (TASK0) crashes with signal 11 (SIGSEGV) on every single startup, immediately after the Python loader logs that it created a shared-memory segment. This happens even with a trivial Function Block with only 3 variables and no logic beyond a periodic file write — so it does not appear to depend on the number/size of VAR_INPUT/VAR_OUTPUT variables, or on the block's Python code.
Regular Python plugins (the plugin_driver.c/venv-based system used by modbus_slave, modbus_master, and opcua) load and run correctly in the same container — only the POU-level "Python Function Block" loader is affected.
Steps to reproduce
- In OpenPLC Editor v4.2.11, create a new Function Block, set Language to Python, with a minimal body, e.g.:
FUNCTION_BLOCK TEST_FILE_WRITE
VAR_INPUT
CPU_TEMPERATURE_C : REAL;
END_VAR
VAR_OUTPUT
WRITE_COUNT : UDINT;
LAST_ERROR : STRING;
END_VAR
import time
TEST_FILE_PATH = "/var/run/runtime/test_write.txt"
TEST_INTERVAL_SECONDS = 10.0
_last_write_time = None
def block_init():
global WRITE_COUNT, LAST_ERROR
WRITE_COUNT = 0
LAST_ERROR = ""
def block_loop():
global _last_write_time, WRITE_COUNT, LAST_ERROR
now = time.monotonic()
if _last_write_time is not None and now - _last_write_time < TEST_INTERVAL_SECONDS:
return
_last_write_time = now
try:
with open(TEST_FILE_PATH, "a", encoding="utf-8") as f:
f.write("%s CPU_TEMPERATURE_C=%s\n" % (time.strftime("%Y-%m-%d %H:%M:%S"), CPU_TEMPERATURE_C))
WRITE_COUNT = WRITE_COUNT + 1
except OSError as exc:
LAST_ERROR = str(exc)
print("TEST_FILE_WRITE: write failed: %s" % exc)
END_FUNCTION_BLOCK
- Declare and call one instance of it from
main (any single scalar input is enough, e.g. a REAL from an existing struct field).
- Compile for OpenPLC Runtime v4, upload to the Runtime container above.
- Observe the Runtime log (
docker logs openplc-runtime or the log file downloaded from the web UI).
Actual behavior
The PLC starts, then crashes on every single run, always at the same point:
[INFO]: PLC State: RUNNING
[INFO]: [Python loader] Random shared memory location: shmXXXXXX7Uyr5O
[ERROR]: [task TASK0] terminated by signal 11 — other tasks keep running
other tasks keep running — the OPC UA server, etc. come up fine afterward, so the crash is silent/non-fatal to the container as a whole, which makes it easy to miss: the Runtime looks "alive" (OPC UA responds) but the actual IEC/Python program logic is dead — no more scans execute, so no PLC logic runs and any Python FB's file writes never happen.
On stop, the Python loader cleanup log always shows the block with PID 0:
[INFO]: [Python loader] Cleaning up 1 Python function block(s)...
[INFO]: [Python loader] Stopping Python block: TEST_FILE_WRITE.py (PID 0)
[INFO]: [Python loader] Cleanup complete
PID 0 looks suspicious — possibly the subprocess spawn for the block never actually completed/registered a real PID before something else (the shared-memory setup?) segfaulted the parent task.
Expected behavior
The Python Function Block loads, block_init()/block_loop() run periodically as documented, and TASK0 keeps scanning normally.
What we already tried
- Reduced the Function Block to the absolute minimum (3 variables, code above) — still crashes identically. Originally we hit this with a much larger custom Function Block (86
VAR_INPUT/VAR_OUTPUT STRING variables) and initially suspected a size/shared-memory-segment-size limit; splitting that block into smaller ones (down to ~50 variables) did not help, and this minimal 3-variable reproduction confirms size is not the deciding factor.
- Multiple vs. single Python FB: tried with 1, 2, and 3 simultaneously-declared Python Function Blocks of varying sizes — all combinations crash the same way, always right after a "[Python loader] Random shared memory location" line. This happens even with just the single 3-variable block in the minimal repro above, so it's not specific to having more than one block. When 3 blocks were declared, the cleanup-on-stop log only ever listed 1 of them (
Cleaning up 1 Python function block(s)...) even though the program declared 3 — but with only 1 block declared (the minimal repro), that same 1 block is listed in the cleanup log despite the crash still occurring. So the crash doesn't simply gate on "the Nth block fails to init" — some part of Python FB shared-memory setup crashes TASK0 regardless of block count, while whichever block(s) had already finished their own setup by that point still get tracked correctly.
mlockall failed: Cannot allocate memory was present in every log alongside the crash. Adding --cap-add IPC_LOCK and --ulimit memlock=-1:-1 to docker run (see command above) made this specific error disappear, but the signal 11 crash persisted identically — so this is a real but separate issue, not the root cause of the crash. (Possibly still worth a graceful-degradation fix on its own — the container previously failed mlockall on every start regardless of whether any Python FB was even used.)
sched_setscheduler failed: Operation not permitted is also present on every start (before and after the IPC_LOCK change) — this looks unrelated to the crash (it's a real-time scheduling priority request for the PLC thread, logged as non-fatal — "running default scheduling" — elsewhere in the log) but noting it in case it's relevant.
- Confirmed the image is the latest available (
v4.1.10, built 2026-08-11) — this is not a stale-build issue.
Log excerpt (single run, minimal repro)
[18-08-26 10:44:33] [ERROR]: mlockall failed: Cannot allocate memory
[18-08-26 10:44:33] [ERROR]: sched_setscheduler failed: Operation not permitted
[18-08-26 10:44:33] [INFO]: PLC State: RUNNING
[18-08-26 10:44:33] [INFO]: [Python loader] Random shared memory location: shmXXXXXXOHkDj2
[18-08-26 10:44:33] [ERROR]: [task TASK0] terminated by signal 11 — other tasks keep running
This excerpt predates the IPC_LOCK/memlock fix; a run after that fix shows the identical crash but without the mlockall line:
[18-08-26 10:58:53] [ERROR]: sched_setscheduler failed: Operation not permitted
[18-08-26 10:58:53] [INFO]: PLC State: RUNNING
[18-08-26 10:58:53] [INFO]: [Python loader] Random shared memory location: shmXXXXXX7Uyr5O
[18-08-26 10:58:53] [ERROR]: [task TASK0] terminated by signal 11 — other tasks keep running
Additional notes
- Other Python-based plugins (
modbus_slave, modbus_master, opcua — the venv-based plugin_driver.c system, distinct from the POU-level Python Function Block loader) load and run without any issue in the same container/build, so this appears isolated to python_loader.c / the POU Function Block shared-memory setup path specifically.
- We searched the OpenPLC community forum and found a thread whose title exactly matches this crash message (
"[task TASK0] terminated by signal 11 — other tasks keep running"), suggesting this isn't unique to our setup, but we weren't able to access its content.
- Happy to provide the full log files, our Python FB source, or test further changes if it helps narrow this down — this is currently a hard blocker for using Python Function Blocks on (arm64) hardware.
Environment
v4.1.10, imageghcr.io/autonomy-logic/openplc-runtime@sha256:1650a50d1a3e9c75cdd548c06866fc328aac1268af9b83c0313a4ab96500325d, revisionbf82b1b661fd95c9899969f629d692b77b4e1454, built2026-08-11T20:23:44.547Z(latest available at time of report)arm64OpenPLC.Editor-4.2.11.AppImageon Ubuntu (desktop, used only to compile/upload)Docker run command:
docker run -d \ --name openplc-runtime \ --restart unless-stopped \ --publish 8443:8443 \ --publish 1502:502 \ --publish 14840:4840 \ --cap-add SYS_NICE \ --cap-add SYS_RESOURCE \ --cap-add IPC_LOCK \ --ulimit memlock=-1:-1 \ --memory 2g \ --memory-swap 2g \ --pids-limit 256 \ --security-opt no-new-privileges:true \ --log-driver json-file \ --log-opt max-size=5m \ --log-opt max-file=2 \ --volume /mnt/data/openplc/runtime:/var/run/runtime \ "$OPENPLC_IMAGE"--cap-add IPC_LOCKand--ulimit memlock=-1:-1were added specifically to try to fix this issue (see "What we already tried" below) — they fixed a separatemlockall failed: Cannot allocate memoryerror but did not fix the crash described here.Summary
As soon as a compiled PLC program contains any Python-language Function Block (POU-level, created via Editor → "+" → Function Blocks → Language: Python), the PLC scan task (
TASK0) crashes withsignal 11(SIGSEGV) on every single startup, immediately after the Python loader logs that it created a shared-memory segment. This happens even with a trivial Function Block with only 3 variables and no logic beyond a periodic file write — so it does not appear to depend on the number/size ofVAR_INPUT/VAR_OUTPUTvariables, or on the block's Python code.Regular Python plugins (the
plugin_driver.c/venv-based system used bymodbus_slave,modbus_master, andopcua) load and run correctly in the same container — only the POU-level "Python Function Block" loader is affected.Steps to reproduce
main(any single scalar input is enough, e.g. aREALfrom an existing struct field).docker logs openplc-runtimeor the log file downloaded from the web UI).Actual behavior
The PLC starts, then crashes on every single run, always at the same point:
other tasks keep running— the OPC UA server, etc. come up fine afterward, so the crash is silent/non-fatal to the container as a whole, which makes it easy to miss: the Runtime looks "alive" (OPC UA responds) but the actual IEC/Python program logic is dead — no more scans execute, so no PLC logic runs and any Python FB's file writes never happen.On stop, the Python loader cleanup log always shows the block with PID 0:
PID 0looks suspicious — possibly the subprocess spawn for the block never actually completed/registered a real PID before something else (the shared-memory setup?) segfaulted the parent task.Expected behavior
The Python Function Block loads,
block_init()/block_loop()run periodically as documented, andTASK0keeps scanning normally.What we already tried
VAR_INPUT/VAR_OUTPUTSTRINGvariables) and initially suspected a size/shared-memory-segment-size limit; splitting that block into smaller ones (down to ~50 variables) did not help, and this minimal 3-variable reproduction confirms size is not the deciding factor.Cleaning up 1 Python function block(s)...) even though the program declared 3 — but with only 1 block declared (the minimal repro), that same 1 block is listed in the cleanup log despite the crash still occurring. So the crash doesn't simply gate on "the Nth block fails to init" — some part of Python FB shared-memory setup crashesTASK0regardless of block count, while whichever block(s) had already finished their own setup by that point still get tracked correctly.mlockall failed: Cannot allocate memorywas present in every log alongside the crash. Adding--cap-add IPC_LOCKand--ulimit memlock=-1:-1todocker run(see command above) made this specific error disappear, but thesignal 11crash persisted identically — so this is a real but separate issue, not the root cause of the crash. (Possibly still worth a graceful-degradation fix on its own — the container previously failedmlockallon every start regardless of whether any Python FB was even used.)sched_setscheduler failed: Operation not permittedis also present on every start (before and after theIPC_LOCKchange) — this looks unrelated to the crash (it's a real-time scheduling priority request for the PLC thread, logged as non-fatal — "running default scheduling" — elsewhere in the log) but noting it in case it's relevant.v4.1.10, built2026-08-11) — this is not a stale-build issue.Log excerpt (single run, minimal repro)
This excerpt predates the
IPC_LOCK/memlockfix; a run after that fix shows the identical crash but without themlockallline:Additional notes
modbus_slave,modbus_master,opcua— the venv-basedplugin_driver.csystem, distinct from the POU-level Python Function Block loader) load and run without any issue in the same container/build, so this appears isolated topython_loader.c/ the POU Function Block shared-memory setup path specifically."[task TASK0] terminated by signal 11 — other tasks keep running"), suggesting this isn't unique to our setup, but we weren't able to access its content.