Skip to content

Deleting app_offline.htm does not restart ASP.NET Core app hosted on IIS #53894

@peterwurzinger

Description

@peterwurzinger

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

I genuinly don't know if this is a bug, but the observed behavior was a bit odd, and even after extensive research I couldn't find out if it's intended or not.

Given an arbitrary ASP.NET Core 8 application that is hosted via the in-process model behind an IIS 10 site. Given also the fact, that the environment (app pool, site, application,...) is configured to keep the app alive permanently and preload it, therefore

  • Set the corresponding application pool to "Always running"
  • Set the idle timeout to 0:00
  • Enable preload
  • Set "serverAutoStart" to "true" on the corresponding site

So when a file named 'app_offline.htm' is placed in the application root, the ANCM picks that up and serves it along with a status of HTTP 503. The application logs show, that it was asked to shut down gracefully.

Now when deleting 'app_offline.htm' I expected the app to be started again immediately. But the logs show, that it is not started until the first request arrives, so basically that's the behavior you would expect on an app that does has preloading disabled.

I guess that behavior is fine for most web applications, but it actually is quite a problem for applications that have some background workers, like Message Bus listeners, etc. since those would not be started either.

Expected Behavior

I would have expected, that deleting 'app_offline.htm' would cause the app to restart immediately, and not on the first arriving request.

Steps To Reproduce

I tested it with an app that hosts a background service, just to see when the service will actually be started.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHostedService<UpDownService>();

var app = builder.Build();

app.MapGet("/", async (r) =>
{
  r.Response.StatusCode = 200;
  await r.Response.WriteAsync("Test");
});

app.Run();

public class UpDownService(ILogger<UpDownService> logger) : IHostedService
{
  public Task StartAsync(CancellationToken cancellationToken)
  {
    logger.LogWarning("This service has been started.");
    return Task.CompletedTask;
  }

  public Task StopAsync(CancellationToken cancellationToken)
  {
    logger.LogWarning("This service has been stopped.");
    return Task.CompletedTask;
  }
}

The corresponding web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <system.webServer>
    <applicationInitialization doAppInitAfterRestart="true">
      <add initializationPage="/" />
    </applicationInitialization>
    <aspNetCore />
  </system.webServer>
</configuration>

The simplified logs would now look something like that - note that lines starting with '>' are not visible in the logs:

This service has been started.
  > app_offline.htm was placed in application root
This service has been stopped.
  > app_offline.htm was deleted from application root
  > Request to "/" arrives
This service has been started.

and the relevant parts from applicationHost.config:

<site name="UpDownTest" id="8" serverAutoStart="true">
  <application path="/" applicationPool="UpDownTest" preloadEnabled="true">
    <virtualDirectory path="/" physicalPath="E:\UpDownTest" />
  </application>
  ...
</site>

...

        <applicationPools>
            <add name="UpDownTest" autoStart="true" ... />

Exceptions (if any)

No exceptions

.NET Version

.NET 8.0
ASP.NET Core 8
.NET SDK 8.0.100

EDIT: Sorry, I opened the issue too early by accident.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-networkingIncludes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractionsfeature-iisIncludes: IIS, ANCMquestion

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions