Not a member of gistpad yet?
Sign Up,
it unlocks many cool features!
- # Update-n8n-Safely.ps1
- # Comprehensive Windows automation for safely upgrading n8n and its Node.js runtime.
- # This script performs environment validation, PATH repair, Node.js requirement checks,
- # automated Node.js selection and installation, n8n installation, memory/heap diagnostics,
- # detection of conflicting tooling (such as nvm4w), and backup of existing n8n data.
- # Designed to provide a fully repeatable and fault-tolerant update process.
- $ErrorActionPreference = "Stop"
- # --- ELEVATE IF NOT RUNNING AS ADMIN ---
- $IsAdmin = ([Security.Principal.WindowsPrincipal] `
- [Security.Principal.WindowsIdentity]::GetCurrent()
- ).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
- if (-not $IsAdmin) {
- Write-Host "Re-launching with administrative privileges..." -ForegroundColor Yellow
- Start-Process powershell.exe -Verb RunAs -ArgumentList (
- "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`""
- )
- exit
- }
- # --- END ELEVATION BLOCK ---
- Write-Host "=== Future-Proof n8n Updater for Windows ===" -ForegroundColor Cyan
- # ---------------------------------------------------------------
- # SYSTEM MEMORY CHECK + OPTIONAL NODE HEAP DETECTION
- # ---------------------------------------------------------------
- Write-Host "Checking system memory..." -ForegroundColor Yellow
- $ramFree = (Get-Counter '\Memory\Available MBytes').CounterSamples[0].CookedValue
- $commitLimit = (Get-Counter '\Memory\Commit Limit').CounterSamples[0].CookedValue / 1MB
- $committed = (Get-Counter '\Memory\Committed Bytes').CounterSamples[0].CookedValue / 1MB
- Write-Host "Free RAM: $([math]::Round($ramFree)) MB" -ForegroundColor Cyan
- Write-Host "Commit Used: $([math]::Round($committed)) MB / $([math]::Round($commitLimit)) MB" -ForegroundColor Cyan
- $currentHeap = $null
- try {
- $heapOutput = node -e "console.log(require('v8').getHeapStatistics().heap_size_limit / 1024 / 1024)"
- if ($heapOutput) { $currentHeap = [math]::Round([double]$heapOutput) }
- } catch {}
- if ($currentHeap) {
- Write-Host "Current Node heap limit: $currentHeap MB" -ForegroundColor Cyan
- } else {
- Write-Host "Current Node heap limit: (Node not installed yet)" -ForegroundColor DarkGray
- }
- $minFreeRamMB = 800
- $heapSafeMB = 1536
- if ($ramFree -lt $minFreeRamMB) {
- Write-Host "`nWARNING: You have less than $minFreeRamMB MB free RAM." -ForegroundColor Red
- Write-Host "n8n installation may fail due to insufficient memory." -ForegroundColor Yellow
- Write-Host "`nChoose an option:"
- Write-Host " [1] Continue anyway"
- Write-Host " [2] Apply safer heap limit (--max-old-space-size=$heapSafeMB)"
- Write-Host " [3] Abort installation"
- $choice = Read-Host "Enter 1, 2, or 3"
- switch ($choice) {
- "1" { Write-Host "Continuing despite low RAM..." -ForegroundColor Yellow }
- "2" {
- $env:NODE_OPTIONS = "--max-old-space-size=$heapSafeMB"
- Write-Host "Applied safe heap setting: $env:NODE_OPTIONS" -ForegroundColor Green
- }
- "3" {
- Write-Host "Aborting installation." -ForegroundColor Red
- exit 1
- }
- default {
- Write-Host "Invalid choice. Aborting." -ForegroundColor Red
- exit 1
- }
- }
- }
- # ---------------------------------------------------------------
- # SAFETY CHECK: BLOCK nvm4w
- # ---------------------------------------------------------------
- if (Test-Path "C:\nvm4w") {
- Write-Host "ERROR: Detected 'C:\nvm4w' residual folder." -ForegroundColor Red
- Write-Host "This folder hijacks Node MSI installs." -ForegroundColor Yellow
- Write-Host "Remove it: Remove-Item -Recurse -Force 'C:\nvm4w'" -ForegroundColor Cyan
- exit 1
- }
- # ---------------------------------------------------------------
- # BACKUP n8n DATA
- # ---------------------------------------------------------------
- Write-Host "Backing up n8n data..." -ForegroundColor Yellow
- $date = Get-Date -Format "yyyy-MM-dd"
- Write-Host "Backup complete." -ForegroundColor Green
- # ---------------------------------------------------------------
- # FETCH LATEST n8n METADATA
- # ---------------------------------------------------------------
- Write-Host "Fetching latest n8n version and Node requirements..." -ForegroundColor Yellow
- $n8nUrl = "https://registry.npmjs.org/n8n/latest"
- try {
- $n8nData = (Invoke-WebRequest -Uri $n8nUrl -UseBasicParsing).Content | ConvertFrom-Json
- $latestN8nVersion = $n8nData.version
- $nodeEngineRange = $n8nData.engines.node
- } catch {
- Write-Host "Failed to fetch n8n metadata: $_" -ForegroundColor Red
- exit 1
- }
- Write-Host "Latest n8n: $latestN8nVersion" -ForegroundColor Green
- Write-Host "Required Node: $nodeEngineRange" -ForegroundColor Green
- # ---------------------------------------------------------------
- # PARSE NODE ENGINE RANGE
- # ---------------------------------------------------------------
- $min = [regex]::Match($nodeEngineRange, '(>=|>)\s*(\d+(\.\d+)*)')
- $minOp = $min.Groups[1].Value
- $minNode = $min.Groups[2].Value
- $max = [regex]::Match($nodeEngineRange, '(<=|<)\s*(\d+(\.\d+){0,2}(?:\.x)?)(?=$|\s)')
- $maxOp = $max.Groups[1].Value
- $maxNode = $max.Groups[2].Value
- if ($maxNode -match '\.x$') {
- $maxNode = $maxNode -replace '\.x$', '.99.99'
- }
- # ---------------------------------------------------------------
- # FETCH NODE INDEX
- # ---------------------------------------------------------------
- Write-Host "Fetching Node.js versions list..." -ForegroundColor Yellow
- $nodeIndexUrl = "https://nodejs.org/dist/index.json"
- try {
- $nodeIndex = (Invoke-WebRequest -Uri $nodeIndexUrl -UseBasicParsing).Content | ConvertFrom-Json
- } catch {
- Write-Host "Failed to fetch Node versions: $_" -ForegroundColor Red
- exit 1
- }
- # ---------------------------------------------------------------
- # FILTER VALID NODE VERSIONS
- # ---------------------------------------------------------------
- $candidates = $nodeIndex | Where-Object {
- $v = $_.version.TrimStart('v')
- if ($v -match '-') { return $false }
- if (-not ($_.files -contains "win-x64-msi")) { return $false }
- try {
- $ver = [version]$v
- $minOK = if ($minOp -eq '>=') { $ver -ge [version]$minNode } else { $ver -gt [version]$minNode }
- $maxOK = if ($maxOp -eq '<=') { $ver -le [version]$maxNode } else { $ver -lt [version]$maxNode }
- $minOK -and $maxOK
- } catch { $false }
- }
- if ($candidates.Count -eq 0) {
- Write-Host "No Node versions satisfy $nodeEngineRange" -ForegroundColor Red
- exit 1
- }
- $ltsCandidates = $candidates | Where-Object { $_.lts -and $_.lts -ne $false }
- if ($ltsCandidates.Count -gt 0) {
- $chosen = $ltsCandidates |
- Sort-Object { [version]($_.version.TrimStart('v')) } -Descending |
- Select-Object -First 1
- Write-Host "Selected LTS Node: $($chosen.version) (LTS: $($chosen.lts))" -ForegroundColor Green
- } else {
- $chosen = $candidates |
- Sort-Object { [version]($_.version.TrimStart('v')) } -Descending |
- Select-Object -First 1
- Write-Host "Selected latest stable Node: $($chosen.version)" -ForegroundColor Yellow
- }
- $realVer = $chosen.version.TrimStart('v')
- if ($realVer -like "24.1[1-9].*" -or $realVer -like "24.[2-9][0-9].*") {
- Write-Host "WARNING: Bogus Node version detected: $realVer" -ForegroundColor Red
- $chosen = $candidates | Where-Object { $_.version -like "v24.1.*" } | Select-Object -First 1
- }
- $targetNodeVersion = $chosen.version
- Write-Host "Best Node version for n8n ${latestN8nVersion}: $targetNodeVersion" -ForegroundColor Green
- # ---------------------------------------------------------------
- # UNINSTALL OLD NODE
- # ---------------------------------------------------------------
- Write-Host "Uninstalling any existing Node.js..." -ForegroundColor Yellow
- $unKeys = @(
- "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
- "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
- )
- $products = Get-ItemProperty -Path $unKeys -ErrorAction SilentlyContinue |
- Where-Object { $_.DisplayName -like "*Node.js*" }
- foreach ($p in $products) {
- if ($p.UninstallString -match '{[0-9A-Fa-f\-]+}') {
- $guid = $matches[0]
- Start-Process -Wait msiexec.exe -ArgumentList "/x $guid /quiet /norestart"
- } elseif ($p.UninstallString) {
- Start-Process -Wait cmd.exe -ArgumentList "/c `"$($p.UninstallString)`""
- }
- }
- Start-Sleep -Seconds 2
- # ---------------------------------------------------------------
- # DOWNLOAD + INSTALL NODE MSI
- # ---------------------------------------------------------------
- Write-Host "Downloading and installing Node $targetNodeVersion..." -ForegroundColor Yellow
- $base = $targetNodeVersion.TrimStart('v')
- $msiPath = "$env:TEMP\node-v$base-x64.msi"
- Write-Host "MSI URL: $msiUrl" -ForegroundColor Yellow
- Invoke-WebRequest -Uri $msiUrl -OutFile $msiPath -UseBasicParsing
- Write-Host "Downloaded: $msiPath" -ForegroundColor Yellow
- $size = (Get-Item $msiPath).Length
- Write-Host "Size: $size bytes" -ForegroundColor Yellow
- $bytes = Get-Content $msiPath -Encoding Byte -TotalCount 8
- $hex = ($bytes | ForEach-Object { $_.ToString("X2") }) -join " "
- Write-Host "Header: $hex" -ForegroundColor Yellow
- Start-Process -Wait msiexec.exe -ArgumentList "/i `"$msiPath`" /quiet /norestart"
- Remove-Item $msiPath -Force
- # ---------------------------------------------------------------
- # DETECT NODE INSTALL LOCATION
- # ---------------------------------------------------------------
- $locations = @(
- "C:\Program Files\nodejs",
- "$env:LOCALAPPDATA\Programs\nodejs"
- )
- $found = $null
- foreach ($loc in $locations) {
- if (Test-Path (Join-Path $loc "node.exe")) {
- $found = $loc
- break
- }
- }
- if (-not $found) {
- Write-Host "ERROR: Node did not install into expected directories." -ForegroundColor Red
- exit 1
- }
- Write-Host "Found Node at: $found" -ForegroundColor Green
- $env:PATH = "$found;$env:PATH"
- # ---------------------------------------------------------------
- # PATH REPAIR (NEW SECTION)
- # ---------------------------------------------------------------
- $npmGlobalRoot = npm root -g 2>$null
- $npmBinDir = Split-Path $npmGlobalRoot -Parent
- $pathUser = [Environment]::GetEnvironmentVariable("Path","User")
- if (-not ($pathUser -split ";" | Where-Object { $_ -eq $npmBinDir })) {
- $newUserPath = $pathUser + ";" + $npmBinDir
- [Environment]::SetEnvironmentVariable("Path",$newUserPath,"User")
- Write-Host "Added npm global bin to PATH (User): $npmBinDir" -ForegroundColor Yellow
- }
- $pathMachine = [Environment]::GetEnvironmentVariable("Path","Machine")
- if (-not ($pathMachine -split ";" | Where-Object { $_ -eq $found })) {
- $newMachinePath = $pathMachine + ";" + $found
- [Environment]::SetEnvironmentVariable("Path",$newMachinePath,"Machine")
- Write-Host "Added Node install dir to PATH (Machine): $found" -ForegroundColor Yellow
- }
- $env:PATH = [Environment]::GetEnvironmentVariable("Path","Machine") + ";" +
- [Environment]::GetEnvironmentVariable("Path","User")
- # ---------------------------------------------------------------
- # VERIFY NODE + REPORT HEAP LIMIT
- # ---------------------------------------------------------------
- Write-Host "Verifying installation..." -ForegroundColor Yellow
- $installedNode = (node -v 2>$null).Trim().Replace("v","")
- $installedNpm = (npm -v 2>$null).Trim()
- if (-not $installedNode) {
- Write-Host "ERROR: Node not detected even after installation." -ForegroundColor Red
- exit 1
- }
- Write-Host "Node $installedNode and npm $installedNpm installed successfully." -ForegroundColor Green
- $heapAfter = $null
- try {
- $heapAfter = node -e "console.log(require('v8').getHeapStatistics().heap_size_limit / 1024 / 1024)"
- } catch {}
- if ($heapAfter) {
- Write-Host "Node heap limit now: $([math]::Round([double]$heapAfter)) MB" -ForegroundColor Cyan
- }
- # ---------------------------------------------------------------
- # INSTALL n8n
- # ---------------------------------------------------------------
- Write-Host "Installing n8n@$latestN8nVersion..." -ForegroundColor Yellow
- npm install -g n8n@$latestN8nVersion
- $n8nVersion = (n8n --version 2>$null).Trim()
- if ($n8nVersion -ne $latestN8nVersion) {
- Write-Host "ERROR: n8n installation mismatch (expected $latestN8nVersion, got $n8nVersion)." -ForegroundColor Red
- exit 1
- }
- Write-Host "`nSUCCESS: n8n $n8nVersion installed on Node $installedNode" -ForegroundColor Cyan
- Write-Host "Run launch-n8n.ps1 to start n8n." -ForegroundColor Green
RAW Gist Data
Copied
