{"id":396,"date":"2026-06-09T08:09:03","date_gmt":"2026-06-09T08:09:03","guid":{"rendered":"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/"},"modified":"2026-08-21T22:24:00","modified_gmt":"2026-08-21T22:24:00","slug":"how-to-deploy-a-net-application-on-windows-vps-complete-tutorial","status":"publish","type":"post","link":"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/","title":{"rendered":"Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS"},"content":{"rendered":"<p class=\"wp-block-paragraph\">Deploying a .NET application to IIS comes down to three deliverables: the publish output, an application pool that matches the runtime, and a site binding with HTTPS in front of it. Get those three right and the app survives reboots, certificate renewals, and bad deploys. Get them wrong and you spend the afternoon chasing 500.30 startup errors. This guide walks through the exact sequence used on Windows Server 2019, 2022, and 2025, with the settings that actually matter and the errors you will most likely hit.<\/p>\n\n<h2 class=\"wp-block-heading\">1. Publish the right output<\/h2>\n\n<p class=\"wp-block-paragraph\">IIS does not compile your project; it runs what you publish. For an ASP.NET Core app, publish a folder, not a source tree. The two relevant modes:<\/p>\n\n<ul class=\"wp-block-list\"><li>Framework-dependent: small output, requires the ASP.NET Core Runtime installed on the server. Fine when you control the server (you do).<\/li><li>Self-contained: bundles the runtime, larger output, no server-side runtime install needed. Useful for locked-down environments.<\/li><\/ul>\n\n<p class=\"wp-block-paragraph\">Framework-dependent is the common choice on a dedicated Windows Server. From the project directory:<\/p>\n\n<pre class=\"wp-block-code\"><code>dotnet publish -c Release -o C:\\deploy\\myapp<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">For a self-contained build targeting Windows x64:<\/p>\n\n<pre class=\"wp-block-code\"><code>dotnet publish -c Release -r win-x64 --self-contained true -o C:\\deploy\\myapp<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">The output folder contains the application DLLs, the wwwroot static files, and a generated web.config. That web.config is what tells IIS how to launch the app, so treat it as part of the deploy, not an afterthought.<\/p>\n\n<h2 class=\"wp-block-heading\">2. Create the application pool<\/h2>\n\n<p class=\"wp-block-paragraph\">Open IIS Manager (inetmgr), go to Application Pools, and add a new pool for the app. Three settings matter most:<\/p>\n\n<ul class=\"wp-block-list\"><li>.NET CLR Version: select &#8220;No Managed Code&#8221;. For .NET Core and .NET 5+, the CLR is loaded by the app itself; a managed pool version only adds confusion.<\/li><li>Managed Pipeline Mode: Integrated. This is the default and the mode ASP.NET Core expects.<\/li><li>Identity: ApplicationPoolIdentity (default). It is a virtual account; grant it read permission on the publish folder (give the account named IIS AppPool\\myapp read rights on C:\\deploy\\myapp).<\/li><\/ul>\n\n<p class=\"wp-block-paragraph\">For production, adjust the defaults so the app does not idle out:<\/p>\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Setting<\/th><th>Default<\/th><th>Recommended for production<\/th><\/tr><\/thead><tbody><tr><td>.NET CLR Version<\/td><td>v4.0<\/td><td>No Managed Code (for .NET Core \/ .NET 5+)<\/td><\/tr><tr><td>Managed Pipeline Mode<\/td><td>Integrated<\/td><td>Integrated<\/td><\/tr><tr><td>Start Mode<\/td><td>OnDemand<\/td><td>AlwaysRunning<\/td><\/tr><tr><td>Idle Timeout (minutes)<\/td><td>20<\/td><td>0 (disabled)<\/td><\/tr><tr><td>Regular Time Interval (minutes)<\/td><td>1740<\/td><td>0 (use scheduled recycling instead)<\/td><\/tr><tr><td>Identity<\/td><td>ApplicationPoolIdentity<\/td><td>ApplicationPoolIdentity<\/td><\/tr><\/tbody><\/table><\/figure>\n\n<h2 class=\"wp-block-heading\">3. Create the site and bindings<\/h2>\n\n<p class=\"wp-block-paragraph\">In IIS Manager, right-click Sites and Add Website. Point the physical path at the publish folder, and set a binding with a host name. A host header keeps multiple sites on one IP cleanly separated, and it is required if you ever want to use SNI certificates. Bind port 443 with your certificate rather than leaving the site on port 80; add an HTTP-to-HTTPS redirect with the URL Rewrite module afterwards so nothing leaks over plaintext.<\/p>\n\n<h2 class=\"wp-block-heading\">4. web.config essentials<\/h2>\n\n<p class=\"wp-block-paragraph\">The publish process generates a web.config containing an aspNetCore element. For a framework-dependent app it looks like this:<\/p>\n\n<pre class=\"wp-block-code\"><code><configuration>\n  <location path=\".\" inheritInChildApplications=\"false\">\n    <system.webServer>\n      <handlers>\n        <add name=\"aspNetCore\" path=\"*\" verb=\"*\" modules=\"AspNetCoreModuleV2\" resourceType=\"Unspecified\" \/>\n      <\/handlers>\n      <aspNetCore processPath=\"dotnet\"\n                  arguments=\".\\myapp.dll\"\n                  stdoutLogEnabled=\"false\"\n                  stdoutLogFile=\".\\logs\\stdout\"\n                  hostingModel=\"inprocess\" \/>\n    <\/system.webServer>\n  <\/location>\n<\/configuration><\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">For a self-contained app, processPath points at the executable (myapp.exe) and arguments is empty. Keep hostingModel inprocess unless you have a specific reason to go out-of-process: in-process avoids the loopback hop and is measurably faster. Turn stdoutLogEnabled on while debugging, then off in production &#8211; the log file grows without bound otherwise.<\/p>\n\n<h2 class=\"wp-block-heading\">Publishing from a CI pipeline<\/h2>\n\n<p class=\"wp-block-paragraph\">dotnet publish behaves identically on a build agent, so the same folder output works from GitHub Actions, Azure DevOps, or a scheduled task on the server. Two common delivery paths: Web Deploy (MSDeploy) pushes the output and can recycle the pool automatically, or a simple file copy plus the app_offline.htm trick from the next section. Either way, the pipeline is the same three steps you just did by hand &#8211; publish, place files, recycle &#8211; which means the manual run only has to work once, and every deploy after that is scripted.<\/p>\n\n<h2 class=\"wp-block-heading\">5. Common deployment errors and fixes<\/h2>\n\n<p class=\"wp-block-paragraph\">These five errors account for the vast majority of broken first deploys:<\/p>\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Error<\/th><th>Typical cause<\/th><th>Fix<\/th><\/tr><\/thead><tbody><tr><td>HTTP 500.30 (In-Process Startup Failure)<\/td><td>App threw during startup &#8211; missing config, missing runtime, port conflict<\/td><td>Read the stdout log and Windows Application event log; fix the first exception listed<\/td><\/tr><tr><td>HTTP 500.31 (Failed to bind to address)<\/td><td>Kestrel could not bind the URL\/port<\/td><td>Remove or fix UseUrls \/ appsettings URLs; let IIS own the binding<\/td><\/tr><tr><td>HTTP 500.19 (Configuration error)<\/td><td>Malformed web.config or missing config section<\/td><td>Validate the XML; check the config error line reported on the page<\/td><\/tr><tr><td>HTTP 502.5 (Process Failure, out-of-process)<\/td><td>App process crashed on launch<\/td><td>Check stdout log; usually a missing runtime or an unhandled startup exception<\/td><\/tr><tr><td>404 for static files<\/td><td>StaticFileMiddleware missing or wwwroot not published<\/td><td>Ensure app.UseStaticFiles() exists and wwwroot is in the publish output<\/td><\/tr><\/tbody><\/table><\/figure>\n\n<h2 class=\"wp-block-heading\">6. Zero-downtime swaps<\/h2>\n\n<p class=\"wp-block-paragraph\">For routine updates, drop an app_offline.htm file into the site root. IIS detects it, drains in-flight requests, and stops the app; replace the files, delete app_offline.htm, and the app restarts. This gives you clean swaps without touching the pool or the binding. Combined with a scheduled (not time-interval) pool recycle, it keeps deployments predictable.<\/p>\n\n<p class=\"wp-block-paragraph\">If you are still deciding where the server should live, <a href=\"https:\/\/windows-vps.org\/#providers\">compare Windows VPS plans on our table<\/a> to see which providers pre-install recent Windows Server images and give you full admin access for IIS configuration. You can also <a href=\"https:\/\/windows-vps.org\">check our main site<\/a> for Windows Server VPS options across budget and managed tiers.<\/p>\n\n<p class=\"wp-block-paragraph\">Deploying to IIS is a pipeline, not a single click: publish, pool, bind, HTTPS, verify. Once the sequence is scripted, every future deploy is the same five minutes.<\/p>\n\n<p class=\"wp-block-paragraph\">Need a clean Windows Server instance to deploy against? <a href=\"https:\/\/vultr.com\/?ref=9804308-9J\" rel=\"noreferrer noopener sponsored\" target=\"_blank\">Vultr provisions a Windows Server VPS in about a minute<\/a> with hourly billing, so you can practice this exact publish-and-bind workflow before touching your production host.<\/p>","protected":false},"excerpt":{"rendered":"<p>Deploying a .NET application to IIS comes down to three deliverables: the publish output, an application pool that matches the runtime, and a site binding with HTTPS in front of it. Get those three right and the app survives reboots, certificate renewals, and bad deploys. Get them wrong and you spend the afternoon chasing 500.30 &#8230; <a title=\"Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS\" class=\"read-more\" href=\"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/\" aria-label=\"Read more about Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":7,"footnotes":""},"categories":[5],"tags":[],"class_list":["post-396","post","type-post","status-publish","format-standard","hentry","category-tutorials-guides"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS - Windows VPS Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS\" \/>\n<meta property=\"og:description\" content=\"Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS\" \/>\n<meta property=\"og:url\" content=\"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/\" \/>\n<meta property=\"og:site_name\" content=\"Windows VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-06-09T08:09:03+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-21T22:24:00+00:00\" \/>\n<meta name=\"author\" content=\"windows-vps\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"windows-vps\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/\",\"url\":\"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/\",\"name\":\"Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS - Windows VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/windows-vps.org\/blog\/#website\"},\"datePublished\":\"2026-06-09T08:09:03+00:00\",\"dateModified\":\"2026-08-21T22:24:00+00:00\",\"author\":{\"@id\":\"https:\/\/windows-vps.org\/blog\/#\/schema\/person\/44caceed916d0db318aa08d5623a7a58\"},\"breadcrumb\":{\"@id\":\"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/windows-vps.org\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/windows-vps.org\/blog\/#website\",\"url\":\"https:\/\/windows-vps.org\/blog\/\",\"name\":\"Windows VPS Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/windows-vps.org\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/windows-vps.org\/blog\/#\/schema\/person\/44caceed916d0db318aa08d5623a7a58\",\"name\":\"windows-vps\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/windows-vps.org\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/3f2573db5afcd1a6ab9abcc5d48fc8e42584bc87ab9d98cc156e5b2097766dd9?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/3f2573db5afcd1a6ab9abcc5d48fc8e42584bc87ab9d98cc156e5b2097766dd9?s=96&d=mm&r=g\",\"caption\":\"windows-vps\"},\"sameAs\":[\"https:\/\/windows-vps.org\/blog\"],\"url\":\"https:\/\/windows-vps.org\/blog\/author\/myxiechengxuan\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS - Windows VPS Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/","og_locale":"en_US","og_type":"article","og_title":"Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS","og_description":"Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS","og_url":"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/","og_site_name":"Windows VPS Blog","article_published_time":"2026-06-09T08:09:03+00:00","article_modified_time":"2026-08-21T22:24:00+00:00","author":"windows-vps","twitter_card":"summary_large_image","twitter_misc":{"Written by":"windows-vps","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/","url":"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/","name":"Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS - Windows VPS Blog","isPartOf":{"@id":"https:\/\/windows-vps.org\/blog\/#website"},"datePublished":"2026-06-09T08:09:03+00:00","dateModified":"2026-08-21T22:24:00+00:00","author":{"@id":"https:\/\/windows-vps.org\/blog\/#\/schema\/person\/44caceed916d0db318aa08d5623a7a58"},"breadcrumb":{"@id":"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/windows-vps.org\/blog\/how-to-deploy-a-net-application-on-windows-vps-complete-tutorial\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/windows-vps.org\/blog\/"},{"@type":"ListItem","position":2,"name":"Deploying .NET Apps on IIS: Publish, App Pool, and HTTPS"}]},{"@type":"WebSite","@id":"https:\/\/windows-vps.org\/blog\/#website","url":"https:\/\/windows-vps.org\/blog\/","name":"Windows VPS Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/windows-vps.org\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/windows-vps.org\/blog\/#\/schema\/person\/44caceed916d0db318aa08d5623a7a58","name":"windows-vps","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/windows-vps.org\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/3f2573db5afcd1a6ab9abcc5d48fc8e42584bc87ab9d98cc156e5b2097766dd9?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/3f2573db5afcd1a6ab9abcc5d48fc8e42584bc87ab9d98cc156e5b2097766dd9?s=96&d=mm&r=g","caption":"windows-vps"},"sameAs":["https:\/\/windows-vps.org\/blog"],"url":"https:\/\/windows-vps.org\/blog\/author\/myxiechengxuan\/"}]}},"_links":{"self":[{"href":"https:\/\/windows-vps.org\/blog\/wp-json\/wp\/v2\/posts\/396","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/windows-vps.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/windows-vps.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/windows-vps.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/windows-vps.org\/blog\/wp-json\/wp\/v2\/comments?post=396"}],"version-history":[{"count":5,"href":"https:\/\/windows-vps.org\/blog\/wp-json\/wp\/v2\/posts\/396\/revisions"}],"predecessor-version":[{"id":680,"href":"https:\/\/windows-vps.org\/blog\/wp-json\/wp\/v2\/posts\/396\/revisions\/680"}],"wp:attachment":[{"href":"https:\/\/windows-vps.org\/blog\/wp-json\/wp\/v2\/media?parent=396"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/windows-vps.org\/blog\/wp-json\/wp\/v2\/categories?post=396"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/windows-vps.org\/blog\/wp-json\/wp\/v2\/tags?post=396"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}