Skip to content

Fix user data persistence across reboots for native Linux installs - #61

Merged
thiagoralves merged 1 commit into
developmentfrom
devin/1767397068-fix-user-persistence
Jan 3, 2026
Merged

Fix user data persistence across reboots for native Linux installs#61
thiagoralves merged 1 commit into
developmentfrom
devin/1767397068-fix-user-persistence

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Summary

Fixes an issue where user credentials were lost after reboot on native Linux installations. The root cause was that /var/run/runtime is a tmpfs that gets cleared on reboot. When the .env file disappeared, a new one was generated with new secrets, which invalidated the existing password hashes and triggered database deletion.

Solution: Store persistent data (.env and restapi.db) in /var/lib/openplc-runtime on native Linux, while continuing to use /var/run/runtime for Docker deployments (where it's expected to be a mounted persistent volume).

Key changes:

  • Added is_running_in_container() function with multiple detection methods (/.dockerenv, env vars, cgroup, /proc/1/environ)
  • Added get_persistent_data_dir() to route persistent data to the appropriate location based on environment
  • Installer now creates /var/lib/openplc-runtime when systemd is available

Review & Testing Checklist for Human

  • Test native Linux reboot persistence: Install on a native Linux system, create a user, reboot, verify the user still exists and can log in
  • Verify Docker still works: Run the Docker image with -v openplc-runtime-data:/var/run/runtime and confirm data persists in the mounted volume (not /var/lib/openplc-runtime)
  • Check container detection edge cases: Test in Podman, Kubernetes pods, or LXC containers if possible to verify detection works correctly
  • Verify directory permissions: After native install, check that /var/lib/openplc-runtime has appropriate permissions (755) and files inside have correct ownership

Recommended Test Plan

  1. Fresh install on Ubuntu/Debian VM with systemd
  2. Create a user via the web interface
  3. Verify .env and restapi.db are in /var/lib/openplc-runtime/ (not /var/run/runtime/)
  4. Reboot the system
  5. Verify the user still exists and can log in
  6. Build and run Docker image to confirm container detection works

Notes

  • CI runs in Docker so it cannot exercise the native Linux code path - manual testing is required
  • Container detection uses multiple fallback methods but may have edge cases in unusual container runtimes
  • No automatic migration for users who may have manually persisted data in /var/run/runtime

Requested by: Thiago Alves (@thiagoralves)
Link to Devin run: https://app.devin.ai/sessions/ebc278ce265842bba5abf29df61e1201

- Add is_running_in_container() function to detect Docker/Podman environments
- Add get_persistent_data_dir() function to determine where to store persistent data
- On native Linux: use /var/lib/openplc-runtime for .env and database files
- On containers: continue using /var/run/runtime (mounted as persistent volume)
- Update installer to create /var/lib/openplc-runtime when systemd is available

This fixes the issue where user credentials were lost after reboot because
/var/run/runtime is a tmpfs that gets cleared on reboot. The .env file
regeneration would then delete the database since new secrets invalidate
old password hashes.

Container detection checks:
- /.dockerenv file existence
- container/DOCKER_CONTAINER environment variables
- /proc/1/cgroup for docker/kubepods/lxc/containerd patterns
- /proc/1/environ for container= variable

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@thiagoralves
thiagoralves requested a review from Copilot January 3, 2026 00:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes user credential loss after system reboots on native Linux installations. The issue occurred because persistent data (.env and restapi.db) were stored in /var/run/runtime, a tmpfs directory cleared on reboot, causing password hash invalidation and database regeneration.

Key changes:

  • Introduced container detection logic to differentiate Docker from native Linux environments
  • Routes persistent data to /var/lib/openplc-runtime on native Linux while preserving /var/run/runtime for Docker deployments
  • Modified installer to create the persistent data directory on systemd-based systems

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
webserver/config.py Added container detection function and separate persistent data directory logic to route .env and database files appropriately
install.sh Creates /var/lib/openplc-runtime directory on native Linux systems with systemd support

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread webserver/config.py
Comment on lines +31 to +36
cgroup_content = f.read()
if "docker" in cgroup_content or "kubepods" in cgroup_content:
return True
# Also check for containerd/cri-o patterns
if "/lxc/" in cgroup_content or "containerd" in cgroup_content:
return True

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading the entire cgroup file and performing multiple substring searches is inefficient. Consider checking for all patterns in a single pass or stopping at the first match to avoid unnecessary string operations.

Suggested change
cgroup_content = f.read()
if "docker" in cgroup_content or "kubepods" in cgroup_content:
return True
# Also check for containerd/cri-o patterns
if "/lxc/" in cgroup_content or "containerd" in cgroup_content:
return True
indicators = ("docker", "kubepods", "/lxc/", "containerd")
for line in f:
if any(indicator in line for indicator in indicators):
return True

Copilot uses AI. Check for mistakes.
Comment thread webserver/config.py
Comment on lines +42 to +45
with open("/proc/1/environ", "rb") as f:
environ_content = f.read().decode("utf-8", errors="ignore")
if "container=" in environ_content:
return True

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading and decoding the entire /proc/1/environ file is inefficient. The file can be large and contain many null-separated environment variables. Consider reading in chunks or using a more targeted search approach.

Suggested change
with open("/proc/1/environ", "rb") as f:
environ_content = f.read().decode("utf-8", errors="ignore")
if "container=" in environ_content:
return True
pattern = b"container="
tail_len = len(pattern) - 1
with open("/proc/1/environ", "rb") as f:
leftover = b""
while True:
chunk = f.read(4096)
if not chunk:
break
data = leftover + chunk
if pattern in data:
return True
if tail_len > 0:
leftover = data[-tail_len:]

Copilot uses AI. Check for mistakes.
Comment thread webserver/config.py
data_dir = Path("/var/lib/openplc-runtime")

# Ensure the directory exists with appropriate permissions
data_dir.mkdir(parents=True, exist_ok=True)

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating the directory without explicit permission checks could lead to security issues if the directory is created with unexpected ownership or permissions. On native Linux, /var/lib/openplc-runtime should have restricted permissions (e.g., 0755) and appropriate ownership to prevent unauthorized access to sensitive data like .env files containing secrets.

Suggested change
data_dir.mkdir(parents=True, exist_ok=True)
if data_dir == Path("/var/lib/openplc-runtime"):
# Explicitly set permissions for sensitive persistent data directory
data_dir.mkdir(parents=True, mode=0o755, exist_ok=True)
try:
os.chmod(data_dir, 0o755)
except PermissionError:
logger.warning(
"Could not set permissions on %s to 0755 due to insufficient privileges",
data_dir,
)
else:
data_dir.mkdir(parents=True, exist_ok=True)

Copilot uses AI. Check for mistakes.
@thiagoralves
thiagoralves merged commit bb12dba into development Jan 3, 2026
1 check passed
@thiagoralves
thiagoralves deleted the devin/1767397068-fix-user-persistence branch January 3, 2026 02:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants