Delayed (staged) auto-update for the NetBird client on Windows (Server 2019+ / Windows 10+).
Don’t upgrade NetBird clients immediately when a new version appears in Chocolatey.
Instead, wait N days. If that version is quickly replaced (hotfix / bad release),
clients will never upgrade to it.
- A candidate NetBird version in the Chocolatey repository must “age” for N days before being deployed.
- If the same version stays in the repo for
DelayDayswithout changes, the installed client is upgraded. - If a newer version appears in the repo, the aging timer is reset and we start counting again.
- NetBird is not auto-installed – only upgraded if it is already installed locally.
- Uses Windows Task Scheduler to run a PowerShell script once per day.
State and logs are stored in:
C:\ProgramData\NetBirdDelayedUpdate\
state.json keeps the “aging” state, logs go into timestamped netbird-delayed-update-*.log files.
- ⏳ Version aging – only upgrades after a version has been stable in Chocolatey for
DelayDays. - 🕓 Daily scheduled task – runs once per day at a configurable time (default:
04:00). - 🎲 Optional random delay – spreads the actual execution time over a random window (
MaxRandomDelaySeconds). - 🧱 Local state tracking – remembers last seen repo version and when it was first observed.
- 🛑 No silent install – if NetBird is not installed locally, the script exits without doing anything.
- 📜 Detailed logs – logs each decision (first seen, still aging, upgraded, already up-to-date, etc.).
- 🧩 Single script – one PowerShell script handles install, uninstall and the actual update logic.
- ⚙️ Optional catch-up after missed runs –
-StartWhenAvailable/-rflag enables Task Scheduler option “Run task as soon as possible after a scheduled start is missed”.
- Windows Server 2019+ or Windows 10+
- PowerShell 5+
- Installed Chocolatey
- NetBird installed via Chocolatey:
choco install netbird -y- Optional: Git (for installation via
git clone) –
otherwise you can use "Download ZIP" on GitHub. - Administrator privileges (to create/remove scheduled tasks).
netbird-delayed-auto-update-windows/
├─ README.md
├─ LICENSE
└─ netbird-delayed-update.ps1
Open PowerShell as Administrator:
git clone https://github.com/NetHorror/netbird-delayed-auto-update-windows.git
cd netbird-delayed-auto-update-windows
# Install scheduled task with defaults:
# DelayDays=3, MaxRandomDelaySeconds=3600, DailyTime=04:00,
# run as SYSTEM with highest privileges.
.\netbird-delayed-update.ps1 -iIf you don't have Git installed, you can download the repository as a ZIP from GitHub
("Code" → "Download ZIP"), extract it and run:
cd C:\Path\To\netbird-delayed-auto-update-windows
# Install scheduled task:
.\netbird-delayed-update.ps1 -InstallAfter successful installation, you should see a scheduled task named:
NetBird Delayed Choco Update
in the Windows Task Scheduler.
The script has three modes:
- Run mode (default) – no
-Install/-Uninstall
Performs a single delayed-update check. This is what the scheduled task uses. - Install mode –
-Install/-i
Creates or updates the scheduled task that runs this script once per day. - Uninstall mode –
-Uninstall/-u
Removes the scheduled task (optionally state/logs).
Examples:
# Install scheduled task with defaults
.\netbird-delayed-update.ps1 -Install
# Install task that waits 5 days and uses no random delay
.\netbird-delayed-update.ps1 -Install -DelayDays 5 -MaxRandomDelaySeconds 0
# Install task that also runs as soon as possible after a missed schedule
.\netbird-delayed-update.ps1 -Install -StartWhenAvailable
# or shorter:
.\netbird-delayed-update.ps1 -i -r
# Install the task to run as the current user instead of SYSTEM
.\netbird-delayed-update.ps1 -Install -RunAsCurrentUserSupported options:
-DelayDays N– how many days a new Chocolatey NetBird version must stay unchanged before upgrade
(default:3).-MaxRandomDelaySeconds N– max random delay added after the scheduled time
(default:3600seconds).-DailyTime "HH:mm"– time of day (24h) when the task should start
(default:04:00).-TaskName NAME– name of the scheduled task (default:NetBird Delayed Choco Update).-RunAsCurrentUser– run the scheduled task as the current user instead ofSYSTEM.-PackageName NAME– Chocolatey package name (default:netbird).-StartWhenAvailable/-r– when used with-Install, enables Task Scheduler option
“Run task as soon as possible after a scheduled start is missed”. Useful for laptops that are often powered off at the scheduled time.-RemoveState– when used with-Uninstall, also remove the state/log directory.
Once per day, the scheduled task runs:
powershell.exe -NoProfile -ExecutionPolicy Bypass `
-File "C:\Path\To\netbird-delayed-update.ps1" `
-DelayDays <DelayDays> `
-MaxRandomDelaySeconds <MaxRandomDelaySeconds>On each run, the script:
- Optionally sleeps for a random delay between
0andMaxRandomDelaySecondsseconds. - Verifies that
choco(Chocolatey) is available inPATH. - Reads the locally installed NetBird version (Chocolatey package
PackageName). - Reads the repository (latest) NetBird version from Chocolatey.
- If NetBird is not installed locally, the script exits (no auto-install).
- Loads the current
state.jsonfromC:\ProgramData\NetBirdDelayedUpdate\, which contains:- last candidate version (
CandidateVersion), - when it was first seen (
FirstSeenUtc), - when it was last checked (
LastCheckUtc).
- last candidate version (
- If a new repo version appears (or there was no state before):
- logs that a new candidate version was detected,
- sets
FirstSeenUtcto now and starts the aging period.
- Computes the age in days of the candidate version.
- If
age < DelayDays:- logs that it is “too early to update” and exits without upgrade.
- If
- If
age ≥ DelayDaysand the local version is older than the candidate:-
logs the planned upgrade,
-
tries to stop the NetBird Windows service (if present),
-
runs:
choco upgrade netbird -y --no-progress
-
tries to start the NetBird service again,
-
saves updated state with the latest timestamps.
-
- If the local version is already
>=candidate version:- logs that no upgrade is required,
- updates the state timestamps and exits.
Short-lived or “bad” versions that are quickly replaced in Chocolatey are never deployed to your clients,
because they do not survive the DelayDays aging period.
When the script uses a random delay, the scheduled task may appear as:
Status = RunningLast Run Result = 0x41301(The task is currently running)
In this case it usually just means the script is sleeping inside the random delay window before doing its checks.
If you install the task without -StartWhenAvailable (default):
- if the machine is powered off at the scheduled time (e.g. 04:00),
- the run for that day is simply skipped,
- and the next run will happen at the next scheduled time.
If you install the task with -StartWhenAvailable / -r:
- Task Scheduler will also run the task as soon as possible after a missed start,
- for example, after the laptop is turned on in the morning,
- which is similar to
RunAtLoad=truebehaviour on macOS.
You can run the delayed-update logic manually without using Task Scheduler:
# Run immediately, no random delay, no "aging" period (for testing)
.\netbird-delayed-update.ps1 -DelayDays 0 -MaxRandomDelaySeconds 0This will:
- perform all checks,
- log the decisions,
- update
state.json, - and, if needed, run
choco upgrade netbird -y --no-progress.
Log files are stored in:
C:\ProgramData\NetBirdDelayedUpdate\
File names look like:
netbird-delayed-update-YYYYMMDD-HHMMSS.log
You can review these logs to see:
- when a candidate version was first observed,
- how long it aged,
- when an upgrade actually happened,
- any warnings or errors (Chocolatey not found, version parsing issues, etc.).
To remove the scheduled task (but keep state/logs):
cd netbird-delayed-auto-update-windows
.\netbird-delayed-update.ps1 -Uninstall
# or shorter:
# .\netbird-delayed-update.ps1 -uTo remove both the task and the state/logs directory:
cd netbird-delayed-auto-update-windows
.\netbird-delayed-update.ps1 -Uninstall -RemoveState
# or shorter:
# .\netbird-delayed-update.ps1 -u -RemoveStateNetBird itself is not removed – only the delayed update mechanism.