IntermediateLesson 7 of 16

IIS Sites, Application Pools, and Bindings

The operational building blocks of IIS — how to create sites, configure application pools for isolation and performance, set bindings for multi-site hosting, and manage these confidently from both IIS Manager and the command line.

🧒 Simple Explanation (ELI5)

Think of IIS as an apartment complex. Each apartment is a Website — it has its own address (binding: IP, port, hostname). Each apartment building has its own security guard and maintenance crew — that is the Application Pool. The building rules (configuration) say which crew handles which building. If the crew of Building A causes a problem, Building B's crew is completely unaffected because they work in separate offices on different contracts. The door number and street name together form the binding — the exact address by which visitors find the right apartment.

🔧 Why Do We Need It?

🌍 Real-world Analogy

IIS Sites are like tenants in a shared office building. Each tenant (site) has their own mailbox address (binding: hostname + port), their own office floor (physical path), their own staff badge (pool identity), and their own cleaning crew (application pool process). The building manager (IIS) has a master directory of which floor maps to which address. New tenants can be added without disturbing existing ones. If one tenant's office floods (app crash), only that office closes for repairs — the rest of the building operates normally.

⚙️ Technical Explanation

IIS Bindings are the network address combinations that a site listens on. A binding has three parts: IP Address (a specific IP on the server, or * for all), Port (80 for HTTP, 443 for HTTPS), and Hostname (the Host header value, e.g., api.example.com, or empty to match any hostname). A site can have multiple bindings — e.g., both HTTP/80 and HTTPS/443, or multiple HTTPS hostnames using SNI. Binding conflicts occur when two sites claim the same IP:Port:Hostname combination — IIS refuses to start the second site's binding.

Application Pools are worker process containers. Configuration parameters that matter in production: Managed Runtime Version (v4.0 for .NET 4.x Framework, No Managed Code for ASP.NET Core). Managed Pipeline Mode (Integrated for all .NET apps; Classic only for legacy ISAPI). Identity (ApplicationPoolIdentity = virtual account, or a specific domain/local account). Recycling (time-based and request-count triggers, plus Private Memory threshold). Rapid Fail Protection (maximum crash count before pool disable). CPU Limit (% of CPU the worker process may use, with optional Throttle or Kill action when exceeded). Queue Length (maximum pending requests in HTTP.sys queue — default 1000).

Sites vs Applications: A Site is the top-level container bound to a network address. An Application is a named path within a site that runs in its own app pool. For example: Site "Corporate" bound to corporate.example.com with physical path C:\web\corporate contains Application "/" running in pool "Corp-Main" and Application "/hr" running in pool "Corp-HR" pointing to C:\web\corporate-hr. This lets you host two .NET applications under the same hostname with different pool identities and .NET versions.

⚠️
Application Pool Advanced Settings — Production Critical

In IIS Manager → App Pool → Advanced Settings, the most critical production settings are: Maximum Worker Processes (default 1 — increase only if you fully understand Web Garden implications). Private Memory Limit (KB) (default 0 = unlimited — set this to prevent OOM crashes from taking down the whole pool). Regular Time Interval (minutes) (default 1740 = ~29 hours — schedule this for low-traffic windows, not random). Idle Time-out (minutes) (default 20 — pools that receive no requests for 20 minutes are stopped; first-request starts a new process and users see a delay. Disable for always-on APIs: set to 0). Shutdown Time Limit (seconds) (default 90 — time given to w3wp.exe to finish in-flight requests during a recycle before it is force-killed).

📊 Visual Representation

