Deployment: ClickOnce, MSIX and single-file

ClickOnce updates, MSIX packaging and signing, single-file and self-contained publishing, and how to pick the channel that fits the users you support.

The channels

ChannelUpdate modelBest forCost
ClickOnceApp checks a manifest URL and updates itselfInternal line-of-business apps with a known URLPublish profile and a signed manifest
MSIXStore or App Installer handles updatesModern managed deployment, Store distributionA certificate must be trusted by the machine
MSIIT pushes a new versionEnvironments with deployment toolingYou build the installer
Self-contained folder or zipYou ship a new folderSmall teams, kiosks, remote usersSize and a manual swap
Single fileSame as a folder, one binarySharing a tool by linkSlower first start; still unzips internally
# framework-dependent: small, needs the runtime installed
dotnet publish -c Release -r win-x64 --self-contained false -o out

# self-contained single file: no runtime needed, larger output
dotnet publish -c Release -r win-x64 --self-contained true \
  -p:PublishSingleFile=true \
  -p:IncludeNativeLibrariesForSelfExtract=true \
  -p:EnableCompressionInSingleFile=true \
  -o out

# trimmed is not supported for WinForms - do not add PublishTrimmed
  • PublishTrimmed and WinForms do not mix; the designer-driven reflection use is trimmed away and the app fails at runtime, not at build.
  • ReadyToRun reduces startup time at the cost of a much larger binary. Measure before you ship it.
  • A self-contained build is around 150 MB uncompressed before compression; put it in a zip or an installer rather than shipping the folder as-is.

ClickOnce

<!-- in the .csproj -->
<PropertyGroup>
  <PublishUrl>\\fileserver\apps\OrderTool\</PublishUrl>
  <InstallUrl>https://apps.example.com/ordertool/</InstallUrl>
  <UpdateMode>Foreground</UpdateMode>
  <UpdateInterval>7</UpdateInterval>
  <UpdateIntervalUnits>Days</UpdateIntervalUnits>
  <ApplicationRevision>3</ApplicationRevision>
  <ApplicationVersion>1.4.0.*</ApplicationVersion>
  <SignManifests>true</SignManifests>
</PropertyGroup>
  1. Each publish writes a new version folder plus a deployment manifest that points at it - the old versions stay, so a rollback is one manifest edit.
  2. Foreground updates ask the user before restarting; Background updates silently and applies on next launch.
  3. Check for updates in code when the app must know: ApplicationDeployment.CurrentDeployment.CheckForUpdateAsync().
  4. A ClickOnce app runs with restricted file access - it cannot write beside its executable. Use %APPDATA%.
  5. Changing the signing certificate forces every user to reinstall, so the certificate must be valid for the lifetime of the application.

ClickOnce's real advantage is that the update path is part of the manifest, not a separate updater you maintain. Its real limitation is that the sandbox assumptions and the URL structure make it unsuitable for anything that needs to modify its own installation.

MSIX packaging

<!-- Package.appxmanifest fragment -->
<Identity Name="Contoso.OrderTool" Publisher="CN=Contoso Ltd, O=Contoso Ltd"
          Version="1.4.0.0" ProcessorArchitecture="x64" />
<Properties>
  <DisplayName>Order tool</DisplayName>
  <PublisherDisplayName>Contoso Ltd</PublisherDisplayName>
  <Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Applications>
  <Application Id="App" Executable="OrderTool.exe" EntryPoint="Windows.FullTrustApplication">
    <uap:VisualElements DisplayName="Order tool" Description="Order management"
        BackgroundColor="transparent" Square150x150Logo="Assets\Logo.png" Square44x44Logo="Assets\Small.png" />
  </Application>
</Applications>
  • MSIX installs per user and uninstalls cleanly, which is the property IT departments actually want.
  • Signing is mandatory for sideloading. The certificate must chain to a root the target machine trusts, or installation fails with an error most users cannot interpret.
  • File system and registry writes are virtualised into the package container; a tool that patches files in Program Files will not behave the same way.
  • Use Package Support Framework or fix the app when it writes to its own install directory.
💡
Pick one channel and automate it in CI. The most common deployment failure is not a bad installer - it is a build produced by hand on one developer's machine that nobody can reproduce.

FAQ

Why does my single-file app take so long to start?
The host extracts native libraries to a temporary directory on first run. IncludeNativeLibrariesForSelfExtract trades startup time for a truly single artefact - test both against your startup budget.
Can I update a ClickOnce app on a schedule?
Yes. UpdateInterval plus the update mode drives automatic checks. For a forced update, check in code and refuse to continue until the new version has been applied.

Files, settings and persistence Migrating from WinForms to WPF, WinUI or Blazor Hybrid

Last refreshed 2026-09-18.