WinGet Guides

Private WinGet Source: How to Host Your Own Repository

WinGet, the Windows Package Manager CLI, brings simple, scriptable installs, upgrades, and uninstalls to Windows. In enterprise and team environments, the most powerful capability isn’t just consuming public catalogs—it’s running your own private WinGet source. A private repository gives you full control over what software is available, how it’s packaged, and when updates are released. In this hands‑on guide you’ll learn exactly how to host your own WinGet repository, add it as a source, publish packages, automate updates, and troubleshoot issues. You’ll get practical examples, ready‑to‑use commands, YAML samples, and scripts you can adapt for your environment.

Overview of the Use Case (en heading H2)

What a private WinGet source does, in plain language

  • A private WinGet source is your organization’s own catalog of packages that users access via the WinGet CLI. It hosts package manifests (YAML) and references installers you control (internal mirrors or vendor URLs).
  • Users can search, install, upgrade, and uninstall software from your private catalog using the same familiar WinGet commands they use for the public sources—except now the catalog is curated by you.

Why this matters

  • Security and compliance: Only approved software is available. You can mirror installers internally, hash them, and gate access behind your security perimeter.
  • Reliability and performance: Faster installs/updates from your LAN/CDN; no dependency on public service uptime.
  • Consistency and automation: Pin versions, enforce silent install flags, and coordinate rollouts via CI/CD, Intune, or device management tools.

Common scenarios where a private WinGet repository shines

  • Fresh device provisioning: Use a curated baseline of apps during imaging or autopilot onboarding.
  • Bulk updates and maintenance: Run one command to upgrade all apps across hundreds or thousands of devices.
  • CI/CD automation: Publish new versions when your product releases; automatically update manifests.
  • Lab, VDI, and offline/low‑bandwidth deployments: Mirror installers behind the firewall or on an internal share.
  • Regulated environments: Maintain auditable manifests and hashes to meet change‑control requirements.

Quick Reference Table (en heading H2)

Command Purpose Example Output
winget –info Show WinGet version and system info Windows Package Manager v1.6.x…; System architecture: x64
winget source list List configured sources Name Arg Type; winget https://… Microsoft.PreIndexed; msstore https://… Microsoft.Store; PrivateRepo https://repo.contoso.local/api Microsoft.Rest
winget source add –name PrivateRepo –type Rest –arg https://repo.contoso.local/api Add your private REST source Added source: PrivateRepo
winget source update –name PrivateRepo Refresh catalog metadata Updating source: PrivateRepo; Done
winget source reset –force Reset sources to defaults (repair) Resetting sources… Done
winget search myapp –source PrivateRepo Search for a package in your repo Name Id Version Source; Contoso MyApp Contoso.MyApp 1.2.3 PrivateRepo
winget show Contoso.MyApp –source PrivateRepo Show manifest details Id: Contoso.MyApp; Version: 1.2.3; InstallerType: msi; InstallerUrl: https://…
winget install –id Contoso.MyApp –source PrivateRepo –silent Install from your repo Successfully installed
winget upgrade –source PrivateRepo –all –silent Upgrade all packages from your repo No applicable upgrades found OR list of upgraded packages
winget validate –manifest .\manifests\Contoso.MyApp.yaml Validate YAML manifest Manifest validation succeeded
winget hash .\MyApp-x64.msi Compute SHA256 for manifest SHA256: ABCDEF…1234
winget export -o .\baseline.json –include-versions Export installed apps Wrote export file to .\baseline.json
winget import -i .\baseline.json –source PrivateRepo –accept-package-agreements –accept-source-agreements Reinstall baseline from your repo Installing packages listed in baseline.json…

Key Concepts and Prerequisites (en header H2)

Key concepts

  • Source types: WinGet can use different source types. For private hosting, the recommended model is a REST source (Type: Microsoft.Rest), which supports search, show, install, and upgrade.
  • Manifests: YAML files describe packages—IDs, versions, installer URLs, checksums, silent switches, dependencies, etc.
  • Installers: Actual binaries (MSI, EXE, MSIX, ZIP, portable) hosted at internal or vendor URLs. WinGet downloads and verifies them via SHA256.
  • Search/upgrade integration: With a REST source, your packages integrate into winget search/upgrade workflows just like public sources.

Prerequisites

  • WinGet (Windows Package Manager) installed via App Installer (Microsoft Store) on Windows 10 1809+ or Windows 11. Recommended WinGet CLI v1.4+ (ideally 1.6+).
  • PowerShell available (5.1+ or PowerShell 7+).
  • Administrative rights for system-wide installs and source configuration on managed devices (recommended).
  • HTTPS endpoint for your private REST service (TLS certificate configured).
  • Storage for manifests and installers (Git repo + CI/CD + object storage/CDN or web server).

