{"id":6001,"date":"2026-11-03T12:24:55","date_gmt":"2026-11-03T09:24:55","guid":{"rendered":"https:\/\/www.dchost.com\/blog\/?p=6001"},"modified":"2026-11-04T00:20:23","modified_gmt":"2026-11-03T21:20:23","slug":"deploy-aspnet-core-iis-windows-vps","status":"publish","type":"post","link":"https:\/\/www.dchost.com\/blog\/en\/deploy-aspnet-core-iis-windows-vps\/","title":{"rendered":"Deploy ASP.NET Core on a Windows VPS with IIS: Hosting Bundle, App Pools and Startup Checks"},"content":{"rendered":"<div class=\"dchost-blog-content-wrapper\"><div class=\"aiw-summary\">\n<p class=\"aiw-box-title\">Quick summary<\/p>\n<p>To deploy ASP.NET Core on a Windows VPS (virtual private server), install IIS, Windows&#8217; web server, and the Hosting Bundle for your application&#8217;s .NET version. Point an IIS site to your published files, set folder permissions and enable HTTPS so visitors can reach your app over an encrypted connection.<\/p>\n<ul>\n<li>The Hosting Bundle provides the .NET runtime-the components that run your app-and the module that connects it to IIS.<\/li>\n<li>Transfer Release output, the deployment-ready build, rather than source code.<\/li>\n<li>Use a dedicated IIS application pool to run your app under its own Windows identity, with only the folder permissions it needs.<\/li>\n<li>Check HTTPS from outside the VPS and save a copy of the current release before updates so you can roll back.<\/li>\n<\/ul>\n<\/div>\n<p>If your app works locally but fails on the VPS, the cause may be the server setup rather than your code. A missing runtime, an incorrect path to the published files or insufficient file permissions can prevent startup. Check the server components first, then the published files and access settings to narrow down the cause.<\/p>\n<p><strong>Example scenario:<\/strong> this hypothetical walkthrough uses a database-free application with IIS 10 and .NET 10 on Windows Server 2022. You&#8217;ll publish the app, make it accessible through your domain over HTTPS, learn how to investigate startup errors and prepare an update process that lets you restore the previous release.<\/p>\n<div id=\"toc_container\" role=\"navigation\" aria-label=\"Table of Contents\" data-nosnippet class=\"toc_transparent no_bullets toc_numbered toc_title_center\"><p class=\"toc_title\">\u0130\u00e7indekiler<\/p><ul class=\"toc_list\"><li><a href=\"#Prepare_the_example_environment_IIS_and_Hosting_Bundle\"><span class=\"toc_number toc_depth_1\">1.<\/span> Prepare the example environment: IIS and Hosting Bundle<\/a><ul><li><a href=\"#Prepare_access_and_a_recovery_path\"><span class=\"toc_number toc_depth_2\">1.1.<\/span> Prepare access and a recovery path<\/a><\/li><li><a href=\"#Install_the_IIS_role_and_management_tools\"><span class=\"toc_number toc_depth_2\">1.2.<\/span> Install the IIS role and management tools<\/a><\/li><li><a href=\"#Install_and_verify_the_Hosting_Bundle\"><span class=\"toc_number toc_depth_2\">1.3.<\/span> Install and verify the Hosting Bundle<\/a><\/li><\/ul><\/li><li><a href=\"#Publish_the_app_configure_its_pool_and_set_permissions\"><span class=\"toc_number toc_depth_1\">2.<\/span> Publish the app, configure its pool and set permissions<\/a><ul><li><a href=\"#Create_the_project_and_publish_a_Release_build\"><span class=\"toc_number toc_depth_2\">2.1.<\/span> Create the project and publish a Release build<\/a><\/li><li><a href=\"#Transfer_the_published_files_to_the_VPS\"><span class=\"toc_number toc_depth_2\">2.2.<\/span> Transfer the published files to the VPS<\/a><\/li><li><a href=\"#Configure_the_pool_and_folder_permissions\"><span class=\"toc_number toc_depth_2\">2.3.<\/span> Configure the pool and folder permissions<\/a><\/li><\/ul><\/li><li><a href=\"#Create_the_IIS_site_connect_DNS_and_verify_HTTPS\"><span class=\"toc_number toc_depth_1\">3.<\/span> Create the IIS site, connect DNS and verify HTTPS<\/a><ul><li><a href=\"#Create_the_site_and_check_a_local_request\"><span class=\"toc_number toc_depth_2\">3.1.<\/span> Create the site and check a local request<\/a><\/li><li><a href=\"#Configure_DNS_and_allow_incoming_connections\"><span class=\"toc_number toc_depth_2\">3.2.<\/span> Configure DNS and allow incoming connections<\/a><\/li><li><a href=\"#Obtain_a_certificate_and_verify_the_HTTPS_binding\"><span class=\"toc_number toc_depth_2\">3.3.<\/span> Obtain a certificate and verify the HTTPS binding<\/a><\/li><\/ul><\/li><li><a href=\"#Diagnose_startup_errors_and_update_safely\"><span class=\"toc_number toc_depth_1\">4.<\/span> Diagnose startup errors and update safely<\/a><ul><li><a href=\"#Narrow_down_the_problem_using_the_error_code\"><span class=\"toc_number toc_depth_2\">4.1.<\/span> Narrow down the problem using the error code<\/a><\/li><li><a href=\"#Enable_startup_logging_temporarily\"><span class=\"toc_number toc_depth_2\">4.2.<\/span> Enable startup logging temporarily<\/a><\/li><li><a href=\"#Update_with_a_rollback_copy_ready\"><span class=\"toc_number toc_depth_2\">4.3.<\/span> Update with a rollback copy ready<\/a><\/li><\/ul><\/li><li><a href=\"#Frequently_Asked_Questions\"><span class=\"toc_number toc_depth_1\">5.<\/span> Frequently Asked Questions<\/a><ul><li><a href=\"#Do_I_need_to_install_the_NET_SDK_on_the_Windows_VPS\"><span class=\"toc_number toc_depth_2\">5.1.<\/span> Do I need to install the .NET SDK on the Windows VPS?<\/a><\/li><li><a href=\"#Can_different_NET_versions_run_on_the_same_server\"><span class=\"toc_number toc_depth_2\">5.2.<\/span> Can different .NET versions run on the same server?<\/a><\/li><li><a href=\"#Will_users_be_signed_out_when_the_server_restarts\"><span class=\"toc_number toc_depth_2\">5.3.<\/span> Will users be signed out when the server restarts?<\/a><\/li><li><a href=\"#Can_I_run_continuous_background_jobs_in_an_IIS_application\"><span class=\"toc_number toc_depth_2\">5.4.<\/span> Can I run continuous background jobs in an IIS application?<\/a><\/li><\/ul><\/li><li><a href=\"#Final_checklist_before_going_live\"><span class=\"toc_number toc_depth_1\">6.<\/span> Final checklist before going live<\/a><\/li><\/ul><\/div>\n<h2><span id=\"Prepare_the_example_environment_IIS_and_Hosting_Bundle\">Prepare the example environment: IIS and Hosting Bundle<\/span><\/h2>\n<p>To serve your application through IIS, install the web server first, then the components that run your application. IIS is Windows&#8217; web server. The Hosting Bundle supplies the .NET runtimes-the software that executes your application-and the module that connects ASP.NET Core to IIS.<\/p>\n<div class=\"aiw-note aiw-note-example\">\n<p class=\"aiw-box-title\">Example scenario<\/p>\n<p>In this hypothetical setup, you will use IIS 10 and .NET 10 LTS on a clean Windows Server 2022 x64 VPS. x64 means 64-bit architecture; LTS means long-term support. You will then deploy OrnekWeb, an application that requires no database, to C:\\Sites\\OrnekWeb.<\/p>\n<\/div>\n<h3><span id=\"Prepare_access_and_a_recovery_path\">Prepare access and a recovery path<\/span><\/h3>\n<p>Connect to the VPS using Remote Desktop and your administrator account. This connection uses RDP, the Remote Desktop Protocol. If the server contains existing data, take a server backup before installing updates or roles, which add server capabilities such as IIS. If you use your provider&#8217;s backup service, check its control panel to confirm that the backup has completed and locate the restore option before proceeding. Keep a separate backup of any existing IIS settings, too.<\/p>\n<p>Next, install updates through <strong>Windows Update<\/strong> and complete any pending restart. On a server hosting existing sites, schedule a maintenance window because installation and restarts can interrupt service.<\/p>\n<p>Check that you can access your domain&#8217;s DNS management panel and note the VPS&#8217;s public IPv4 address. DNS holds the records that direct your domain name to the server&#8217;s IP address. The example hostname <code>uygulama.example.com<\/code> is a placeholder; use your own subdomain wherever it appears. If you are moving an existing site, leave its DNS record unchanged for now. You will point it to the VPS once the site is ready.<\/p>\n<h3><span id=\"Install_the_IIS_role_and_management_tools\">Install the IIS role and management tools<\/span><\/h3>\n<ol>\n<li>Open <strong>Server Manager &#8211; Manage &#8211; Add Roles and Features<\/strong> to launch the installation wizard. Select <strong>Role-based or feature-based installation<\/strong>. On the server selection screen, choose the VPS you are connected to and continue.<\/li>\n<li>Under <strong>Server Roles<\/strong>, select <strong>Web Server (IIS)<\/strong>. In the dialog that appears, click <strong>Add Features<\/strong> to include the required management components.<\/li>\n<li>Continue to the IIS <strong>Role Services<\/strong> screen. Keep the default selections and make sure <strong>Management Tools &#8211; IIS Management Console<\/strong> is selected. This installs the interface you will use to manage sites. ASP.NET Core does not require the older ASP.NET 4.x role service. Continue to the confirmation screen, click <strong>Install<\/strong> and wait for the wizard to report a successful installation.<\/li>\n<\/ol>\n<p>In a browser on the VPS, open <code>http:\/\/localhost<\/code>. On a clean installation, you should see the IIS welcome page. This confirms that the web server responds to an HTTP request from within the VPS; it does not yet confirm access from the internet. You will check external access later.<\/p>\n<h3><span id=\"Install_and_verify_the_Hosting_Bundle\">Install and verify the Hosting Bundle<\/span><\/h3>\n<p>On the VPS, open Microsoft&#8217;s official .NET download page. Under the latest maintenance release of .NET 10, find the ASP.NET Core Runtime section and download the Windows <strong>Hosting Bundle<\/strong> installer. A maintenance release provides fixes within the same major version.<\/p>\n<p>With IIS installed, run the Hosting Bundle installer as an administrator and keep the default components. The package installs the .NET and ASP.NET Core runtimes your application needs, along with the ASP.NET Core Module for IIS integration. If you installed the Hosting Bundle before IIS, reopen the installer now and select <strong>Repair<\/strong> to complete the IIS module installation.<\/p>\n<p>Restart the VPS after installation; if it hosts existing sites, do this during a maintenance window. Once you reconnect, open a new PowerShell window on the VPS and run this command to list the installed runtimes:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">dotnet --list-runtimes<\/code><\/pre>\n<p>You should see entries for <code>Microsoft.NETCore.App 10.0.x<\/code> and <code>Microsoft.AspNetCore.App 10.0.x<\/code>; <code>x<\/code> represents the installed maintenance release number. These entries confirm that the runtimes needed by the example application are present.<\/p>\n<p>Next, check that the IIS integration module is installed. Open <strong>Server Manager &#8211; Tools &#8211; Internet Information Services (IIS) Manager<\/strong>. Select the server in the connections tree on the left, then open <strong>Modules<\/strong>. If <code>AspNetCoreModuleV2<\/code> is listed as well as the required runtimes, you are ready to prepare the published files. If the runtimes are present but the module is missing, repair the Hosting Bundle installation, restart the server and check the same list again.<\/p>\n<h2><span id=\"Publish_the_app_configure_its_pool_and_set_permissions\">Publish the app, configure its pool and set permissions<\/span><\/h2>\n<h3><span id=\"Create_the_project_and_publish_a_Release_build\">Create the project and publish a Release build<\/span><\/h3>\n<p>Prepare the example application&#8217;s deployment files on your developer PC; you do not need to compile it on the VPS. On your Windows PC, download and install the <strong>.NET 10 SDK Windows x64<\/strong> package from Microsoft&#8217;s official .NET download page. The SDK, or software development kit, provides the tools to create and compile the project.<\/p>\n<figure class=\"aiw-figure\" data-aiw-visual-id=\"5997\" data-aiw-visual-key=\"0a2411b3de975d2cecf96adf0b7aaf08c2367a06b1da47324c4a413ee063978f\"><img decoding=\"async\" src=\"https:\/\/www.dchost.com\/blog\/wp-content\/uploads\/2026\/11\/dchost-flow-en-cb6621c9a0f475f73d0c.png\" alt=\"Flow showing OrnekWeb publishing, transfer to the VPS, application pool configuration and folder permissions.\" width=\"840\" height=\"1149\" loading=\"lazy\" class=\"aiw-article-visual wp-image-5997\" srcset=\"https:\/\/www.dchost.com\/blog\/wp-content\/uploads\/2026\/11\/dchost-flow-en-cb6621c9a0f475f73d0c.png 840w, https:\/\/www.dchost.com\/blog\/wp-content\/uploads\/2026\/11\/dchost-flow-en-cb6621c9a0f475f73d0c-219x300.png 219w, https:\/\/www.dchost.com\/blog\/wp-content\/uploads\/2026\/11\/dchost-flow-en-cb6621c9a0f475f73d0c-749x1024.png 749w, https:\/\/www.dchost.com\/blog\/wp-content\/uploads\/2026\/11\/dchost-flow-en-cb6621c9a0f475f73d0c-768x1051.png 768w\" sizes=\"auto, (max-width: 840px) 100vw, 840px\" \/><figcaption>Workflow diagram: The flow shows how source code becomes published files and how to prepare IIS to use those files through a dedicated application pool identity with limited permissions.<\/figcaption><\/figure>\n<p>Open a new PowerShell window and run <code>dotnet --list-sdks<\/code>. Check that the output includes a .NET 10 SDK version matching <code>10.0.x<\/code>, where x stands for the remaining version digits. Then run the following commands in the same window to create OrnekWeb and publish its deployment-ready files. For this hypothetical example, the project and publish folders must not already be in use:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">New-Item -ItemType Directory -Force C:\\src | Out-Null\nSet-Location C:\\src\ndotnet new webapp -n OrnekWeb -f net10.0\nSet-Location C:\\src\\OrnekWeb\ndotnet publish -c Release -r win-x64 --self-contained false -p:AspNetCoreHostingModel=InProcess -o C:\\publish\\OrnekWeb<\/code><\/pre>\n<p>These commands create and publish a database-free web project using Razor Pages, ASP.NET Core&#8217;s page-based framework. <strong>Release<\/strong> is the build configuration intended for deployment. <strong>Framework-dependent<\/strong> means the application uses the .NET runtime installed on the server; <code>--self-contained false<\/code> selects this option. <code>win-x64<\/code> targets 64-bit Windows, while <strong>in-process<\/strong> means the application runs inside the IIS process that handles requests.<\/p>\n<p>After the publish command completes successfully, open <code>C:\\publish\\OrnekWeb<\/code> in File Explorer. It should contain <code>OrnekWeb.dll<\/code>, <code>OrnekWeb.runtimeconfig.json<\/code>, <code>wwwroot<\/code> and <code>web.config<\/code>. These are the published files you will transfer to the VPS.<\/p>\n<p>The automatically generated <code>web.config<\/code> tells IIS how to start the application and route requests to it through the ASP.NET Core Module. Open the file in a text editor and check for <code>AspNetCoreModuleV2<\/code> and <code>hostingModel=\"inprocess\"<\/code>. Keep this generated configuration; do not replace it with one from an older ASP.NET application.<\/p>\n<h3><span id=\"Transfer_the_published_files_to_the_VPS\">Transfer the published files to the VPS<\/span><\/h3>\n<p>On your developer PC, open Remote Desktop Connection. Under <strong>Show Options &#8211; Local Resources &#8211; More &#8211; Drives<\/strong>, select your local C drive, confirm the selection and reconnect to the VPS. This makes that drive accessible from your remote session so you can copy the published files.<\/p>\n<p>On the VPS, open File Explorer and select the redirected C drive under <strong>This PC<\/strong>, rather than the VPS&#8217;s own C drive. Find the developer PC&#8217;s <code>C:\\publish\\OrnekWeb<\/code> folder and copy its <em>contents<\/em> into a folder you create at <code>C:\\Sites\\OrnekWeb<\/code> on the VPS. Copy the published output, not the source project or an extra enclosing folder. When the transfer finishes, <code>web.config<\/code> should appear directly inside the destination folder.<\/p>\n<p>The destination folder is empty in this example. If it already contains deployment files, use the safe update steps later rather than overwriting them. Unless an environment setting overrides it, the example application defaults to <strong>Production<\/strong>, the environment for live use. The developer PC&#8217;s <code>launchSettings.json<\/code> controls local development launch settings; IIS does not use it to configure the deployed application.<\/p>\n<h3><span id=\"Configure_the_pool_and_folder_permissions\">Configure the pool and folder permissions<\/span><\/h3>\n<p>Create a dedicated application pool so your site runs under its own Windows identity, the account used to access files and other resources. On the VPS, open IIS Manager &#8211; <strong>Application Pools<\/strong> &#8211; <strong>Add Application Pool<\/strong>. Enter <code>OrnekWeb<\/code> in the <strong>Name<\/strong> field. From <strong>.NET CLR version<\/strong>, select <code>No Managed Code<\/code>. This field controls the older .NET Framework runtime; ASP.NET Core uses its own runtime. Leave <strong>Managed pipeline mode<\/strong>, the request-processing mode, set to <code>Integrated<\/code>. Click <strong>OK<\/strong>; the new pool should appear in the list.<\/p>\n<p>Select the new pool and open <strong>Advanced Settings<\/strong>. Set <strong>Enable 32-Bit Applications<\/strong> to <code>False<\/code> to match your 64-bit deployment. Under <strong>Process Model<\/strong>, select <strong>Identity<\/strong> and click the ellipsis button. Choose <strong>Built-in account<\/strong>, select <code>ApplicationPoolIdentity<\/code> and click <strong>OK<\/strong> in both dialogs. When you assign the site to this pool in the next section, the application will run under the pool-specific Windows identity <code>IIS AppPool\\OrnekWeb<\/code>.<\/p>\n<p>Next, give that identity permission to read the published files and run the application. These are folder permissions on NTFS, the Windows file system. On the VPS, open PowerShell as an administrator and run:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">icacls &quot;C:\\Sites\\OrnekWeb&quot; \/grant &quot;IIS AppPool\\OrnekWeb:(OI)(CI)RX&quot;<\/code><\/pre>\n<p><code>RX<\/code> grants read and execute permissions; <code>(OI)(CI)<\/code> passes those permissions to child files and folders. After the command completes without errors, right-click the deployment folder in File Explorer. Under <strong>Properties &#8211; Security<\/strong>, select the pool identity; you should see <strong>Read &amp; execute<\/strong> allowed.<\/p>\n<p>Do not grant the application identity write or full control permissions on the deployment root folder: this application does not need to change its own deployment files. The command adds permissions without removing existing ones, so also check the same screen for write access the identity might receive through other groups. Do not remove administrator or system permissions as part of this check. If diagnostics are needed later, you will grant write permission only to the <code>logs<\/code> subfolder; the example application does not need that permission for normal operation.<\/p>\n<h2><span id=\"Create_the_IIS_site_connect_DNS_and_verify_HTTPS\">Create the IIS site, connect DNS and verify HTTPS<\/span><\/h2>\n<h3><span id=\"Create_the_site_and_check_a_local_request\">Create the site and check a local request<\/span><\/h3>\n<p>Your publish folder is ready. Next, tell IIS which domain name and application pool to use for the site. On the VPS, open IIS Manager, right-click <strong>Sites<\/strong> and choose <strong>Add Website<\/strong>. Enter <code>OrnekWeb<\/code> in <strong>Site name<\/strong>. Beside <strong>Application pool<\/strong>, click <strong>Select<\/strong> and choose the existing <code>OrnekWeb<\/code> pool. In <strong>Physical path<\/strong>, the location of your published files, enter <code>C:\\Sites\\OrnekWeb<\/code>.<\/p>\n<p>In the <strong>Binding<\/strong> section, which determines which requests reach this site, set <strong>Type<\/strong> to <code>http<\/code>, <strong>IP address<\/strong> to <code>All Unassigned<\/code> and <strong>Port<\/strong> to <code>80<\/code>. Enter your own subdomain in <strong>Host name<\/strong>; the hypothetical example uses <code>uygulama.example.com<\/code>. This binding directs HTTP requests for that hostname to the OrnekWeb site. Click <strong>OK<\/strong>; the site should appear under Sites and start. No other site should have a binding with the same combination of IP address, hostname and port.<\/p>\n<p>Before changing DNS, check that local IIS serves the correct application. Run the following command in PowerShell on the VPS, replacing the placeholder hostname with your own. It connects directly to IIS on this server and supplies the hostname needed to select your site:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">curl.exe -i -H &quot;Host: uygulama.example.com&quot; http:\/\/127.0.0.1\/<\/code><\/pre>\n<p>The expected response is <code>200 OK<\/code> followed by the HTML content of the OrnekWeb page. If you see the IIS welcome page instead, compare the hostname in the site&#8217;s <strong>Bindings<\/strong> list with the name in your command. Correct any mismatch and repeat the request before changing application files. If the response redirects to HTTPS, the <code>Location<\/code> header shows its destination; complete the HTTPS setup below before checking that address.<\/p>\n<h3><span id=\"Configure_DNS_and_allow_incoming_connections\">Configure DNS and allow incoming connections<\/span><\/h3>\n<p>Once the local check succeeds, point your domain name to the VPS. In your domain&#8217;s DNS panel, add an <strong>A<\/strong> record, which maps a hostname to an IPv4 address. If you manage the DNS zone for <code>example.com<\/code>, the name field is usually <code>uygulama<\/code>; enter the VPS&#8217;s public IPv4 address as the value, then save the record. If you are changing an existing record, save its previous value first so you can restore it if needed. Also check for an <strong>AAAA<\/strong> record, which specifies an IPv6 address. If you have not configured IPv6 hosting, do not leave a record for this hostname pointing to the wrong server.<\/p>\n<p>DNS directs visitors to the server, but the firewall must also allow their connections. On the VPS, open <strong>Windows Defender Firewall with Advanced Security<\/strong>, select <strong>Inbound Rules<\/strong> and click <strong>New Rule<\/strong>. Choose <strong>Port<\/strong> and click <strong>Next<\/strong>. Select <strong>TCP<\/strong> and <strong>Specific local ports<\/strong>, then enter <code>80,443<\/code> for HTTP and HTTPS. Click <strong>Next<\/strong>, choose <strong>Allow the connection<\/strong> and continue to the profile screen. Select the server&#8217;s active network profile, then name the rule <code>OrnekWeb HTTP HTTPS<\/code> and click <strong>Finish<\/strong>. The rule should appear enabled in the inbound rules list. If your provider&#8217;s panel has a separate network firewall, allow the same inbound TCP ports there too; do not disable Windows Firewall.<\/p>\n<p>Now check access from outside the VPS. Open PowerShell on your developer PC, replace the placeholder hostname and check DNS with <code>Resolve-DnsName uygulama.example.com<\/code>, then test port 80 with <code>Test-NetConnection uygulama.example.com -Port 80<\/code>. The first command should show the VPS&#8217;s public IP address; the second should report <strong>TcpTestSucceeded: True<\/strong>. If the IP address is wrong, check the saved DNS record and whether an older response remains cached. If the IP is correct but the connection fails, check the firewall rules and confirm that the IIS site is running. After correcting the issue, repeat the failed command before proceeding to certificate setup.<\/p>\n<h3><span id=\"Obtain_a_certificate_and_verify_the_HTTPS_binding\">Obtain a certificate and verify the HTTPS binding<\/span><\/h3>\n<p>HTTPS encrypts the connection between your browser and the server. To enable it, you will obtain a trusted certificate for your subdomain and assign it to an IIS HTTPS binding. This walkthrough uses a certificate provider that accepts a certificate signing request (CSR) and supports DNS validation to confirm that you control the domain.<\/p>\n<p>On the VPS, select the server node in IIS Manager and open <strong>Server Certificates &#8211; Create Certificate Request<\/strong>. Enter your actual subdomain in <strong>Common name<\/strong> and the requested organization and location details in the remaining fields. Select <strong>Microsoft RSA SChannel Cryptographic Provider<\/strong> as the provider and <code>2048<\/code> as the key length. Create the destination folder in File Explorer if it does not exist, then save the request to <code>C:\\DeployBackups\\OrnekWeb.req<\/code>. You should now have a CSR file to submit to your certificate provider; the corresponding private key stays on the VPS.<\/p>\n<p>Open the generated file in Notepad and paste its entire contents into the <strong>CSR<\/strong> field on your certificate provider&#8217;s order screen. Check that the order includes your actual subdomain among the names the certificate will cover. Choose DNS validation, then add the record type, name and value supplied by the provider to your DNS panel. Follow your DNS panel&#8217;s naming format so it does not append your domain twice. Once the provider confirms domain validation and certificate issuance are complete, download the certificate file supplied for IIS.<\/p>\n<p>Back on the VPS, open <strong>Server Certificates &#8211; Complete Certificate Request<\/strong> and select the downloaded file. Set the friendly name to <code>OrnekWeb TLS<\/code> and the certificate store to <code>Personal<\/code>. Install any intermediate certificates supplied by the provider according to its instructions; these link your certificate to a root certificate trusted by browsers. Complete the request on this same VPS so IIS can associate the issued certificate with the private key created earlier. The certificate should then appear in the <strong>Server Certificates<\/strong> list.<\/p>\n<p>If you already have a <strong>PFX<\/strong> certificate package containing the certificate and its private key, you can use it instead of creating and completing a CSR. In <strong>Server Certificates<\/strong>, choose <strong>Import<\/strong>, supply the PFX file and its password, and import it into the <strong>Personal<\/strong> store. Keep the PFX out of the publish folder: anyone who obtains it and its password could access the private key. After installation, remove the transfer copy and retain a secure backup.<\/p>\n<p>Once the certificate appears in the list, open <strong>Sites &#8211; OrnekWeb &#8211; Bindings &#8211; Add<\/strong>. Set <strong>Type<\/strong> to <code>https<\/code>, <strong>Port<\/strong> to <code>443<\/code> and <strong>Host name<\/strong> to your actual subdomain. Leave <strong>IP address<\/strong> set to <strong>All Unassigned<\/strong> for this example. Select <strong>Require Server Name Indication<\/strong>; SNI lets IIS select the certificate by hostname when sites share an IP address. Choose the certificate you just installed from the <strong>SSL certificate<\/strong> list and click <strong>OK<\/strong>. The bindings list should now show an HTTPS entry with your hostname.<\/p>\n<p>On your developer PC, open your domain over HTTPS in a browser. The OrnekWeb page should load without a certificate warning. Open the browser&#8217;s certificate details and check that the certificate covers your subdomain, is within its validity dates and has a trusted certificate chain. If you see a warning, compare those details with the certificate selected in the IIS binding, then correct any domain mismatch or missing intermediate certificates and reopen the same address. If the connection fails before a page or certificate appears, check that the HTTPS binding exists and inbound TCP port 443 is allowed. Bypassing a certificate warning does not count as a successful HTTPS check.<\/p>\n<h2><span id=\"Diagnose_startup_errors_and_update_safely\">Diagnose startup errors and update safely<\/span><\/h2>\n<h3><span id=\"Narrow_down_the_problem_using_the_error_code\">Narrow down the problem using the error code<\/span><\/h3>\n<p>If the application returns an error page, note its status code, then open <strong>Event Viewer &#8211; Windows Logs &#8211; Application<\/strong> on the VPS. Request the page again and look for <code>IIS AspNetCore Module V2<\/code> entries matching that time. The <code>sc-status<\/code> and <code>sc-substatus<\/code> fields in the IIS logs show the main HTTP status code and its more specific substatus. To locate these logs, select the OrnekWeb site in IIS Manager and check the <strong>Directory<\/strong> field on the <strong>Logging<\/strong> screen. Use the matching row below to choose your next check.<\/p>\n<figure class=\"aiw-figure\" data-aiw-visual-id=\"5999\" data-aiw-visual-key=\"38c1c7d8dd20ce9447ea2295c61d9661c71208c7becfc6b8190e2ecab9341531\"><img decoding=\"async\" src=\"https:\/\/www.dchost.com\/blog\/wp-content\/uploads\/2026\/11\/dchost-flow-en-a35ee928a42fce7fe2c8.png\" alt=\"Update flow showing backup, app_offline.htm shutdown, file replacement, HTTPS checks and rollback if needed.\" width=\"840\" height=\"1227\" loading=\"lazy\" class=\"aiw-article-visual wp-image-5999\" srcset=\"https:\/\/www.dchost.com\/blog\/wp-content\/uploads\/2026\/11\/dchost-flow-en-a35ee928a42fce7fe2c8.png 840w, https:\/\/www.dchost.com\/blog\/wp-content\/uploads\/2026\/11\/dchost-flow-en-a35ee928a42fce7fe2c8-205x300.png 205w, https:\/\/www.dchost.com\/blog\/wp-content\/uploads\/2026\/11\/dchost-flow-en-a35ee928a42fce7fe2c8-701x1024.png 701w, https:\/\/www.dchost.com\/blog\/wp-content\/uploads\/2026\/11\/dchost-flow-en-a35ee928a42fce7fe2c8-768x1122.png 768w\" sizes=\"auto, (max-width: 840px) 100vw, 840px\" \/><figcaption>Workflow diagram: The flow explains when to save a rollback copy, stop the app with a maintenance page, replace published files and restore the previous deployment if HTTPS checks fail.<\/figcaption><\/figure>\n<table>\n<thead>\n<tr>\n<th>Symptom<\/th>\n<th>Diagnostic check and targeted fix<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>500.19<\/td>\n<td>Read the error code and configuration line in the error details returned by a request made from the VPS. If the XML configuration syntax is invalid, save a copy of the file outside the publish folder and correct only the faulty line. If the error identifies an unrecognized ASP.NET Core Module, repair the Hosting Bundle installation. For an access-denied error, check file permissions.<\/td>\n<\/tr>\n<tr>\n<td>500.30<\/td>\n<td>The application failed to start. Examine the exception-the error reported by the application-in the Application log. Correct the missing setting or startup code error identified in that entry. Do not install different runtimes based on this status code alone.<\/td>\n<\/tr>\n<tr>\n<td>500.31<\/td>\n<td>On the VPS, compare the output of <code>dotnet --list-runtimes<\/code> with the framework requirements in <code>OrnekWeb.runtimeconfig.json<\/code>. This example requires x64 Microsoft.NETCore.App and Microsoft.AspNetCore.App 10.0.x. If they are missing, install the appropriate Hosting Bundle.<\/td>\n<\/tr>\n<tr>\n<td>503<\/td>\n<td>Check the OrnekWeb pool&#8217;s status under IIS Manager &#8211; Application Pools. If it is stopped, examine WAS (Windows Process Activation Service, which manages IIS worker processes) events under Event Viewer &#8211; Windows Logs &#8211; System. Resolve the crash or identity issue identified there, then select the pool and click Start. Do not disable Rapid-Fail Protection, which stops the pool after repeated crashes.<\/td>\n<\/tr>\n<tr>\n<td>401.3 or 403.14<\/td>\n<td>For 401.3, examine permissions in PowerShell with <code>icacls C:\\Sites\\OrnekWeb<\/code>; grant only read\/execute access to the identity that needs to read the files. For 403.14, check that the site&#8217;s physical path points to the publish folder and that the generated web.config is directly inside it. Enabling directory browsing will not start the application and may expose file names.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>After each fix, reopen the same address and check the logs for entries matching the new request. The expected result is the application page loading without the corresponding error recurring. Change one setting at a time so you can identify which change resolves the problem.<\/p>\n<h3><span id=\"Enable_startup_logging_temporarily\">Enable startup logging temporarily<\/span><\/h3>\n<p>If the event log does not reveal the cause, temporarily capture the application&#8217;s startup output in a file. First, save a copy of <code>web.config<\/code> outside the publish folder so you can restore it if the edit causes problems. To create the log folder and grant write access only to that folder, run these commands in an administrator PowerShell window on the VPS:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">New-Item -ItemType Directory -Force C:\\Sites\\OrnekWeb\\logs | Out-Null\nicacls &quot;C:\\Sites\\OrnekWeb\\logs&quot; \/grant &quot;IIS AppPool\\OrnekWeb:(OI)(CI)M&quot;<\/code><\/pre>\n<p>The commands should complete without errors. The final M grants Modify permission, allowing the pool identity to write diagnostic files in the logs folder without granting write access to the deployment root.<\/p>\n<p>Open the publish folder&#8217;s web.config file in a text editor running as administrator. In the existing <code>aspNetCore<\/code> element, set <code>stdoutLogEnabled=\"true\"<\/code> and <code>stdoutLogFile=\".\\logs\\stdout\"<\/code>; preserve the other attributes. Save the file, request the page again, then open the newly created file in the logs folder. Look for the startup exception and any details identifying a missing setting or dependency.<\/p>\n<p><strong>stdout<\/strong> means standard output. This setting redirects the application&#8217;s standard output and error streams to files, which can reveal startup failures not fully described in Event Viewer. These files have no automatic size limit, so keep this logging enabled only while diagnosing the problem.<\/p>\n<p>When diagnosis is complete, set the stdoutLogEnabled value to <code>false<\/code> and save the file. If the application does not otherwise write to this folder, open the logs folder&#8217;s <strong>Properties &#8211; Security &#8211; Advanced<\/strong> screen and remove the explicit Modify permission you added for the OrnekWeb pool identity. Retain the read\/execute permission inherited from the parent folder. Request the page again and confirm that no new startup log is created and the existing log is no longer growing.<\/p>\n<p>Do not share these files publicly, because logs may contain passwords or connection details. Follow the principles of <a href=\"https:\/\/www.dchost.com\/blog\/en\/managing-env-files-and-secrets-on-a-vps-safely\/\">secret management on a VPS<\/a> to store settings securely. Keep the environment set to Production; switching to Development could expose error details to visitors.<\/p>\n<h3><span id=\"Update_with_a_rollback_copy_ready\">Update with a rollback copy ready<\/span><\/h3>\n<ol>\n<li><strong>Back up the deployment:<\/strong> In File Explorer on the VPS, copy the contents of the current publish folder to <code>C:\\DeployBackups\\OrnekWeb\\surum-001<\/code>. Use a separate backup folder for each release so you do not overwrite an earlier rollback copy. Open the backup folder and confirm that the published files, including web.config, are directly inside it. A file copy is sufficient for this example application because it does not write persistent data; database migrations are outside the scope of this procedure.<\/li>\n<li><strong>Stop the application:<\/strong> Write a short maintenance message in <code>C:\\Sites\\OrnekWeb\\app_offline.htm<\/code>. If you use Notepad, select <strong>All Files<\/strong> as the file type when saving; the file name must not have .txt appended. The ASP.NET Core Module detects this file and shuts down the application. Refresh the page in your browser and confirm that the maintenance message appears before replacing any files. If files remain locked, select only the OrnekWeb pool under <strong>IIS Manager &#8211; Application Pools<\/strong> and click <strong>Stop<\/strong>. While the pool is stopped, visitors may see a service-unavailable response instead of the maintenance message.<\/li>\n<li><strong>Replace the files:<\/strong> Keep app_offline.htm in place while removing the old published files and copying the complete new Release output from your developer PC into the same publish folder. Compare server-specific settings against the backup and preserve the required values. In File Explorer, open a copied file&#8217;s <strong>Properties &#8211; Security &#8211; Advanced<\/strong> screen and confirm that it inherits the pool identity&#8217;s read\/execute permissions from the publish folder. The new web.config and application files should be directly inside the publish folder, not inside an extra nested folder.<\/li>\n<li><strong>Verify or roll back:<\/strong> If you stopped the pool, click <strong>Start<\/strong> on the same screen, then remove app_offline.htm. From your computer outside the VPS, open the home page and Privacy page over HTTPS; both should load without application errors or certificate warnings. If these checks fail, put the maintenance file back and confirm that the application has stopped, using <strong>Stop<\/strong> on the OrnekWeb pool if files remain locked. Keep the maintenance file in place while removing the new published files and copying back the contents of surum-001. If you stopped the pool during rollback, start it again. Remove the maintenance file and repeat the same page checks.<\/li>\n<\/ol>\n<p>This method interrupts service while you replace and check the deployment. If you need to reduce downtime, you can design a switch between separate deployment folders, but changing the file path alone is not enough. Running processes and application state must also be managed to support that transition.<\/p>\n<h2><span id=\"Frequently_Asked_Questions\">Frequently Asked Questions<\/span><\/h2>\n<h3><span id=\"Do_I_need_to_install_the_NET_SDK_on_the_Windows_VPS\">Do I need to install the .NET SDK on the Windows VPS?<\/span><\/h3>\n<p>No, unless you plan to build applications on the server. The <strong>SDK<\/strong> is the toolkit for developing and building your application. In this example, you install it only on your developer PC and transfer the published output to the VPS rather than source code.<\/p>\n<p>The server needs the appropriate .NET runtime and the module that connects the application to IIS. The Hosting Bundle supplies both. Installing the SDK does not replace the Hosting Bundle.<\/p>\n<h3><span id=\"Can_different_NET_versions_run_on_the_same_server\">Can different .NET versions run on the same server?<\/span><\/h3>\n<p>Yes, runtimes for different major versions can coexist. As a hypothetical example, applications targeting .NET 8 and .NET 10 can run on the same IIS server if their required runtimes are installed. Give each application a separate application pool. The walkthrough&#8217;s <strong>in-process<\/strong> model, where the application runs inside the IIS worker process, does not support multiple applications sharing a pool.<\/p>\n<p>Before removing a runtime, check the requirements in your applications&#8217; <code>runtimeconfig.json<\/code> files. Installing a new major version does not automatically upgrade an application targeting an older version; it may still need the older runtime.<\/p>\n<h3><span id=\"Will_users_be_signed_out_when_the_server_restarts\">Will users be signed out when the server restarts?<\/span><\/h3>\n<p>Not necessarily. To read existing authentication cookies after a restart, your application must still have access to the keys used by <strong>Data Protection<\/strong>, the ASP.NET Core system that protects data such as authentication cookies. If those keys are held only in memory, users may need to sign in again after a restart.<\/p>\n<p>When you add sign-in functionality, check where the keys are stored. If you choose a persistent file-based store, keep it outside the publish folder so deployment updates do not delete the keys. Restrict access to the application identity and necessary administrator accounts. Use a method such as DPAPI, Windows&#8217; data protection mechanism, to encrypt the keys on disk.<\/p>\n<p>Include the keys in your backup plan and ensure that you can decrypt the restored keys in the recovery environment. DPAPI protection can tie decryption to a Windows account or machine, so copying the key files alone may not be enough when moving to another server.<\/p>\n<p>During a maintenance window, sign in to the application. In IIS Manager, open <strong>Application Pools<\/strong>, select the application&#8217;s pool and click <strong>Recycle<\/strong>. Reload the application in the same browser and check that you remain signed in. <strong>Session<\/strong>, meaning server-side session data, can still be lost on restart if it is stored in memory, even when the authentication cookie remains valid.<\/p>\n<h3><span id=\"Can_I_run_continuous_background_jobs_in_an_IIS_application\">Can I run continuous background jobs in an IIS application?<\/span><\/h3>\n<p>You can, but you cannot rely on an IIS application running continuously. A pool restart, an idle shutdown or a deployment update can interrupt work started with <code>BackgroundService<\/code>. The <strong>AlwaysRunning<\/strong> setting enables the pool to start automatically, but it does not eliminate these interruptions.<\/p>\n<p>For continuous queue processing or long-running tasks, a separate <strong>.NET Worker Service<\/strong> running as a Windows service is more suitable. This keeps the background work separate from the IIS application&#8217;s lifecycle. For jobs that run at scheduled times, you can use Windows Task Scheduler. With either approach, save job progress to persistent storage and design retries so they do not accidentally perform the same operation twice.<\/p>\n<h2><span id=\"Final_checklist_before_going_live\">Final checklist before going live<\/span><\/h2>\n<p>A working home page confirms that the application runs, but there are a few more checks before you go live. Use this checklist to confirm that the application runs under the correct Windows identity, is accessible over HTTPS from outside the VPS and has a recovery path if an update fails.<\/p>\n<ul>\n<li><strong>Required runtimes are installed:<\/strong> Run <code>dotnet --list-runtimes<\/code> in PowerShell on the VPS. For this example, the <code>Microsoft.NETCore.App<\/code> and <code>Microsoft.AspNetCore.App<\/code> entries should show versions 10.0.x, where x represents the installed maintenance release. Select the server in IIS Manager and open <strong>Modules<\/strong>; the list should also include <code>AspNetCoreModuleV2<\/code>.<\/li>\n<li><strong>The pool uses the correct identity:<\/strong> Open <strong>IIS Manager &#8211; Application Pools<\/strong>, select <strong>OrnekWeb<\/strong> and open <strong>Advanced Settings<\/strong>. <strong>Identity<\/strong> should be <code>ApplicationPoolIdentity<\/code>, <strong>.NET CLR Version<\/strong> should be <code>No Managed Code<\/code> and <strong>Enable 32-Bit Applications<\/strong> should be <code>False<\/code>. Then open <strong>Sites &#8211; OrnekWeb &#8211; Basic Settings<\/strong> and confirm that the site is assigned to the OrnekWeb pool.<\/li>\n<li><strong>Folder permissions are limited:<\/strong> Examine the output of <code>icacls C:\\Sites\\OrnekWeb<\/code> in PowerShell on the VPS. The <code>IIS AppPool\\OrnekWeb<\/code> identity should have read and execute permissions at the root of the publish folder, without gaining write access through other groups. If you added write permission to the logs folder for temporary diagnostics, confirm that you removed it when it was no longer needed.<\/li>\n<li><strong>HTTPS works externally:<\/strong> Open your domain over HTTPS from your computer outside the VPS. The home page and Privacy page should load without certificate warnings. Open the browser&#8217;s certificate details and check that the certificate covers your hostname and is within its validity dates. This checks external access as well as the application&#8217;s response.<\/li>\n<li><strong>Temporary diagnostics are disabled:<\/strong> Open the deployed <code>web.config<\/code> file and check that it contains <code>stdoutLogEnabled=\"false\"<\/code>. Confirm that the application runs in the <code>Production<\/code> environment, with no Development environment setting left over from troubleshooting. Disable only the temporary startup diagnostics; keep normal IIS access logs and application error logging enabled.<\/li>\n<li><strong>A rollback copy is ready:<\/strong> Before an update, open the previous release folder under <code>C:\\DeployBackups\\OrnekWeb<\/code> in File Explorer and confirm that it contains the published files and server-specific settings. For a first deployment, save a copy of the working release once your checks pass; it will be your rollback copy for the next update. No <code>app_offline.htm<\/code> file should remain in the live publish folder. Record the rollback steps and the backup folder path so you can find them quickly.<\/li>\n<\/ul>\n<p><strong>Make monitoring and certificate renewal your next steps.<\/strong> Set up a check for external HTTPS access and the expected page content, then send a test notification to confirm that alerts reach you. Assign responsibility for certificate renewal and schedule it before the certificate expires. If you use automatic renewal, confirm that it also updates the certificate selected in the IIS binding. These checks help you catch access problems and keep HTTPS working after deployment.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Get your ASP.NET Core application running on a Windows VPS with IIS. Follow the OrnekWeb example from Hosting Bundle installation and Release publishing through app pool permissions, HTTPS, startup diagnostics and rollback.<\/p>\n","protected":false},"author":4,"featured_media":5990,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[180],"tags":[697,693,698,216,694,692],"class_list":["post-6001","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to","tag-net","tag-asp-net-core","tag-deployment","tag-https","tag-iis","tag-windows-vps"],"_links":{"self":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/6001","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/comments?post=6001"}],"version-history":[{"count":2,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/6001\/revisions"}],"predecessor-version":[{"id":6102,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/6001\/revisions\/6102"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/media\/5990"}],"wp:attachment":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/media?parent=6001"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/categories?post=6001"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/tags?post=6001"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}