Hosting Bundle: รัน ASP.NET Core บน IIS และแก้ Error 500.30
คำตอบสั้น ๆ: ติดตั้ง .NET Hosting Bundle แล้ว publish แอป ASP.NET Core ขึ้น IIS ตั้ง App Pool แบบ No Managed Code อ่าน stdout log และแก้ Error 500.30, 500.19, 502.5
คู่มือนี้อธิบาย Hosting Bundle ทีละขั้นตอน พร้อมคำสั่งที่ใช้ได้จริงและปัญหาที่พบบ่อย
.NET Hosting Bundle คือชุดติดตั้งที่รวม .NET Runtime, ASP.NET Core Runtime และ ASP.NET Core Module (ANCM) ซึ่งเป็นตัวเชื่อมที่ทำให้ IIS รันแอป ASP.NET Core ได้ ถ้าเซิร์ฟเวอร์ไม่มี Hosting Bundle แอปที่ publish มาอย่างถูกต้องจะขึ้น HTTP Error 500.19 ทันที เพราะ IIS ไม่รู้จักส่วน aspNetCore ในไฟล์ web.config
คู่มือนี้พาคุณติดตั้งองค์ประกอบที่จำเป็นบน Windows Server, publish แอป, สร้าง Application Pool และเว็บไซต์ใน IIS, deploy เวอร์ชันใหม่โดยไม่เจอไฟล์ถูกล็อก และอ่าน log เมื่อแอปไม่ยอมขึ้น เมื่อจบคุณจะมีแอป ASP.NET Core ที่ทำงานหลัง IIS พร้อมรับ HTTPS ได้ทันที
ANCM จาก Hosting Bundle คือตัวกลางที่ทำให้ IIS ส่งคำขอเข้าแอป .NET ได้
สิ่งที่ต้องเตรียม
- Windows Server 2019, 2022 หรือ 2025 ที่ติดตั้ง IIS แล้ว ถ้ายังไม่มี ดู คู่มือติดตั้ง IIS
- สิทธิ์ Administrator บนเซิร์ฟเวอร์ และ PowerShell แบบ Run as administrator
- รู้ว่าแอปสร้างด้วย .NET รุ่นใด (ดู TargetFramework ในไฟล์ .csproj เช่น net8.0 หรือ net10.0) เพราะต้องติดตั้ง Runtime ให้ตรงรุ่นหลัก
- เครื่องพัฒนาที่มี .NET SDK สำหรับ publish หรือระบบ CI ที่ build ให้
- โดเมนที่ชี้มาที่ IP ของเซิร์ฟเวอร์แล้ว ถ้าต้องการเปิด HTTPS
.NET 8 เป็นรุ่น LTS ที่สิ้นสุดการสนับสนุนในเดือนพฤศจิกายน 2026 แอปใหม่ควรใช้ .NET 10 ซึ่งเป็น LTS รุ่นล่าสุด
ขั้นตอนที่ 1: ติดตั้ง Hosting Bundle
เปิดหน้า Download .NET ของ Microsoft เลือกรุ่นที่ตรงกับแอป ในส่วน ASP.NET Core Runtime แถว Windows ให้คลิกลิงก์ Hosting Bundle แล้วนำไฟล์ติดตั้งขึ้นเซิร์ฟเวอร์ ติดตั้งแบบเงียบได้ด้วยคำสั่งนี้
.\dotnet-hosting-10.0.11-win.exe /install /quiet /norestart
หลังติดตั้ง ให้รีสตาร์ต IIS เพื่อให้ worker process เห็น PATH ใหม่ของ dotnet แล้วตรวจว่ามี Runtime และโมดูลครบ
net stop was /y
net start w3svc
dotnet --list-runtimes
Get-WebGlobalModule -Name AspNetCoreModuleV2
ต้องเห็นทั้ง Microsoft.AspNetCore.App และโมดูล AspNetCoreModuleV2 จึงพร้อมรันแอป
ลำดับการติดตั้งสำคัญมาก ถ้าติดตั้ง Hosting Bundle ก่อนติดตั้ง IIS โมดูลจะไม่ถูกลงทะเบียนกับ IIS ต้องรันตัวติดตั้งอีกครั้งแล้วเลือก Repair ติดตั้งหลายรุ่นคู่กันได้ เช่น 8 กับ 10 สำหรับเซิร์ฟเวอร์ที่มีหลายแอป
ขั้นตอนที่ 2: Publish แอปและวางไฟล์บนเซิร์ฟเวอร์
บนเครื่องพัฒนา ให้ publish แบบ framework-dependent ซึ่งใช้ Runtime ที่ติดตั้งไว้บนเซิร์ฟเวอร์ ไฟล์มีขนาดเล็ก และอัปเดต Runtime เพื่อแก้ช่องโหว่ได้โดยไม่ต้อง publish แอปใหม่
dotnet publish -c Release -o .\publish
โฟลเดอร์ publish จะมีไฟล์ MyApp.dll และ web.config ที่ระบบสร้างให้ คัดลอกทั้งโฟลเดอร์ไปไว้ที่ C:\inetpub\myapp บนเซิร์ฟเวอร์ ถ้าต้องการไม่พึ่ง Runtime บนเครื่อง ให้ publish แบบ self-contained ด้วย -r win-x64 --self-contained แต่ยังต้องมี ANCM จาก Hosting Bundle อยู่ดี
ขั้นตอนที่ 3: สร้าง Application Pool แบบ No Managed Code
ASP.NET Core ไม่ได้ใช้ .NET Framework CLR ที่ IIS โหลดให้ แต่ ANCM จะโหลด .NET Runtime เอง จึงต้องตั้ง Application Pool เป็น No Managed Code และควรใช้หนึ่ง Pool ต่อหนึ่งแอป เพราะโหมด in-process ไม่รองรับหลายแอปใน worker process เดียวกัน
Import-Module WebAdministration
New-WebAppPool -Name "MyAppPool"
Set-ItemProperty IIS:\AppPools\MyAppPool -Name managedRuntimeVersion -Value ''
New-Website -Name "MyApp" -PhysicalPath "C:\inetpub\myapp" -ApplicationPool "MyAppPool" -Port 80 -HostHeader "app.example.com"
icacls C:\inetpub\myapp /grant "IIS AppPool\MyAppPool:(OI)(CI)RX"
ถ้าทำผ่านหน้าจอ เปิด IIS Manager คลิก Application Pools แล้วเลือก Add Application Pool ตั้ง .NET CLR version เป็น No Managed Code และ Managed pipeline mode เป็น Integrated จากนั้นสร้างเว็บไซต์ใหม่ที่ Sites › Add Website แล้วเลือก Pool นี้
เลือก No Managed Code และ Integrated แล้วกด OK หนึ่ง Pool ต่อหนึ่งแอป (ภาพจำลอง)
คำสั่ง icacls ให้ตัวตนของ Application Pool อ่านและรันไฟล์ในโฟลเดอร์แอปได้ ซึ่งเพียงพอสำหรับแอปส่วนใหญ่ ถ้าแอปต้องเขียนไฟล์ เช่นอัปโหลดรูป ให้เพิ่มสิทธิ์ Modify เฉพาะโฟลเดอร์นั้น
ขั้นตอนที่ 4: ตรวจ web.config และโหมดของ ANCM
web.config ที่ได้จากการ publish มีหน้าตาประมาณนี้ ค่าที่ต้องสังเกตคือ processPath, arguments และ hostingModel
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\MyApp.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess">
<environmentVariables>
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</location>
</configuration>
โหมด inprocess ให้แอปทำงานอยู่ใน w3wp.exe ของ IIS โดยตรง ซึ่งเร็วที่สุดและเป็นค่าเริ่มต้น ส่วน outofprocess จะเปิดโปรเซส dotnet แยกที่รัน Kestrel แล้วให้ IIS ส่งต่อคำขอ ใช้เมื่อแอปต้องแยกโปรเซสด้วยเหตุผลเฉพาะ คำสั่ง publish สร้าง web.config ใหม่ทุกครั้ง ถ้าแก้ค่าเอง ให้เก็บไฟล์ web.config ไว้ในโปรเจกต์ ระบบจะใช้ไฟล์นั้นเป็นต้นแบบตอน publish
ขั้นตอนที่ 5: Deploy เวอร์ชันใหม่ด้วย app_offline.htm
ขณะแอปทำงาน ไฟล์ DLL ถูกล็อกไว้ การคัดลอกทับจะล้มเหลวด้วยข้อความว่าไฟล์ถูกใช้งานอยู่ วิธีที่ ANCM รองรับคือวางไฟล์ app_offline.htm ไว้ที่รากของแอป ระบบจะหยุดแอปและแสดงเนื้อหาไฟล์นั้นแทนชั่วคราว
Set-Content C:\inetpub\myapp\app_offline.htm '<h1>Maintenance in progress</h1>' -Encoding UTF8
Copy-Item C:\deploy\myapp\* C:\inetpub\myapp -Recurse -Force
Remove-Item C:\inetpub\myapp\app_offline.htm
เมื่อลบไฟล์ออก คำขอถัดไปจะเปิดแอปเวอร์ชันใหม่ทันที ผู้ใช้เห็นหน้าปิดปรับปรุงเพียงไม่กี่วินาที
ขั้นตอนที่ 6: เปิด stdout log เมื่อแอปไม่ยอมขึ้น
เริ่มจาก Event Viewer › Windows Logs › Application แล้วดูรายการที่ Source เป็น IIS AspNetCore Module V2 ซึ่งมักบอกสาเหตุคร่าว ๆ ถ้ายังไม่ชัด ให้เปิด stdout log ชั่วคราว
New-Item -ItemType Directory C:\inetpub\myapp\logs -Force
icacls C:\inetpub\myapp\logs /grant "IIS AppPool\MyAppPool:(OI)(CI)M"
# แก้ web.config ให้ stdoutLogEnabled="true" แล้ว recycle pool
Restart-WebAppPool -Name MyAppPool
เปิดเว็บหนึ่งครั้งแล้วอ่านไฟล์ stdout_*.log ในโฟลเดอร์ logs ซึ่งจะมี exception ตัวจริง เช่นเชื่อมต่อฐานข้อมูลไม่ได้ อีกวิธีที่เร็วคือรัน dotnet .\MyApp.dll จากโฟลเดอร์แอปตรง ๆ เพื่อดู error บนหน้าจอ เมื่อแก้เสร็จต้องตั้ง stdoutLogEnabled กลับเป็น false เพราะ log นี้ไม่มีการหมุนไฟล์และจะโตจนดิสก์เต็มได้
ปัญหาที่พบบ่อยเกี่ยวกับ Hosting Bundle และ ASP.NET Core บน IIS
ดูรหัสบนหน้า error แล้วเทียบตารางนี้ก่อนเปิด log ช่วยให้หาสาเหตุได้เร็วขึ้น
เปิดเว็บแล้วขึ้น HTTP Error 500.19 รหัส 0x8007000d ต้องแก้อย่างไร?
IIS อ่าน web.config ไม่ออกเพราะไม่รู้จักโมดูล AspNetCoreModuleV2 แปลว่ายังไม่ได้ติดตั้ง Hosting Bundle หรือติดตั้งก่อน IIS ให้รันตัวติดตั้งแล้วเลือก Repair จากนั้นรัน net stop was /y และ net start w3svc
Error 500.30 In-Process Startup Failure เกิดจากอะไร?
แอปล้มระหว่างเริ่มทำงาน ส่วนใหญ่เป็นค่าตั้งของแอปเอง เช่น connection string ผิด ขาด environment variable หรือ Application Pool ไม่มีสิทธิ์อ่านไฟล์ที่ต้องใช้ เปิด stdout log ตามขั้นตอนที่ 6 หรือรัน dotnet MyApp.dll ตรง ๆ เพื่อดู exception
Error 500.31 Failed to Find Native Dependencies แก้อย่างไร?
เซิร์ฟเวอร์ไม่มี Runtime รุ่นหลักที่แอปต้องการ เช่นแอปเป็น net8.0 แต่ติดตั้งไว้เฉพาะ .NET 10 ให้ติดตั้ง Hosting Bundle รุ่น 8 เพิ่ม แอปจะไม่ข้ามไปใช้ Runtime รุ่นหลักที่ใหม่กว่าเองตามค่าเริ่มต้น
Error 502.5 Process Failure ต่างจาก 500.30 อย่างไร?
502.5 เกิดในโหมด outofprocess เมื่อโปรเซส dotnet เริ่มไม่สำเร็จ เช่น processPath ผิด Runtime ไม่ตรง หรือแอป 64 บิตถูกรันใน Pool ที่เปิด Enable 32-Bit Applications ส่วน 500.30 เกิดในโหมด inprocess ตรวจค่าใน web.config และสถาปัตยกรรมของ Pool ให้ตรงกับแอป
ย้ายแอปที่สองเข้า Application Pool เดิมแล้วขึ้น 500.35 ทำไม?
โหมด inprocess รองรับหนึ่งแอปต่อหนึ่ง worker process เท่านั้น สร้าง Application Pool ใหม่สำหรับแอปที่สองตามขั้นตอนที่ 3 แล้วผูกเว็บไซต์หรือ Application นั้นกับ Pool ใหม่
เมื่อแอปขึ้นแล้ว ต่อด้วย ขอ SSL ฟรีบน IIS ด้วย win-acme เพื่อเปิด HTTPS ถ้าต้องการเซิร์ฟเวอร์สำหรับแอป .NET หรือให้ช่วยดูปัญหาการ deploy ติดต่อทีมงาน THAI DATA CLOUD หรือดูแพ็กเกจที่ Cloud Windows Server รายละเอียดเชิงลึกอ่านได้จาก เอกสาร .NET Hosting Bundle และ การแก้ปัญหา ASP.NET Core บน IIS ของ Microsoft Learn
อ่านเพิ่มเติมเกี่ยวกับ Hosting Bundle