How to check your WinGet version

  • Command: winget –info
  • You should see Windows Package Manager v1.4.x or higher. If not, update the App Installer package from the Microsoft Store or your enterprise channel.

Step-by-Step Guide (en heading H2)

This guide uses the recommended REST source model. You’ll deploy a REST API, create manifests, publish them, and connect clients.

Option A (recommended): Host a REST source

H3: 1) Plan your repository and network architecture

  • Choose your host: Windows Server with IIS/Kestrel, Linux with Nginx/Apache, or a managed PaaS (App Service). The REST service is a standard HTTPS API.
  • Storage:
    • Manifests: Keep them in a Git repository for version control.
    • Installers: Prefer internal object storage, NAS, or web server under your control. Mirror vendor binaries if allowed and keep checksums updated.
  • Access control:
    • Internal only: Restrict via firewall/VPN.
    • Authenticated: Reverse proxy with auth (e.g., mTLS, OAuth, header‑based auth). You can place an API gateway in front.
  • DNS and TLS:
    • Create a DNS name like repo.contoso.local or packages.contoso.com.
    • Install a trusted TLS cert on the endpoint.
See also  winget configure Explained: Rebuild Any PC from a Single YAML

H3: 2) Deploy the WinGet REST source

  • Use the official reference implementation for the Windows Package Manager REST source (often called “winget-rest-source”). It’s a .NET web API that implements the WinGet source contract.
  • Deployment outline (generic)
    1. Prepare a host (Windows or Linux) with .NET 6+ runtime (or container host).
    2. Publish the REST service:
      • If using source code: dotnet publish -c Release, then host behind IIS/Kestrel or systemd.
      • If using a container image: run it behind a reverse proxy with TLS.
    3. Configure environment variables or appsettings for:
      • Base URL (e.g., https://repo.contoso.local/api)
      • Storage/metadata backend for packages and versions (database or files, depending on implementation)
      • Read vs. write permissions (set write to CI/CD only; read for clients)
    4. Test health: curl https://repo.contoso.local/api/information

Notes:

  • The REST schema exposes endpoints for searching packages, retrieving manifests, and version queries. Make sure the app responds with 200 OK on /information and supports the manifest search endpoints.

H3: 3) Create a package manifest (YAML)
You can write manifests manually or use a helper (e.g., the wingetcreate tool) to scaffold them. Below is a full singleton YAML example with inline comments.

Example: .\manifests\Contoso.MyApp.yaml

A simple singleton manifest for WinGet

Save as: Contoso.MyApp.yaml (for demonstration; production repos often use multi-file manifests)

Id: Contoso.MyApp # Unique ID (Publisher.App)
Version: 1.2.3 # Semantic version
Name: Contoso MyApp
Publisher: Contoso Ltd.
Author: Contoso Ltd.
License: Proprietary
LicenseUrl: https://contoso.example.com/myapp/license
Homepage: https://contoso.example.com/myapp
AppMoniker: myapp
Description: “Contoso MyApp is a sample enterprise tool.”
Tags:

Define how to install (MSI example)

InstallerType: msi # exe | msi | msix | zip | inno | nullsoft | burn | portable
Scope: machine # machine | user
InstallModes:

  • silent
  • silentWithProgress
    InstallerSwitches: # Silent flags are critical for automation
    Silent: /qn
    SilentWithProgress: /qb
    Custom: TRANSFORMS=MyApp.mst

If MSI, include ProductCode to help WinGet track upgrades

ProductCode: ‘{12345678-90AB-CDEF-1234-567890ABCDEF}’
UpgradeBehavior: install

Expected return codes (optional but recommended for robust automation)

InstallerSuccessCodes:

  • 3010 # MSI: restart required

Define installers for each architecture/locale you support

Installers:

  • Architecture: x64
    InstallerUrl: https://repo.contoso.local/content/myapp/1.2.3/MyApp-x64.msi
    InstallerSha256: ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890
    SignatureSha256: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF # optional if you sign
    NestedInstallerType: none
    AppsAndFeaturesEntries:
    • DisplayName: Contoso MyApp
      Publisher: Contoso Ltd.
      ProductCode: ‘{12345678-90AB-CDEF-1234-567890ABCDEF}’
      InstallerType: msi

Commands:

  • myapp

