No AppDomains in .NET Core Why?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
AppDomain was an important isolation feature in the classic .NET Framework, so its absence in .NET Core surprised many developers. The short answer is that .NET Core simplified the runtime around process-based isolation and cross-platform design, and the old AppDomain model no longer fit those goals cleanly.
What AppDomains Used To Provide
In .NET Framework, AppDomains offered a lightweight isolation boundary inside one process. They were used for scenarios such as:
- loading plugins with some isolation
- unloading assemblies by unloading the whole AppDomain
- controlling configuration or evidence for a separate application boundary
A simplified .NET Framework style example looked like this conceptually:
That capability was useful, but it came with runtime complexity, remoting boundaries, and behaviors that were difficult to make consistent across a modern, cross-platform runtime.
Why .NET Core Dropped Them
The design of .NET Core focused on a smaller, faster, more portable runtime. AppDomains were tightly connected to older runtime features such as remoting and code-access security models that .NET Core intentionally did not carry forward.
There were several practical reasons:
- process isolation is stronger and easier to reason about than in-process domain isolation
- many AppDomain scenarios depended on legacy runtime mechanisms that were removed
- cross-platform runtime simplification mattered more than preserving every compatibility feature
- startup, deployment, and hosting models in .NET Core moved toward cleaner process and container boundaries
In other words, AppDomains solved some real problems, but they were not cheap to keep.
What Replaced the Main Use Cases
The most important replacement for dynamic assembly loading is AssemblyLoadContext.
AssemblyLoadContext is not a full AppDomain replacement. It focuses mainly on assembly loading and unloading, not on recreating the entire old boundary model.
For stronger isolation, the recommended answer is usually a separate process.
That gives you operating-system-level isolation, clearer failure boundaries, and simpler deployment patterns.
Unloading in .NET Core
A common reason developers miss AppDomains is unloading plugins. In .NET Core and later .NET, unloading is possible through collectible AssemblyLoadContext, but only when nothing still references the loaded types or assemblies.
That means plugin unloading is more explicit and often more constrained than the old AppDomain mental model. The runtime did not remove the need entirely. It replaced it with a more focused mechanism.
Security and Isolation Changed Too
AppDomains were once discussed alongside in-process security boundaries, but modern .NET does not treat them as the right answer for untrusted code isolation. If code is truly untrusted, separate processes, containers, permissions, and OS-level controls are the safer direction.
That shift is part of the reason AppDomains feel more historical than foundational in current .NET.
Common Pitfalls
A common mistake is assuming AssemblyLoadContext does everything AppDomains did. It does not. It mainly addresses assembly loading behavior.
Another issue is trying to port old AppDomain-based plugin systems line by line. Usually the better move is to rethink the boundary as either AssemblyLoadContext or an out-of-process worker.
Developers also sometimes expect collectible contexts to unload immediately. They only unload when the runtime can prove nothing still references the loaded code.
Finally, if the real need is fault isolation or trust isolation, in-process mechanisms are usually the wrong target. Use process boundaries instead.
Summary
- .NET Core dropped AppDomains to simplify the runtime and support modern cross-platform design.
- Many AppDomain scenarios depended on older runtime features that were intentionally removed.
- '
AssemblyLoadContextreplaces assembly-loading use cases, not the full AppDomain model.' - Separate processes are the preferred answer for strong isolation.
- Porting old AppDomain code usually requires redesign, not a one-to-one API substitution.
Related reading
- No current assignment for partition occurs even after poll in Kafka
- Nodejs Child Process on another server using server to server communication
- Node.js distributed shared memory solution
- Node.js maxing out at 1000 concurrent connections
- No authenticationScheme was specified, and there was no DefaultChallengeScheme found with default authentification and custom authorization
- No ConcurrentListT in .Net 4.0?
- NotEnoughReplicasException The size of the current ISR Set(2) is insufficient to satisfy the min.isr requirement of 3
- NServiceBus and Rabbit MQ or Kafka

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.