Skip to content

fix(pulse): keep the container recoverable when an update fails - #16311

Open
rcourtman wants to merge 1 commit into
community-scripts:mainfrom
rcourtman:fix/pulse-update-path-resilience
Open

fix(pulse): keep the container recoverable when an update fails#16311
rcourtman wants to merge 1 commit into
community-scripts:mainfrom
rcourtman:fix/pulse-update-path-resilience

Conversation

@rcourtman

Copy link
Copy Markdown
Contributor

✍️ Description

Three related fixes to update_script() in ct/pulse.sh. All are about the same thing: a failed or migrating update currently leaves the container in a state the operator cannot recover from.

1. The pre-fetch rm -f /opt/pulse/pulse is a vestige and is actively harmful.

On a v5-layout container that file is the Pulse binary, and pulse-backend.service points ExecStart at it. It is deleted after the service is stopped and before anything is downloaded, so a failed download leaves the container with no binary at all.

It is also redundant. It was added back when the fetch call had no CLEAN_INSTALL=1 (226a5bc, 2025-09-09), so it was the only thing clearing the old binary. CLEAN_INSTALL=1 was added later and _deploy_unpacked_archive now wipes the target anyway.

2. A failed fetch leaves Pulse stopped.

catch_errors sets set -Ee with an ERR trap, so a 403 rate limit, DNS failure or truncated download exits through the error handler and never reaches systemctl start. The fetch is now guarded and the previously installed Pulse is restarted, reporting honestly whether that restart worked. Exit code 250 matches error_handler.func's "App: Download failed or version not determined", consistent with ct/ollama.sh.

3. The pulse-backend.service to pulse.service rename drops boot enablement.

The installer created and enabled pulse-backend.service up to a403da9 (2025-08-14) and switched to pulse.service at 226a5bc. Containers built in that window have a wants symlink pointing at the old name; the mv leaves it dangling and nothing enables the new name, so Pulse stops starting at boot. The unit is now re-enabled after the rename, but only when it was enabled beforehand, so an operator who deliberately disabled autostart keeps that.

Testing

Throwaway unprivileged LXC on PVE 9.2.5, Debian 13, systemd 257. Each behaviour checked unpatched and patched.

Scenario Before After
Unit rename, was enabled pulse reports disabled, wants symlink dangling, no boot start enabled, symlink correct
Unit rename, operator had disabled autostart n/a stays disabled
Download fails mid-update (v5 layout) both units inactive and /opt/pulse holds only VERSION, binary gone "Restarted the previously installed Pulse", binary intact
Happy path v5 to v6 n/a pulse.service active + enabled, old unit gone, Pulse v6.1.2, listening on 7655
Happy path v6 to v6 (common case) n/a active + enabled, unchanged behaviour
Download fails, v6 to v6 n/a restarted, binary intact

The download failures were produced by blackholing the release asset hosts while leaving api.github.com reachable, so the update check succeeds and the failure lands on the download, which is the window at issue.

No create_backup/restore_backup is needed here: Pulse keeps its state in /etc/pulse, outside the app dir, which is the design AGENTS.md says to prefer.

I maintain Pulse and am a co-author of this script.

AI assistance: written with Claude Opus 5 (claude-opus-5) in an interactive Claude Code session with extended reasoning. Every claim in this PR was reproduced on real hardware before submitting, and I have reviewed the diff.

🔗 Related Issue

N/A

✅ Prerequisites (X in brackets)

  • Self-review completed – Code follows project standards.
  • Tested thoroughly – Changes work as expected.
  • No security risks – No hardcoded secrets, unnecessary privilege escalations, or permission issues.

🤖 AI Assistance (X in brackets)

  • No AI used – Scripts were written without AI assistance.
  • AI was used – I confirm the scripts were built using AGENTS.md and .github/agents/pve-script-creator.agent.md as guidance, and the output has been reviewed and corrected to match those guidelines.

🛠️ Type of Change (X in brackets)

  • 🐞 Bug fix – Resolves an issue without breaking functionality.
  • New feature – Adds new, non-breaking functionality.
  • 💥 Breaking change – Alters existing functionality in a way that may require updates.
  • 🆕 New script – A fully functional and tested script or script set.
  • 🌍 Website update – Changes to script metadata (PocketBase/website data).
  • 🔧 Refactoring / Code Cleanup – Improves readability or maintainability without changing functionality.
  • 📝 Documentation update – Changes to README, AppName.md, CONTRIBUTING.md, or other docs.

- Do not delete /opt/pulse/pulse before the download. CLEAN_INSTALL=1
  already wipes the target, so the rm only widened the failure window and
  destroyed the v5 binary when the fetch failed.
- Guard fetch_and_deploy_gh_release and restart the running installation
  when it fails, instead of exiting through the error handler with Pulse
  stopped.
- Re-enable the unit after the pulse-backend to pulse rename, but only if
  it was enabled before, so a deliberately disabled service stays that way.
@rcourtman
rcourtman requested a review from a team as a code owner August 6, 2026 13:01
@github-actions github-actions Bot added bugfix update script A change that updates a script labels Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix update script A change that updates a script

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant