One of the key elements to understand, as an IT professional (Mostly working with Windows) that’s transitioning to DevOps or Platform Engineering, is everything that surrounds code. If you maintain servers for applications, you’ve likely encountered scenarios where a seemingly straightforward application fails to deploy or fails after deployment. Perhaps they’ve copied all the files to the right locations, but the application refuses to run. Or maybe it works on one server but not another, even though they appear identical at first glance.

The root of these problems, aside from networking and having the correct ports opened to different services if you are in an air-gapped environment, often lies in an incomplete understanding of the application stack – the complete set of software components required for an application to run properly. In this article, we’ll explain application stacks fundamentals, focusing on Windows server environments and .NET applications as an example. I’ll explain how the various layers interact and how to ensure your servers are properly configured before deploying code.

What Is an Application Stack?

An application stack is like a layer cake. Each layer provides essential functionality that the layers above it depend on. If any layer is missing or misconfigured, the entire application may fail to run correctly – or at all.

Consider a typical .NET web application. From bottom to top, its stack might include:

  1. The operating system (Windows Server)
  2. Required Windows features (IIS, necessary Windows components)
  3. Runtime environments (.NET Framework or .NET Core)
  4. Middleware components (ASP.NET, Entity Framework)
  5. The application code itself

Let’s break down each of these components to understand their role in the stack.

The Foundation: Operating System and Windows Features

At the base of our application stack is the operating system. For .NET applications, this is typically a Windows Server environment. However, simply having Windows Server with runtimes installed isn’t enough – you also need IIS from Windows features.

Internet Information Services (IIS)

IIS is Microsoft’s web server software that handles HTTP requests and responses. For web applications, IIS is essential, but it’s not a monolithic feature. IIS comprises multiple components and features, each serving a specific purpose, examples below.

  • Web Server (IIS) – The core feature that enables the server to respond to HTTP requests
  • IIS Management Console – The GUI tool for configuring IIS
  • Basic Authentication – For simple username/password authentication
  • Windows Authentication – For integrated Windows authentication
  • URL Rewrite Module – For manipulating requested URLs based on defined rules

Think of IIS features as specialized tools in a toolbox. Installing all IIS features on every server would be like carrying the entire toolbox to every job when you only need a screwdriver. Understanding which features your application requires is critical for proper configuration and security.

Picking, ONLY, the necessary features is also essential for good security. We often see admins that enable all features in IIS and move on.

How Missing IIS Features or too many features Cause Problems

Imagine deploying a web application that uses Windows Authentication. If the Windows Authentication feature isn’t installed on IIS, users will receive authentication errors even though the application code is perfectly valid. These issues can be perplexing because they’re not caused by bugs in the code but by missing infrastructure components.

The Engines: Runtime Environments

Runtimes are the engines that execute your application code. They provide the necessary libraries and services for your application to run. In the .NET ecosystem, the most common runtimes are:

.NET Framework Runtime

The traditional .NET Framework is Windows-only and includes:

  • CLR (Common Language Runtime) – Executes the compiled code
  • Base Class Library – Provides fundamental types and functionality

Applications targeting specific versions of .NET Framework (e.g., 4.6.2, 4.7.2, 4.8) require that exact version installed on the server.

.NET Core/.NET Runtime

The newer, cross-platform .NET implementation includes:

  • .NET Runtime – The basic runtime for console applications
  • ASP.NET Core Runtime – Additional components for web applications
  • .NET Desktop Runtime – Components for Windows desktop applications
  • Web Hosting Bundle – Combines the ASP.NET Core Runtime with the IIS integration module

Why Runtimes Matter

Runtimes are version-specific. An application built for .NET Core 3.1 won’t run on a server with only .NET 5 installed, even though .NET 5 is newer. This version specificity is a common source of deployment issues.

Consider this real-world scenario: A development team builds an application using .NET Core 3.1. The production server has .NET 5 installed. When deployed, the application fails with cryptic errors about missing assemblies. The solution isn’t to fix the code but to install the correct runtime on the server.

The Bridges: Middleware and Frameworks

Between the runtime and your application code lies middleware – components that provide additional functionality beyond what the basic runtime offers. In .NET applications, this often includes:

  • ASP.NET (for .NET Framework) or ASP.NET Core (for .NET Core/.NET) – For web applications
  • Entity Framework – For database access
  • SignalR – For real-time communications

Middleware components can have their own dependencies and version requirements. For example, an application using Entity Framework Core 3.1 needs compatible versions of other components.

The Pinnacle: Application Code

At the top of the stack sits your application code – the custom software that provides the specific functionality your users need. This includes:

  • Compiled assemblies (.dll files)
  • Configuration files
  • Static content (HTML, CSS, JavaScript, images)
  • Client-side libraries

While this is the most visible part of the stack, it cannot function without all the layers beneath it.

Bringing It All Together: A Practical Example

Let’s examine a concrete example to illustrate how all these components interact:

Scenario: Deploying a .NET Core 3.1 MVC web application that uses Windows Authentication and connects to a SQL Server database.

Required stack components:

  1. Operating System: Windows Server 2019
  2. Windows Features:
    • IIS Web Server
    • Windows Authentication
    • ASP.NET 4.8 (for backward compatibility with some components)
  3. Runtimes:
    • .NET Core 3.1 SDK (for development servers)
    • .NET Core 3.1 ASP.NET Core Runtime (for production servers)
    • .NET Core 3.1 Hosting Bundle (which installs the ASP.NET Core Module for IIS)
  4. Middleware:
    • Entity Framework Core 3.1
  5. Application Code:
    • Your custom application DLLs
    • Configuration files (appsettings.json)
    • Static web content