Manifest metadata

ManifestType: singleton
ManifestVersion: 1.6.0

Best practices for authoring manifests

  • Always set InstallerSha256 for every installer. Compute it with: winget hash .\MyApp-x64.msi
  • Use Scope: machine for enterprise apps needing admin install; pair with –silent in your scripts.
  • Provide ProductCode (MSI) so WinGet can detect installed versions and upgrades.
  • Include correct InstallerSwitches for silent/silentWithProgress. For MSI: /qn or /qb; for EXE, consult vendor docs.

H3: 4) Validate your manifest locally
Use WinGet’s validation tools before publishing:

Validate YAML schema and basic rules

winget validate –manifest .\manifests\Contoso.MyApp.yaml

Compute and verify file hashes

winget hash .\MyApp-x64.msi

Make sure the SHA256 in the manifest matches the installer file you intend to host.

H3: 5) Publish manifests and installers to your repository

  • Upload installers to your internal web server or object store (the URL used in InstallerUrl).
  • Publish the manifest(s) to the storage backend that your REST service uses, or push them to a Git repo and run a pipeline that synchronizes manifests into the REST source’s store.
  • Common CI/CD approach:
    • When a release is tagged, build artifacts (MSI/EXE).
    • Compute SHA256, update YAML manifest automatically.
    • Validate manifests.
    • Publish installers to https://repo.contoso.local/content/… and push manifests to the REST backend.
    • Trigger a cache refresh (if your implementation has a reindex step).
See also  How to Pin App Versions with WinGet for Stable Environments

H3: 6) Add the private source on clients
On any device that should consume your repo:

Optional: verify WinGet availability

winget –info

Add your private REST source (name and URL to your service)

winget source add –name PrivateRepo –type Rest –arg https://repo.contoso.local/api

Confirm it shows up

winget source list

Update metadata (good practice after adding)

winget source update –name PrivateRepo

H3: 7) Install, search, upgrade from your private source
Try these end‑to‑end commands:

Search for a package

winget search myapp –source PrivateRepo

Show details (verify manifest fields)

winget show Contoso.MyApp –source PrivateRepo

Install silently (perfect for automation)

winget install –id Contoso.MyApp –source PrivateRepo –silent –accept-package-agreements –accept-source-agreements

Upgrade from your repo (all apps your source tracks)

winget upgrade –source PrivateRepo –all –silent –accept-package-agreements –accept-source-agreements

Uninstall (if supported)

winget uninstall –id Contoso.MyApp

Option B: Use local manifests without a source (quick and offline-friendly)
If you don’t need search/upgrade integration and just want repeatable installs:

  • Place YAML manifests and installers on a file share.
  • Install directly from YAML:

winget install -m \fileserver\manifests\Contoso.MyApp.yaml –silent –accept-package-agreements

This is handy for offline or ad‑hoc scenarios, but you won’t get source‑backed search/upgrade.

Troubleshooting (en heading H2)

Common errors and how to fix them

H3: Hash mismatch (InstallerSha256 mismatch)
Symptoms: WinGet errors that the installer hash does not match the manifest.

  • Causes: Installer file changed, wrong URL, or incorrect SHA.

  • Fix:

    1. Download the exact file referenced by InstallerUrl and compute its hash:

      winget hash .\MyApp-x64.msi

    2. Update InstallerSha256 in the YAML to the correct value.

    3. Re‑publish the manifest and update source metadata:

      winget source update –name PrivateRepo

H3: Multiple installers found / ambiguous match
Symptoms: Search or install finds multiple packages or architectures.

  • Fix:

    • Specify the Id and –exact if needed:

      winget install –id Contoso.MyApp –source PrivateRepo –exact –silent

    • Ensure your manifest defines Architecture for each installer and avoid overlapping criteria.

    • Include ProductCode (MSI) and consistent naming to help detection.

H3: Source not responding / index errors
Symptoms: winget source update fails; search returns no results.

  • Fix:

    • Verify endpoint health: curl https://repo.contoso.local/api/information

    • Check TLS cert validity and firewall rules.

    • Refresh sources:

      winget source update –name PrivateRepo

    • As a last resort, reset sources:

      winget source reset –force

    • Then re‑add your source.

H3: 401/403 Unauthorized when accessing a private source

  • Fix:
    • Confirm the device is on the allowed network or VPN.
    • If you protect the endpoint behind an auth proxy, configure client trust (certs, device auth) or allow anonymous GET for read‑only package queries.
    • Validate time skew and TLS configuration.

