wingettutorialpinversioning

How to Use winget pin to Lock App Versions

Stop winget from upgrading specific apps. Complete guide to winget pin add, pin list, pin remove, and pinning to a specific version range.

· 5 min read · updated May 29, 2026
How to Use winget pin to Lock App Versions

You want winget to keep most apps updated automatically — but not all of them. Maybe you need to stay on Node 20 LTS, or your team standardised on a specific Python version, or one app's latest release broke your workflow. That's what winget pin is for.

TL;DR

# Pin Node LTS at major version 20.x
winget pin add --id OpenJS.NodeJS.LTS --version "20.*"

# Pin Python 3.13 exactly
winget pin add --id Python.Python.3.13 --version "3.13.5"

# Now upgrade everything else
winget upgrade --all

Pinned apps get skipped silently. Other apps update normally.

What pin actually does

winget pin add adds an entry to the pin database that tells winget upgrade (without an explicit --id) to ignore that package. Three pin modes:

  • Pinning (default) — soft pin; bulk upgrades skip, manual upgrades still work
  • Version pin — locks to a specific version or range
  • Gating — hard pin; ANY upgrade attempt fails until you unpin

Step 1 — Pin a single app

The simplest case: stop winget upgrade --all from touching a specific app.

winget pin add --id Microsoft.PowerToys

Now winget upgrade will skip PowerToys until you unpin. Confirm:

winget pin list

Output:

Name                   Id                       Version   Pin Type
------------------------------------------------------------------
PowerToys              Microsoft.PowerToys      0.92.1    Pinning

Step 2 — Pin to a specific version

Use --version to fix at a single version:

winget pin add --id OpenJS.NodeJS.LTS --version "20.18.0"

Now winget upgrade will only upgrade to 20.18.0 — never above.

Step 3 — Pin to a version range (wildcards)

The most useful flavour. Pin to "stay on the 20.x LTS line but get patches":

winget pin add --id OpenJS.NodeJS.LTS --version "20.*"

Now winget will upgrade 20.17.0 → 20.18.0 → 20.19.0 but refuse to jump to 22.x.

Other examples:

# Stay on Python 3.13.x
winget pin add --id Python.Python.3.13 --version "3.13.*"

# Pin to anything below 2.0 (legacy app)
winget pin add --id Some.Legacy.App --version "1.*"

# Pin to a major + minor range
winget pin add --id App.Tool --version "1.5.*"

Step 4 — Gating (hard pin)

If you want to completely block ANY upgrade, even manual ones, use --gated:

winget pin add --id Critical.Server.Tool --gated

Now even winget upgrade --id Critical.Server.Tool will fail with "Package has been pinned". To upgrade, you'd need to remove the pin first. Useful for production servers where uncontrolled upgrades are a release-management problem.

Step 5 — List and remove pins

List all pins:

winget pin list

Remove a specific pin:

winget pin remove --id OpenJS.NodeJS.LTS

Remove all pins (nuclear option):

winget pin reset --force

Real-world recipes

Recipe 1 — LTS Node + Python

Many dev teams standardise runtime versions. Pin them once:

winget pin add --id OpenJS.NodeJS.LTS    --version "20.*"
winget pin add --id Python.Python.3.13   --version "3.13.*"

Then your team's daily winget upgrade --all won't accidentally bump to Node 22 mid-sprint.

Recipe 2 — Freeze IDE for a project

You're shipping a build with VS Code 1.118 for compatibility reasons:

winget pin add --id Microsoft.VisualStudioCode --version "1.118.*" --gated

Gating means even an explicit upgrade attempt fails — forces a conscious decision to unpin.

Recipe 3 — Pin Microsoft system apps

If you don't want winget upgrading critical Microsoft-bundled apps:

winget pin add --id Microsoft.PowerShell
winget pin add --id Microsoft.WindowsTerminal

These will stay on whatever Microsoft Store / Windows Update gives you, and winget won't override.

Recipe 4 — Bulk pin from a file

Save IDs to pins.txt:

OpenJS.NodeJS.LTS
Python.Python.3.13
Microsoft.PowerShell

Apply:

Get-Content pins.txt | ForEach-Object {
  winget pin add --id $_
}

What pinning doesn't do

  • Doesn't lock the running installer version — only what winget upgrade will install going forward. If someone manually downloads a new installer from the publisher's site and runs it, winget can't stop them.
  • Doesn't roll back — pinning to 1.0.0 when you've already installed 2.5.0 just means winget won't upgrade further. To revert, uninstall and reinstall the older version.
  • Doesn't sync across machines — pins are local. If you want a team-wide pin policy, distribute the settings.json or use a DSC configuration.

Combining with import/export

Pins are saved in your winget settings, NOT in the export JSON. So winget export followed by winget import on a new machine doesn't transfer pins.

Workaround: copy your settings file:

%LOCALAPPDATA%\Microsoft\WinGet\Settings\settings.json

Or script the pins as part of your machine setup:

# setup-pins.ps1
winget pin add --id OpenJS.NodeJS.LTS  --version "20.*"
winget pin add --id Python.Python.3.13 --version "3.13.*"

Run this after winget import on every new machine.

Common errors and fixes

"Package already pinned"

You're trying to pin something already pinned. Remove the existing pin first:

winget pin remove --id <ID>
winget pin add --id <ID> --version "<new spec>"

"Package upgrade was pinned"

This is the error you'll see during winget upgrade --all for a gated pin — and it's exactly the behaviour you wanted. To upgrade anyway:

winget pin remove --id <ID>
winget upgrade --id <ID>

"Pin not found"

The pin ID doesn't exist. List pins with winget pin list to see exact IDs.

Building a reproducible setup?
winget.tech generates install scripts for new machines — pair with pins for fully version-locked dev environments.
Open Browse →

Why use pins at all?

Most users don't bother — they let winget upgrade --all do its thing and accept new versions. But these scenarios make pins worth it:

  • LTS dependencies — Node 20.x, Python 3.13.x, Java 21 LTS
  • Production servers — upgrade only on a release cadence, not on a developer whim
  • Compatibility frozen — your tooling broke at version N+1 and you're not ready to debug
  • Team standardisation — everyone on the same version for reproducible builds
  • Recovery — you got burned by an upgrade and want a buffer to evaluate before adopting

If none of those apply, skip pins and run plain winget upgrade --all weekly. If any do, pin the relevant apps and sleep better.

What's next?

Continue reading