IIS Site, Application, and Pool Relationships
Site: example.com
Binding: *:80 (http://example.com)
Binding: *:443 (https://example.com)
Root App (/) → Pool: web-main
App (/api) → Pool: web-api
App (/admin) → Pool: web-admin
Application Pools
web-main → w3wp.exe PID 1234
.NET v4.0, Integrated, IIS AppPool\web-main
web-api → w3wp.exe PID 5678
No Managed Code, ANCM, IIS AppPool\web-api
web-admin → w3wp.exe PID 9012
.NET v4.0, Integrated, DOMAIN\svc-admin

⌨️ Commands / Syntax

cmd / PowerShell
# === SITES ===
# Create a new site (appcmd)
%windir%\system32\inetsrv\appcmd add site /name:"MySite" /id:2 ^
  /physicalPath:"C:\inetpub\wwwroot\MySite" ^
  /bindings:"http/*:80:mysite.local"

# List all sites
appcmd list site

# Change physical path
appcmd set vdir /vdir.name:"MySite/" /physicalPath:"D:\NewPath\MySite"

# Set bindings on existing site
appcmd set site /site.name:"MySite" /+bindings.[protocol='https',bindingInformation='*:443:mysite.local']

# Start / Stop a site
appcmd start site /site.name:"MySite"
appcmd stop site /site.name:"MySite"

# === APP POOLS ===
# Create an app pool (PowerShell)
Import-Module WebAdministration
New-WebAppPool -Name "MyPool"
Set-ItemProperty IIS:\AppPools\MyPool -Name managedRuntimeVersion -Value "v4.0"
Set-ItemProperty IIS:\AppPools\MyPool -Name managedPipelineMode -Value "Integrated"

# Set pool identity to virtual account (default)
Set-ItemProperty IIS:\AppPools\MyPool -Name processModel.userName -Value ""
Set-ItemProperty IIS:\AppPools\MyPool -Name processModel.identityType -Value 4  # 4 = AppPoolIdentity

# Set pool recycling to specific time (3:00 AM)
$pool = Get-Item IIS:\AppPools\MyPool
Clear-ItemProperty $pool -Name recycling.periodicRestart.schedule
New-ItemProperty -Path "IIS:\AppPools\MyPool" -Name "recycling.periodicRestart.schedule" -Value @{value="03:00"}

# Set Private Memory limit (2 GB)
Set-ItemProperty IIS:\AppPools\MyPool -Name recycling.periodicRestart.privateMemory -Value 2097152

# Set Idle Timeout to 0 (no shutdown for always-on pools)
Set-ItemProperty IIS:\AppPools\MyPool -Name processModel.idleTimeout -Value "00:00:00"

# Recycle, Stop, Start app pool
appcmd recycle apppool /apppool.name:"MyPool"
appcmd stop apppool /apppool.name:"MyPool"
appcmd start apppool /apppool.name:"MyPool"

# === APPLICATIONS ===
# Create a sub-application under a site
appcmd add app /site.name:"MySite" /path:"/api" /physicalPath:"C:\inetpub\wwwroot\api" /applicationPool:"MyPool"

💼 Example (Real-world Use Case)

A company runs 5 microservices under the same domain (api.company.com). Each service is an ASP.NET Core app. The setup: one IIS site "api.company.com" with SNI HTTPS binding on port 443. Five applications under it — /auth, /billing, /inventory, /notifications, /reports. Each application has its own app pool (auth-pool, billing-pool, etc.) running as "No Managed Code" with ANCM hosting. Each pool runs as a separate gMSA with only the database and queue permissions its specific microservice needs. Benefits: billing can be deployed and recycled independently; if /reports crashes its pool, all other services continue. The operations team monitors each pool's memory and CPU independently via Performance Monitor.

🧪 Hands-on

  1. Create a new application pool in IIS Manager: right-click Application Pools → Add Application Pool. Name it "TestPool", set .NET CLR version to v4.0, Managed Pipeline Mode to Integrated. Click OK.
  2. Create a new website: right-click Sites → Add Website. Name: "TestSite", Application Pool: assign TestPool, Physical Path: C:\inetpub\wwwroot\TestSite (create this directory first). Binding: HTTP, All Unassigned, port 8080 (avoid conflict with Default Web Site on port 80). Click OK.
  3. Create a test index.html in C:\inetpub\wwwroot\TestSite. Browse http://localhost:8080 — confirm the page appears.
  4. In IIS Manager, right-click TestPool → Advanced Settings. Set: Idle Time-out to 0; Regular Time Interval to 0; Private Memory to 1048576 (1 GB). Click OK. These are the settings for a production always-on application pool.
  5. Recycle the pool from command line: appcmd recycle apppool /apppool.name:TestPool. Immediately run appcmd list wp — observe a new w3wp.exe PID for TestPool (the recycle created a new process). Browse the site again to confirm it remains accessible throughout.
💡
Site Isolation vs Resource Cost

Every separate application pool requires a w3wp.exe worker process, which uses baseline RAM (~10-20 MB before application code loads). On a server hosting 50 sites with 50 pools, this baseline is 500 MB-1 GB just from empty worker processes. Balance isolation with resource cost: group low-criticality internal tools into one pool; keep production customer-facing apps in their own pools. On servers with limited RAM, consider reducing idle timeout to 5 minutes for rarely-used apps — the pool shuts down when idle and restarts on demand, keeping memory free for active pools.

🐛 Debugging Scenario

Failure: A newly added site returns HTTP 503 immediately after configuration. The app pool shows as Stopped in IIS Manager.

🎯 Interview Questions

Beginner

What is a binding in IIS and what are its three components?

A binding defines where and how a website listens for traffic. It has three components: IP Address — a specific IP address on the server or * (all unassigned IPs). Port — the TCP port (80 for HTTP, 443 for HTTPS, or any custom port). Host Name (Host Header) — the HTTP Host header value the site responds to (e.g., www.example.com). All three together form the unique key. Two sites cannot share the same IP:Port:HostName combination. Multiple bindings on one site allow it to answer on HTTP and HTTPS, or on multiple hostnames (with or without SNI). An empty hostname means "respond to any Host header on this IP:port that isn't claimed by another site."

What is the DefaultAppPool and should production apps use it?

DefaultAppPool is the application pool that IIS creates automatically with the Default Web Site. It runs as ApplicationPoolIdentity under .NET v4.0, Integrated pipeline mode. Production applications should NOT use DefaultAppPool because: (1) it is shared — if you put multiple sites or apps in DefaultAppPool, they lose isolation; a crash in one app crashes the pool for all. (2) It uses the generic system account rather than a dedicated identity — security principle of least privilege requires each app to run under its own identity. (3) Default recycling settings (29-hour cycle at an arbitrary time) are not appropriate for production. Best practice: create a named application pool for every production application.

What is the idle timeout setting on an app pool and what happens when it fires?

The idle timeout setting (default 20 minutes) causes WAS to shut down the worker process if no new requests arrive within that period. When the pool is idle-stopped and a new request arrives, WAS must start a new w3wp.exe, initialise the CLR, warm up application state, and process the request — this creates a "cold start" delay of potentially tens of seconds for the first request after idle. For always-on production APIs (especially those with health check pings from load balancers), set idle timeout to 0 to disable it. For infrequently accessed internal tools, the default is fine as it conserves memory. Never use idle timeout with Application Initialization warm-up — they conflict.

How do you host two different websites on the same IP and port 80 in IIS?

Use Host Headers (also called Host Name in the binding configuration). Configure Site A with binding: *:80:siteA.example.com. Configure Site B with binding: *:80:siteB.example.com. DNS must resolve both hostnames to the same server IP. When an HTTP request arrives, HTTP.sys reads the Host header from the request and routes it to the matching site. This is the standard method for multi-site IIS hosting — you can host hundreds of sites on a single IP:port combination, each identified by its Host header value. IIS 10.0 added Wildcard Host Headers support (*.example.com) to capture all subdomains with a single binding.

What does "Application Pool Identity" mean as the default pool identity type?

Application Pool Identity (identityType=4) is a virtual service account automatically created by IIS when an application pool is started. The account name follows the pattern IIS AppPool\PoolName. It is not a real user account you can log into — it exists only while the pool is running. Benefits: automatic creation (no manual account management), automatic membership in IIS_IUSRS group, minimal privilege (no logon rights, no admin rights), and isolated between pools (IIS AppPool\Pool1 and IIS AppPool\Pool2 are different security principals). Limitation: it cannot authenticate to remote resources over the network — for network access (SQL Server, file shares, AD), use a gMSA or domain user account instead.

Intermediate

How do you set up CPU rate limiting on an application pool?

In App Pool Advanced Settings → CPU section: set Limit (percentage). Specify in thousandths of a percent: 25% = 25000. Set Limit Action: NoAction (just log), KillW3wp (terminate the process when limit is reached), Throttle (reduce CPU allocation but don't kill), ThrottleUnderLoad (throttle only when overall CPU is high). Set Limit Interval (minutes, default 5 — the window over which CPU is averaged). Example: set 50000 (50%) with Throttle action to prevent any single app pool from monopolising more than half the CPU. This is critical for shared-hosting environments. PowerShell: Set-ItemProperty IIS:\AppPools\MyPool -Name cpu.limit -Value 50000; Set-ItemProperty IIS:\AppPools\MyPool -Name cpu.action -Value "Throttle".

What is the maximum number of sites IIS can host and what are the practical limits?

Technically IIS supports tens of thousands of sites — there is no hard-coded limit in the software. Practical limits: (1) Memory: each app pool worker process uses 10-50 MB baseline. 1000 pools × 50 MB = 50 GB RAM required. (2) SSL: each HTTPS site traditionally needed a dedicated IP for SSL (pre-SNI). With SNI (IIS 8+), multiple HTTPS sites share one IP. (3) W3SVC startup: IIS reads all site configuration on start — thousands of sites cause long startup delays. (4) applicationHost.config: the configuration file gets large and complex with hundreds of sites. Real-world large IIS deployments (shared hosting) keep pools to 50-200 and use idle timeout to keep only active pools alive. For elastic scaling, prefer containerised deployments (Windows containers) over packing hundreds of sites on one IIS server.

How do you use appcmd to bulk-export and import IIS site configuration?

Export full IIS config including sites, pools, and bindings: %windir%\system32\inetsrv\appcmd list config /config.section:system.applicationHost > applicationHost_backup.xml. More precisely for migration: appcmd add backup "PreMigrationBackup" — this creates a backup in %windir%\system32\inetsrv\backup\PreMigrationBackup. To restore: appcmd restore backup "PreMigrationBackup". For individual site export: use Web Deploy (msdeploy) which packages a site with its content, config, and certificates. The IIS configuration backup/restore approach is the fastest way to recover a corrupted applicationHost.config without reinstalling IIS — a critical runbook step in disaster recovery planning for IIS environments.

What is the difference between a site physical path and a virtual directory physical path?

The site's physical path is the root directory for the site's root application (/). All relative URL paths that don't match an application or virtual directory map inside this root path. A virtual directory's physical path is a separate directory that is mapped to a URL alias within the site. Example: Site root = C:\inetpub\wwwroot\MySite → request to /index.html serves C:\inetpub\wwwroot\MySite\index.html. Virtual directory /uploads → D:\SharedUploads → request to /uploads/file.pdf serves D:\SharedUploads\file.pdf. Virtual directories are used when content lives on a different drive/server (mapping UNC paths like \\fileserver\docs is also supported). Important: virtual directories inherit the app pool identity of their parent application for NTFS access checks.

What is SNI and why is it important for IIS HTTPS bindings?

SNI (Server Name Indication) is a TLS extension that allows the client to specify which hostname it wants to connect to during the TLS handshake — before the server has sent the certificate. Before SNI, each HTTPS hostname required its own dedicated IP address (because TLS handshake happens before the HTTP Host header is sent — so IIS couldn't distinguish which certificate to present without reading the Host header, which wasn't available yet). With SNI: IIS reads the SNI extension in the TLS ClientHello and selects the matching certificate. Multiple HTTPS sites can share one IP. IIS 8.0 (Server 2012) added SNI support. Configure in IIS Manager → Bindings → Add HTTPS binding → check "Require Server Name Indication" and enter the hostname. Required for Let's Encrypt certificates on multi-site IIS servers.

Scenario-based

You need to migrate 20 sites from one IIS server to another. What is your step-by-step plan?

1. Install IIS and the web-deploy (msdeploy) role on the new server. Match IIS version, .NET Framework versions, and URL Rewrite/ARR modules. 2. Export each site with msdeploy: msdeploy -verb:sync -source:webServer,computerName=OldServer -dest:package=allsites.zip or per-site packages for individual control. 3. Export SSL certificates from the old server's certificate store (MMC → Certificates → Personal → export with private key as PFX). 4. Import SSL certificates on the new server, noting the new thumbprints. 5. Import sites from package: msdeploy -verb:sync -source:package=allsites.zip -dest:webServer,computerName=NewServer. 6. Update SSL certificate bindings in IIS to reference the new thumbprints: netsh http show sslcert and re-bind with appcmd set site. 7. Verify NTFS permissions on all site physical paths for the pool identities. 8. Test all 20 sites locally on the new server (modify hosts file). 9. Switch DNS. 10. Keep old server live for rollback for 24-48 hours.

A new site deployment fails with "The binding 'http/*:80:myapp.company.com' already exists." What do you do?

This means another IIS site already has that exact binding. Find it: appcmd list site /bindings:"http/*:80:myapp.company.com". If the conflicting site is an old decommissioned site: delete or stop it, then add the binding to the new site. If the conflicting site is active and serving traffic: you need to differentiate them. Options: (a) use a different port for the new site (if it's internal and users can reach a non-standard port); (b) use HTTPS on the new binding instead; (c) update the conflicting site's binding to use a different hostname if appropriate. If the conflicting binding is on the same site but you want to add HTTPS: add a new binding for https/*:443:myapp.company.com — the HTTP and HTTPS bindings can coexist for the same hostname. Never delete a binding from a live site without confirming with the site's owner first.

Management asks why the same IIS server hosts some pools that are always running and others that randomly stop. What explains this and how do you fix it?

The randomly stopping pools have the idle timeout setting enabled (default 20 minutes). When no requests arrive for 20 minutes, WAS shuts down the worker process. The pool itself remains configured and will restart on the next request (a 5-30 second delay for the cold start). This is intentional IIS behaviour to conserve memory on servers with many low-traffic sites. To fix for specific critical pools: in App Pool Advanced Settings → Process Model → "Idle Time-out (minutes)" set to 0. Alternatively, configure a health-check endpoint to ping the pool every few minutes, keeping it alive without changing configuration (a common approach for SLA-critical sites that must have sub-second response on all requests, even after quiet periods).

Two developers deploy versions of the same app to the same IIS server simultaneously. One says their version works, the other says theirs doesn't. Both are the root application on the same site. What is happening and how do you resolve it?

They are deploying to the same physical path and the same application pool. The second deployment's files are overwriting the first's (or partially overwriting causing a hybrid broken state). If the app pool hasn't recycled, some in-memory state from the first deployment is still running with files from the second on disk — catastrophic mix. Resolution: each developer or release needs its own isolated environment. Correct setup: create separate IIS applications under the site — /app-devA (physical path C:\dev\app-A, pool DevA-Pool) and /app-devB (physical path C:\dev\app-B, pool DevB-Pool). In production, use a staging slot pattern (Blue/Green): site "app-blue" and site "app-green" with a single load balancer rule switching traffic between them after validation. Never let two deployments share the same physical directory or application pool without a clear, atomic file swap mechanism.

How do you configure application pool recycling to prevent any customer-impacting downtime?

The key is overlapping recycle (default enabled). Confirm it is on: App Pool → Advanced Settings → "Disable Overlapping Recycle" = False. Configure recycling at low-traffic time: set Specific Times under Recycling (e.g., 3:00 AM), remove the default 29-hour interval recycle (set Regular Time Interval to 0). Set Shutdown Time Limit to 300 seconds (give long-running in-flight requests adequate time to drain). Enable Application Initialization (Web-AppInit feature): configure startMode="AlwaysRunning" on the app pool and create a warm-up initializationPage in the application. IIS will spin up the new worker process, send the warm-up request, wait for a 200 response before switching HTTP.sys routing to the new process. With these settings, users will not see any visible interruption during a scheduled pool recycle — the new process is fully warm before any public traffic reaches it.

🌐 Real-world Usage

Sites, pools, and bindings are the bread-and-butter of everyday IIS administration. Adding a new application, updating a binding when a site moves to a new hostname, changing a pool identity for a database-connected service, and tuning recycling parameters for a critical API are all daily tasks for operations engineers. Fluency with both IIS Manager and appcmd/PowerShell is expected in every Windows infrastructure role.

📝 Summary

Sites are network-addressable containers bound to IP:port:hostname combinations. Application pools isolate process lifetime, identity, .NET version, and resource limits. Applications within sites run in named pools, enabling independent deployment and security. Master these three concepts in IIS Manager and from the command line — they underlie every IIS configuration decision.