H3: Install fails silently in automation

  • Fix:
    • Ensure correct InstallerSwitches. For MSI, use /qn for true silent; many EXE installers need vendor‑specific silent flags.
    • Add –silent –accept-package-agreements –accept-source-agreements to non‑interactive scripts.
    • Check exit codes. Add InstallerSuccessCodes for non‑zero “success” codes like 3010 (restart required).

H3: Upgrades not detected

  • Fix:

    • Verify Version increments in manifests.

    • For MSI, ensure ProductCode reflects upgrades (same or changed depending on MSI scheme) and that UpgradeCode/ProductVersion rules are correct.

    • Run:

      winget upgrade –source PrivateRepo

Automation Tips (en heading H2)

H3: PowerShell bootstrap for devices
Use a single script to configure the source and install a baseline.

Example: Add the source idempotently and install a baseline list

Add private source if missing

$srcName = “PrivateRepo”
$srcUrl = “https://repo.contoso.local/api
$exists = winget source list | Select-String -SimpleMatch $srcName

if (-not $exists) {
winget source add –name $srcName –type Rest –arg $srcUrl
} else {
winget source update –name $srcName
}

Optional: install a baseline set (by Id)

$packages = @(
“Contoso.MyApp”,
“Contoso.Tools.CLI”,
“Contoso.Viewer”
)

foreach ($pkg in $packages) {
winget install –id $pkg –source $srcName –silent –accept-package-agreements –accept-source-agreements
}

H3: Scheduled Task to auto‑upgrade nightly

  1. Save a script (Upgrade-PrivateRepo.ps1):

winget source update –name PrivateRepo
winget upgrade –source PrivateRepo –all –silent –accept-package-agreements –accept-source-agreements

  1. Create a scheduled task to run as SYSTEM or with admin rights at 3 AM daily.

H3: Intune device script integration

  • Push a PowerShell script that:
    • Adds the PrivateRepo source.
    • Installs required packages with –silent.
    • Optionally runs a periodic winget upgrade –all from your source.
  • Scope devices via dynamic groups to control rollout waves.

H3: CI/CD pipeline to publish packages

  • On release:
    • Build your MSI/EXE artifacts.
    • Compute SHA256: winget hash path\to\installer.ext
    • Update manifest YAML (bump Version, URLs, hashes).
    • Validate: winget validate –manifest path\to\yaml
    • Publish installers to your internal content store.
    • Publish manifest(s) to the REST source storage.
    • Trigger your REST source to refresh (if your implementation caches results).

H3: Offline and low‑bandwidth sites

  • Mirror installers to a local file server in each site and point InstallerUrl to the local mirror (per site DNS).
  • Pre‑stage downloads overnight using a management script.
  • For completely offline segments, use local YAML with winget install -m path\to\yaml.

Best Practices (en heading H2)

H3: Governance and security

  • Only host trusted installers and verify their hashes. Always update InstallerSha256 after any rebuild.
  • Serve your REST source over HTTPS with a valid certificate.
  • Control access using network ACLs, VPN, or an auth‑enabled reverse proxy.
  • Separate read (client) and write (publisher) paths; CI/CD should be the only writer.
See also  WinGet vs Chocolatey vs Scoop (2025): What to Use and When

H3: Manifest quality and consistency

  • Use consistent IDs: Publisher.App (e.g., Contoso.MyApp). Don’t rename IDs once in use.
  • Keep manifests small and focused. For complex apps, consider multi‑file manifests (installer.yaml, locale.yaml, version.yaml) following WinGet schema guidance.
  • Include silent install switches and expected return codes to avoid hung installs in unattended runs.
  • Specify Architecture for each installer and provide both x64 and arm64 where applicable.
  • Include ProductCode for MSI packages and AppsAndFeaturesEntries to aid detection.

H3: Versioning and pinning

  • Use strict semantic versions and avoid reusing a version number for different binaries.

  • For baselines, export with versions and re‑import to pin:

    winget export -o .\baseline.json –include-versions
    winget import -i .\baseline.json –source PrivateRepo –accept-package-agreements –accept-source-agreements

H3: Repository hygiene

  • Remove or archive deprecated versions after a deprecation window.
  • Keep release notes and metadata updated; include ReleaseNotes and ReleaseNotesUrl.
  • Test installs in a staging environment before promoting manifests to production.

H3: Operational excellence

  • Monitor your REST source health (availability, latency).
  • Log package installs and upgrades on endpoints for auditability.
  • Document app‑specific switches and post‑install tasks (e.g., licensing) in team runbooks.

