<# .SYNOPSIS Installs power-outage auto-start for the Coolify stack on the Proxmox host. .DESCRIPTION Makes sure that after a power outage (host reboot) LXC 102 (Coolify) and its Cloudflare tunnel come back up on their own. It does three things: 1. Sets the LXC to start on boot -> pct set --onboot 1 --startup order=1,up=30 2. Installs a self-healing guardian -> /usr/local/bin/coolify-autostart.sh + systemd unit /etc/systemd/system/coolify-autostart.service 3. Enables the guardian so it runs on every boot. The guardian is idempotent: on each boot it ensures the LXC is running, waits for Docker inside it, and starts any core Coolify container or tunnel that isn't up. Everything is additive and reversible (see -Uninstall). Source files live in scripts/host/ and are pushed over SSH (base64, so no quoting issues and CRLF is normalised to LF on the host). .PARAMETER VerifyOnly Only report current state (onboot flag, unit enabled, log tail). No changes. .PARAMETER SkipOnboot Install/enable the guardian but do NOT touch the LXC onboot flag. .PARAMETER RunNow After installing, trigger the guardian once (systemctl start) to test it and print the resulting log. Safe: the guardian only starts things, never stops. .PARAMETER Uninstall Remove the guardian (disable + delete unit + script). Does NOT clear onboot. .EXAMPLE .\scripts\Install-CoolifyAutostart.ps1 -RunNow .EXAMPLE .\scripts\Install-CoolifyAutostart.ps1 -VerifyOnly #> [CmdletBinding()] param( [switch]$VerifyOnly, [switch]$SkipOnboot, [switch]$RunNow, [switch]$Uninstall ) $ErrorActionPreference = "Stop" . "$PSScriptRoot\ProxmoxAgent.ps1" $config = Get-ProxmoxConfig $lxc = $config.CoolifyLxc $svcName = "coolify-autostart.service" $svcPath = "/etc/systemd/system/$svcName" $binPath = "/usr/local/bin/coolify-autostart.sh" $logPath = "/var/log/coolify-autostart.log" $localSh = Join-Path $PSScriptRoot "host\coolify-autostart.sh" $localSvc = Join-Path $PSScriptRoot "host\coolify-autostart.service" function Write-Section([string]$Text) { Write-Host "" Write-Host "== $Text ==" -ForegroundColor Cyan } # Run a remote command, normalising CRLF -> LF first. Multi-line command strings # authored in this (CRLF) file would otherwise carry a trailing '\r' on every # line, which breaks commands whose last token matters (e.g. unit names). function Invoke-Remote([string]$Command) { Invoke-ProxmoxSshCommand -Command ($Command -replace "`r`n", "`n") } # Push text to the host as LF-normalised bytes via base64 (avoids every # quoting/CRLF pitfall of heredocs over SSH). Pass -Content to push a # pre-built string instead of a file. function Push-TextFile { param( [string]$LocalPath, [Parameter(Mandatory)][string]$RemotePath, [string]$Content ) if ($Content) { $text = $Content } else { $text = Get-Content -LiteralPath $LocalPath -Raw } $text = $text -replace "`r`n", "`n" $bytes = [Text.Encoding]::UTF8.GetBytes($text) $b64 = [Convert]::ToBase64String($bytes) Invoke-ProxmoxSshCommand -Command "echo '$b64' | base64 -d > '$RemotePath'" } # --------------------------------------------------------------------------- # VERIFY-ONLY # --------------------------------------------------------------------------- if ($VerifyOnly) { Write-Section "Verification (read-only) - LXC $lxc" Invoke-Remote @" echo '--- onboot / startup on LXC $lxc ---' grep -Ei 'onboot|startup' /etc/pve/lxc/$lxc.conf 2>/dev/null || echo '(onboot not set -> defaults to 0, will NOT auto-start)' echo '--- guardian unit ---' systemctl is-enabled $svcName 2>/dev/null || echo '($svcName not installed/enabled)' echo '--- guardian script present? ---' test -x $binPath && echo 'present ($binPath)' || echo 'missing ($binPath)' echo '--- last log lines ---' tail -n 15 $logPath 2>/dev/null || echo '(no log yet)' "@ return } # --------------------------------------------------------------------------- # UNINSTALL # --------------------------------------------------------------------------- if ($Uninstall) { Write-Section "Uninstalling guardian (onboot flag left untouched)" Invoke-Remote @" systemctl disable --now $svcName 2>/dev/null || true rm -f $svcPath $binPath systemctl daemon-reload echo 'guardian removed. To also stop auto-start of the LXC: pct set $lxc --onboot 0' "@ Write-Host "Done. onboot flag NOT changed." -ForegroundColor Yellow return } # --------------------------------------------------------------------------- # INSTALL # --------------------------------------------------------------------------- foreach ($f in @($localSh, $localSvc)) { if (-not (Test-Path -LiteralPath $f)) { throw "Missing source file: $f" } } # Bake the configured LXC id into the unit so the guardian targets the right one. $svcText = (Get-Content -LiteralPath $localSvc -Raw) -replace "`r`n", "`n" $svcText = $svcText -replace "(?m)^\[Service\]\n", "[Service]`nEnvironment=COOLIFY_LXC=$lxc`n" Write-Section "1/4 Push guardian script + unit to host" Push-TextFile -LocalPath $localSh -RemotePath $binPath Push-TextFile -RemotePath $svcPath -Content $svcText Invoke-Remote "chmod 755 '$binPath' && bash -n '$binPath' && echo 'script pushed + syntax OK'" Write-Host " script -> $binPath" -ForegroundColor Green Write-Host " unit -> $svcPath (COOLIFY_LXC=$lxc)" -ForegroundColor Green if (-not $SkipOnboot) { Write-Section "2/4 Enable LXC $lxc onboot (native Proxmox auto-start)" Invoke-Remote "pct set $lxc --onboot 1 --startup order=1,up=30 && echo 'onboot=1 set' && grep -Ei 'onboot|startup' /etc/pve/lxc/$lxc.conf" } else { Write-Section "2/4 Skipping onboot flag (-SkipOnboot)" } Write-Section "3/4 Enable guardian on every boot" Invoke-Remote "systemctl daemon-reload && systemctl enable $svcName && echo 'guardian enabled'" Write-Section "4/4 Verify" Invoke-Remote @" printf 'onboot: '; grep -Ei 'onboot' /etc/pve/lxc/$lxc.conf 2>/dev/null || echo '(not set)' printf 'guardian enabled: '; systemctl is-enabled $svcName 2>/dev/null || echo 'no' printf 'script executable: '; test -x $binPath && echo yes || echo no "@ if ($RunNow) { Write-Section "RunNow Triggering guardian once (test)" Invoke-Remote "systemctl start $svcName; sleep 2; systemctl is-active $svcName; echo '--- log ---'; tail -n 25 $logPath" } Write-Host "" Write-Host "Done. After a power outage the host will auto-start LXC $lxc, Docker, the" -ForegroundColor Green Write-Host "Coolify core containers and the Cloudflare tunnel. Re-run with -VerifyOnly" -ForegroundColor Green Write-Host "to check state, or -Uninstall to remove the guardian." -ForegroundColor Green