When a blank CODESYS project keeps its RETAIN values but your application loses them, the automatic save at shutdown is failing. Catch the application exception in /tmp/codesyscontrol.log while the runtime stops, check for retain variables declared inside a function block, and move the affected values to the Persistence Manager.
RETAIN variables that come back as zero after a reboot usually point at one fault: the runtime failed to write the retain file when it shut down, so the next start had no current snapshot to load. A blank test project with a single RETAIN variable keeps its value across the same reboot, which proves the save and restore path works and shows the problem follows your application. In the reported case the blockage was an application-related exception caused by retain variables declared inside a function block, on CODESYS 3.5 SP15 Patch 1 with target version 3.5.15.10 running on a single-board computer. The unlicensed demo-mode warning in the log was a separate constraint. This article gives the symptom list, the two tests that isolate the failing step, the log lines to watch at shutdown, and the Persistence Manager workaround with a verification pass you can repeat.
Why do CODESYS retain variables reset to zero after a reboot?
The runtime is failing to write the retain file when it shuts down, so the next start loads whatever snapshot it last managed to save, and in the reported case the blockage was an exception caused by retain variables declared inside a function block.
What should happen is simple enough to check. At shutdown the runtime writes the retained data to a file named after the application, under /var/opt/codesys/PlcLogic/. For the default application that file is Application.ret. In the reported case, after sudo reboot now, the file had never been created and every RETAIN variable was zero.
The test that splits the two possible causes takes one download and one reboot. Create a blank project with a single RETAIN variable, download it, write a value, and run the same reboot. On the reported system the blank project created the retain file and the value came back. Same runtime, same hardware, same license state. A fault that follows the application and skips a blank project lives in the application, and the usual place is how its retained data is declared.
Which symptoms point to a failed retain save?
Four symptoms separate a failed shutdown save from normal behavior: values return as zero, the retain file never appears, a manual snapshot restores but never updates, and the startup log shows the retain data being initialized.
- RETAIN variables read zero after
sudo reboot now: the runtime started with no usable snapshot and initialized the retain area. - No file named
Application.retunder/var/opt/codesys/PlcLogic/Application/: the automatic save has never completed for this application. - A blank project with one RETAIN variable survives the same reboot: the save and restore path works, so the fault follows your application.
saveretainswith the PLC stopped creates/var/opt/codesys/PlcLogic/HomeAutomation/HomeAutomation.retand a restart reloads it: the manual write path is healthy.- Values you change while running never reach the file, and the old snapshot returns: the automatic save made at shutdown is the failing step.
- The startup log prints
Warning: Retain data are initialized now: the restore failed, which is the downstream effect, not the cause.
Is the missing runtime license the cause?
No: the investigated runtime ran unlicensed in demo mode, with its log printing roughly 119 minutes, and it still created and restored a retain file for a blank project, so the license state was not what blocked the save.
The log line on the reported setup read no runtime license - running in demo mode (~119 minutes). Under that same license state the blank-project test passed, and the manual saveretains snapshot wrote and reloaded in the affected project. A cause has to explain both results, and the demo clock explains neither.
Buy the license before production regardless. A runtime that is still evaluating belongs on the test bench, and the demo line will otherwise sit next to every fault you chase in the log. Keep the two problems on separate lines of your notes: the license is a purchase, the retain save is an application fault.
How do I prove the automatic save is the failing step?
Stop the PLC and run saveretains in the PLC Shell on the Device tab: on the reported system this wrote /var/opt/codesys/PlcLogic/HomeAutomation/HomeAutomation.ret at once, and a reboot loaded that snapshot back, which leaves the automatic save at shutdown as the only failing step.
- Put the PLC in stop and enter
saveretainsin the PLC Shell on the Device tab. - Check the application folder under
/var/opt/codesys/PlcLogic/for a.retfile and note its timestamp. - Restart the CODESYS service or reboot, and confirm the snapshot values come back.
- Change a retained value while the application runs, then run
sudo reboot nowand check the timestamp again. - Read the log for write errors: on the reported system none appeared, the timestamp never moved, and the previous values returned.
A manual write that lands while the PLC is stopped, followed by online changes that never land, is the signature of an automatic save that dies during shutdown.
Which log lines matter, and when do they appear?
Startup lines such as Warning: Retain data are initialized now tell you a restore failed, but the save failure itself only shows in the log while the runtime stops, so run tail -f /tmp/codesyscontrol.log through a shutdown and keep watching.
On the affected application the startup log carried two useful lines. Warning: Retain data are initialized now names the application in brackets and means the restore failed and the retain area was set to its initial values, which is exactly where the zeros came from. Retains matched to bootproject of application means the boot application itself loaded normally, so the project on the runtime is intact. The harsher variant seen in the same case reads ERROR: Retain restore from file failed: HomeAutomation, area=2, size=4096, type=0x0120; the fields name the application, the memory area (2), its size (4,096 bytes) and a type code, and the operative part is the failed restore.
None of that explains the save. The save runs at shutdown, so that is when you capture: open a terminal, run tail -f /tmp/codesyscontrol.log, then stop the service or reboot, and read every line the stop produces. The vendor's instruction in the reported case was exactly this, with the expectation of finding an application-related exception that prevents the save.
An exception raised against your application belongs to the same family of fault as an exception in a running task; stopping a CODESYS status task spin watchdog exception shows how that variant gets isolated. And if a crash has damaged the boot application rather than the retain data, recovering a corrupt CODESYS project file after a crash is the separate repair.
Why do retain variables inside a function block break the save?
The vendor's investigation found the exception was caused by retain variables declared in a function block, reported it as still under investigation with no direct solution, and prescribed one workaround: use the Persistence Manager for the affected values.
That finding matches the two test results. The blank project declared its RETAIN variable at program level and saved cleanly. The failing application held retained data inside a function block, and its shutdown save died with an exception.
So the check in your project is a search: list every VAR RETAIN declaration and mark the ones inside function blocks. Those are the values to move. The reported investigation offered no runtime setting or patch that makes a function-block retain save again, so the declaration is the thing you change.
What does a worked example of the fix look like?
In the reported application, named HomeAutomation, the fix was to move the retained values into the Persistence Manager and then verify with known numbers through both a service restart and a full reboot.
Use illustrative values: an operating-hours counter and a heating setpoint, set to 1,234 h and 21.5 °C before the test. The versions are the reported ones, CODESYS 3.5 SP15 Patch 1 with target version 3.5.15.10.
- Declare the counter and the setpoint in the Persistence Manager and remove their RETAIN declaration inside the function block.
- Download the application. An application download resets RETAIN variables by design, so write the two test numbers back: 1,234 and 21.5.
- Run
tail -f /tmp/codesyscontrol.login a terminal, then stop the runtime. Pass condition: the stop produces no application-related exception. - Start the runtime again and read the values back: 1,234 h and 21.5 °C.
- Change the counter to 1,237, as if the plant had run 3 hours, then run
sudo reboot now. - After the reboot, read again: 1,237 h and 21.5 °C mean the shutdown write succeeded for both restart types. A counter back at 1,234 means the shutdown write failed again; capture the stop log and compare it with the RETAIN failure.
Result: the two values survived a service restart and a reboot with a clean stop log, which is the exact sequence the RETAIN declaration failed. Run the pass twice; a single pass on a quiet system hides an intermittent exception.
Recommendation: Keep RETAIN for values declared at program level, and move any retained value that lives inside a function block to the Persistence Manager on a 3.5-series runtime; after the move, run the six-step verification above, including the reboot, before you trust those values in production.
When do retain variables reset by design?
Three operations reinitialize RETAIN variables by design: reset cold, reset origin and an application download; outside those, the documented behavior is that retained values are maintained.
The vendor's retain-versus-persistent guidance lists those three triggers, and they explain a lot of false alarms. If you downloaded a changed application an hour before the reboot that showed zeros, the reset was the documented download behavior. Run the blank-project test again after a clean stop before you conclude the save is broken.
PERSISTENT variables are the second remanent class, and they fail in their own way on small boards; why CODESYS persistent variables reset on a single-board PLC covers that diagnosis. If your project mixes both classes, fix one class at a time so you know which change produced the result.
Questions
Does running saveretains regularly make a usable workaround?
No. The command writes a manual snapshot while the PLC is stopped, and that snapshot reloads on the next start, but values you change while the application runs never reach the file while the shutdown save is broken, so the next reboot brings the old snapshot back. Use saveretains to prove where the failure sits, then move the affected values to the Persistence Manager.
Where does the runtime keep the retain file and the boot project?
Under /var/opt/codesys/PlcLogic/, in a folder named after the application. The reported case had /var/opt/codesys/PlcLogic/HomeAutomation/HomeAutomation.ret for the manual snapshot, and the default application folder holds Application.ret beside Application.app and Application.crc. After a controlled stop, check the timestamp: a retain file that never moves is the signature of the failed shutdown save.
Do these paths and commands apply outside this version?
The reported case ran CODESYS 3.5 SP15 Patch 1 with target version 3.5.15.10 on a single-board Linux runtime, and the paths shown are that platform's layout under /var/opt/codesys/. The three checks work on any 3.5-series runtime: the blank project, saveretains with the PLC stopped, and the stop-time log capture. File locations differ between runtime packages, so read the locations from your own installation rather than copying these.
Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.