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
| Channel | Update model | Best for | Cost |
|---|---|---|---|
| ClickOnce | App checks a manifest URL and updates itself | Internal line-of-business apps with a known URL | Publish profile and a signed manifest |
| MSIX | Store or App Installer handles updates | Modern managed deployment, Store distribution | A certificate must be trusted by the machine |
| MSI | IT pushes a new version | Environments with deployment tooling | You build the installer |
| Self-contained folder or zip | You ship a new folder | Small teams, kiosks, remote users | Size and a manual swap |
| Single file | Same as a folder, one binary | Sharing a tool by link | Slower 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 PublishTrimmedPublishTrimmedand 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>- 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.
Foregroundupdates ask the user before restarting;Backgroundupdates silently and applies on next launch.- Check for updates in code when the app must know:
ApplicationDeployment.CurrentDeployment.CheckForUpdateAsync(). - A ClickOnce app runs with restricted file access - it cannot write beside its executable. Use
%APPDATA%. - 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 Fileswill not behave the same way. - Use
Package Support Frameworkor 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.Related
Files, settings and persistence Migrating from WinForms to WPF, WinUI or Blazor Hybrid
Last refreshed 2026-09-18.