We still get tickets where a .NET 10 app loads templates, JSON sidecars, or a license file fine on a developer laptop and 500s the moment it hits an IIS app pool. The stack trace is almost always a FileNotFoundException on a path the author built with Directory.GetCurrentDirectory(). Under IIS that API does not mean “the folder I published.”

On many Windows hosts the worker process current directory is C:\Windows\System32\inetsrv (or another system path), not the site root ANCM is running. Relative opens silently walk off into a directory your app pool identity cannot read—or that simply does not contain your files.

#What breaks in production

Typical failures we see after a clean Web Deploy: Path.Combine(Directory.GetCurrentDirectory(), "templates") resolving under inetsrv; a custom appsettings overlay loaded with a relative path; log or export writers creating folders next to w3wp instead of beside the app; plugins or razor class libraries probing the wrong base. It worked locally because Kestrel started with the project directory as cwd. IIS does not make that promise—in-process or out-of-process.

Do not “fix” it by calling Directory.SetCurrentDirectory at startup either. That races with other middleware, background services, and anything else that already captured the original cwd. It also surprises the next person who recycles the pool.

#Bind paths to the host, not the shell

Inject IHostEnvironment (or IWebHostEnvironment) and build from ContentRootPath for app-owned files. Use WebRootPath only for static web content. For assembly-adjacent probes, AppContext.BaseDirectory is the reliable fallback when you are outside DI.

csharp
// Bad: cwd under IIS is often inetsrv, not the publish folder
// var dir = Path.Combine(Directory.GetCurrentDirectory(), "templates");

// Good: content root is the deployed app path ANCM set up
var dir = Path.Combine(env.ContentRootPath, "templates");
var file = Path.Combine(dir, "invoice.html");
if (!File.Exists(file))
    throw new FileNotFoundException("Template missing from publish output.", file);

Ship the folder. Publish trims content it does not know about—mark templates and sidecars so they copy to the output, then confirm they exist in the extracted site before you blame the runtime. If the path must be writable on a locked-down shared pool, point it at a dedicated data directory you control rather than assuming the site root is writable.

One opinionated rule on our floor: treat Directory.GetCurrentDirectory as diagnostic-only in ASP.NET Core. If a path matters after deploy, it comes from ContentRootPath, WebRootPath, a configuration absolute path, or AppContext.BaseDirectory—never from the process cwd IIS happened to inherit.