"It works on my machine" is not a deployment strategy. Modern ASP.NET Core applications are designed to be cross-platform, meaning they can be deployed to Windows Server (IIS), Linux (Nginx/Apache), or containerized in Docker and orchestrated by Kubernetes. Choosing your hosting model correctly dictates your app's scalability and maintenance costs.
ASP.NET Core applications run on a web server called Kestrel. Kestrel is lightweight, insanely fast, and cross-platform. However, by itself, it lacks advanced features like process management or port sharing. Thus, we usually place a "Reverse Proxy" (like IIS or Nginx) in front of Kestrel.
The ASP.NET Core app is hosted directly inside the IIS worker process (w3wp.exe). There is only one web server running. This provides the highest performance for Windows deployments because it avoids the overhead of internal HTTP loopback routing.
IIS or Nginx acts as a Reverse Proxy. It listens on Port 80/443, handles SSL termination and security, and then forwards the raw request to Kestrel running as a separate background process on Port 5000. Required for Linux deployments.
To run ASP.NET Core on a Windows Server, you must install the ASP.NET Core Hosting Bundle. This installs the .NET Runtime and the ASP.NET Core IIS Module.
# 1. Publish the application locally via CLI
dotnet publish --configuration Release --output ./publish_folder
# 2. Copy the contents of ./publish_folder to Windows Server (e.g., C:\inetpub\wwwroot\MyApp)
# 3. Create a Website in IIS Manager pointing to that path.
# 4. Set the Application Pool to "No Managed Code" (CRITICAL, because .NET Core manages itself).
Linux does not have IIS. You must run the compiled DLL as a system service (using `systemd`) and use Nginx to route traffic strictly to that service.
# 1. Install .NET runtime on the Linux Server (Ubuntu example)
sudo apt-get install -y aspnetcore-runtime-8.0
# 2. Create a systemd service file (/etc/systemd/system/myapp.service)
[Unit]
Description=ASP.NET Core MVC Application
[Service]
WorkingDirectory=/var/www/myapp
# EXECUTING THE APP
ExecStart=/usr/bin/dotnet /var/www/myapp/MyApp.dll
Restart=always
RestartSec=10
Environment=ASPNETCORE_ENVIRONMENT=Production
[Install]
WantedBy=multi-user.target
# 3. Nginx Server Block (/etc/nginx/sites-available/default)
server {
listen 80;
server_name www.myapp.com;
location / {
# Forward external traffic exactly to Kestrel's internal port
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
PaaS solutions completely abstract away the server configuration. You focus strictly on code.
.zip deployment package and pushes it to Azure upon every merge to the main branch.When you run dotnet publish, you have a crucial choice:
| Deployment Mode | Requirements on Target Server | File Size | Use Case |
|---|---|---|---|
| Framework Dependent (Default) | Must have .NET Runtime exactly installed. | Very Small (~5MB) | PaaS (Azure) or closely managed internal servers. |
| Self-Contained | Nothing. | Huge (~100MB+) | Deploying to client environments where you cannot install software. |
appsettings.Production.json). Use Azure Key Vault, AWS Secrets Manager, or OS-level environment variables to inject them at runtime.Q: "Why do we still use IIS or Nginx if Kestrel is a fully functional web server?"
Architect Answer: "While Kestrel's raw HTTP serving speed outperforms almost any other web server globally, it is intentionally designed to be highly focused. It historically lacked advanced edge-server features. Placing IIS or Nginx in front of Kestrel provides a robust 'Reverse Proxy' to handle SSL/TLS termination, port sharing (allowing Node.js and .NET apps to both respond on Port 443), static file caching, load balancing, and process management (restarting the Kestrel process if it crashes recursively)."