If any component is missing from this stack, the application won’t function correctly. For instance:

  • Without the Windows Authentication feature, users can’t log in.
  • Without the .NET Core 3.1 Runtime, the application won’t start.
  • Without the ASP.NET Core Module, IIS won’t know how to handle requests for the application.

Best Practices for Managing Application Stacks

Now that we understand what makes up an application stack, let’s look at some best practices for managing them:

1. Document Your Application Stack

Create detailed documentation of every component required for your application, including specific versions. This documentation should be maintained alongside your codebase and updated whenever dependencies change.

2. CICD and Server Setup Scripts

Automate the installation and configuration of your application stack using PowerShell scripts or configuration management tools. This ensures consistency across environments and makes it easier to set up new servers.

# Example PowerShell script to install required IIS components for a .NET Core application
# Enable IIS and required features

$features = @(
    'Web-Default-Doc',
    'Web-Dir-Browsing',
    'Web-Http-Errors',
    'Web-Static-Content',
    'Web-Http-Redirect',
    'Web-Http-Logging',
    'Web-Custom-Logging',
    'Web-Log-Libraries',
    'Web-ODBC-Logging',
    'Web-Request-Monitor',
    'Web-Http-Tracing',
    'Web-Stat-Compression',
    'Web-Dyn-Compression',
    'Web-Filtering',
    'Web-Basic-Auth',
    'Web-CertProvider',
    'Web-Client-Auth',
    'Web-Digest-Auth',
    'Web-Cert-Auth',
    'Web-IP-Security',
    'Web-Url-Auth',
    'Web-Windows-Auth',
    'Web-Net-Ext',
    'Web-Net-Ext45',
    'Web-AppInit',
    'Web-Asp',
    'Web-Asp-Net',
    'Web-Asp-Net45',
    'Web-ISAPI-Ext',
    'Web-ISAPI-Filter',
    'Web-Mgmt-Console',
    'Web-Metabase',
    'Web-Lgcy-Mgmt-Console',
    'Web-Lgcy-Scripting',
    'Web-WMI',
    'Web-Scripting-Tools',
    'Web-Mgmt-Service'
)

foreach ($iissharefilereq in $features){
Install-WindowsFeature $iissharefilereq -Confirm:$false
}
 # Download and install .NET Core Hosting Bundle Invoke-WebRequest -Uri 'https://download.visualstudio.microsoft.com/download/pr/48d3bdeb-c0c0-457e-b570-bc2c65a4d51e/c81fc85c9319a573881b0f8b1f671f3a/dotnet-hosting-3.1.25-win.exe' -OutFile 'dotnet-hosting-3.1.25-win.exe' Start-Process -FilePath 'dotnet-hosting-3.1.25-win.exe' -ArgumentList '/quiet' -Wait # Restart IIS to apply changes net stop was /y net start w3svc 

3. Use Configuration Verification

Implement scripts that verify server configurations before deployment. These scripts should check for all required components and their versions, alerting you to any discrepancies.

4. Consider Containerization

For more complex applications, consider containerization technologies like Docker. Containers package the application and its dependencies together, ensuring consistency across environments and eliminating many configuration issues.

5. Create Environment Parity

Ensure that your development, testing, and production environments have identical application stacks. This reduces the “it works on my machine” problem and makes testing more reliable.

6. Application Logging

Ensure that web.config has a logging directory to catch errors.

IIS web.config with logs
IIS web.config with logs


Common Pitfalls and How to Avoid Them

Several common pitfalls can trip up IT teams when managing application stacks:

Pitfall 1: Assuming Newer Is Always Better

Just because a newer version of a runtime or framework is available doesn’t mean your application is compatible with it. Always test compatibility before upgrading components in your application stack.

Pitfall 2: Incomplete Feature Installation

When installing Windows features like IIS, it’s easy to miss sub-features that your application requires. Use comprehensive installation scripts that include all necessary components.

Pitfall 3: Overlooking Dependencies

Some components have dependencies that aren’t immediately obvious. For example, certain .NET features depend on specific Visual C++ Redistributable packages. Make sure to identify and install all dependencies.

Pitfall 4: Ignoring Regional and Language Settings

Applications may behave differently based on regional settings, time zones, or character encodings. Ensure these settings are consistent across your environments.

Pitfall 5: Misconfigured Permissions

Even with all the right components installed, incorrect permissions on IIS web folder level can prevent applications from running correctly. Ensure your application has the necessary permissions to access files, folders, and other resources. The app pool usually has IDs to authenticate.

Conclusion

Understanding application stacks is crucial for successful deployment and maintenance of modern applications. By recognizing that your application is more than just the code you write – it’s a complex interplay of operating system features, runtimes, middleware, and your custom code – you can approach server configuration methodically and avoid mysterious deployment failures.

The next time you prepare to deploy an application, take the time to document and verify your application stack. Your future self (and your colleagues) will thank you when deployments go smoothly and applications run as expected in every environment.

Remember: Proper server configuration isn’t an afterthought – it’s a prerequisite for your application code to function correctly.