Conclusion (en heading H2)

Hosting a private WinGet source gives you reliable, secure, and automated software deployment across Windows fleets. You learned how to:

  • Deploy a REST source and secure it behind HTTPS.
  • Author high‑quality YAML manifests with correct hashes and silent flags.
  • Publish packages and add your source to clients.
  • Automate installs and upgrades via PowerShell, Task Scheduler, Intune, or CI/CD.
  • Diagnose and fix common issues fast.

Try the commands in this guide on a test machine, starting with winget source add and winget search. With careful manifests and a solid publishing pipeline, WinGet becomes a safe and powerful foundation for managing Windows software at scale.

FAQ Section (en heading H2)

H4: Can I host installers on an internal server and still use WinGet?
Yes. Set InstallerUrl in your manifest to an internal HTTPS endpoint (e.g., https://repo.contoso.local/content/…). Ensure the endpoint is reachable from clients and that InstallerSha256 matches the hosted file. This is a common pattern for bandwidth control and security.

H4: Do I have to use a REST source, or can I install directly from YAML?
You can install directly from local or network YAML using winget install -m path\to\manifest.yaml. However, without a source you won’t get integrated search/upgrade. For a full catalog experience, use a REST source.

H4: How do I handle packages that use custom EXE installers?
Declare InstallerType: exe and provide the correct silent switches under InstallerSwitches. Common examples include /S, /silent, /verysilent, or vendor‑specific flags. Always test unattended installs, and add non‑zero success codes if needed.

H4: How can I pin versions or prevent automatic upgrades?
Export a baseline with versions and re‑import it for consistent deployments:

  • winget export -o .\baseline.json –include-versions
  • winget import -i .\baseline.json –source PrivateRepo
    For strict pinning, avoid running blanket winget upgrade –all on those devices, or curate your repo so only approved versions are present.

H4: What about portable apps and ZIP installers?
WinGet supports portable and zip types. For ZIP, provide extraction behavior using manifest keys appropriate for your schema version. For portable apps, specify Installers with InstallerType: portable, and define the executable and path actions as required by the schema. Always include a valid InstallerSha256 and test silent behavior if applicable.

Appendix: Additional ready‑to‑use snippets (en heading H2)

H3: Verify and re‑publish a new version end‑to‑end

1) Build artifacts and place MyApp-x64.msi at .\dist\MyApp-x64.msi

2) Compute hash

$hash = (winget hash .\dist\MyApp-x64.msi | Select-String ‘SHA256:’ ).ToString().Split()[1]

3) Update manifest (simple example; in CI, edit YAML via a script)

(Get-Content .\manifests\Contoso.MyApp.yaml) -replace ‘Version: .‘,’Version: 1.2.4’ `
-replace ‘InstallerSha256: .
‘,”InstallerSha256: $hash” | Set-Content .\manifests\Contoso.MyApp.yaml

4) Validate

winget validate –manifest .\manifests\Contoso.MyApp.yaml

5) Publish installer to your content store and manifest to the REST backend (implementation-specific)

6) Refresh clients’ view

winget source update –name PrivateRepo

H3: Baseline deployment on a new device

Ensure source is present

winget source add –name PrivateRepo –type Rest –arg https://repo.contoso.local/api

Install baseline using a JSON export created earlier

winget import -i \share\baselines\standard.json –source PrivateRepo –accept-package-agreements –accept-source-agreements

H3: Silent install and logging

MSI example with logging

winget install –id Contoso.MyApp –source PrivateRepo –silent –override “/qn /L*v C:\Temp\MyAppInstall.log” –accept-package-agreements

Tip: For MSI packages, you can pass vendor flags via –override. For EXE installers, ensure the switches are correct; combine with –silent for non‑interactive runs.

You now have everything you need to design, deploy, and operate a private WinGet source—from manifests and hosting to automation and troubleshooting. With a bit of upfront setup and good manifest hygiene, WinGet becomes a robust, enterprise‑grade delivery mechanism for your Windows applications.

About the author

Jonathan Dudamel

Jonathan Dudamel

I'm Jonathan Dudamel, an experienced IT specialist and network engineer passionate about all things Windows. I have deep expertise in Microsoft project management, virtualization (VMware ESXi and Hyper-V), and Microsoft’s hybrid platform. I'm also skilled with Microsoft O365, Azure ADDS, and Windows Server environments from 2003 through 2022.

My strengths include Microsoft network infrastructure, VMware platforms, CMMS, ERP systems, and server administration (2016/2022).