· Charlie Holland · Architecture  · 10 min read

Service Fabric: The Microsoft Orchestrator That Lost

Service Fabric got a lot right — stateful services, the actor model, rolling upgrades with health checks. But it lost the ecosystem war to Kubernetes, and being technically superior wasn't enough to save it.

Service Fabric got a lot right — stateful services, the actor model, rolling upgrades with health checks. But it lost the ecosystem war to Kubernetes, and being technically superior wasn't enough to save it.

I’ve been watching Microsoft slowly back away from Service Fabric for a while now, and I think it’s time to say out loud what a lot of us have been thinking: Service Fabric is a dead man walking.

This isn’t schadenfreude. I’ve run Service Fabric in production twice — first at a semantic technology company between 2014 and 2017, where we built their entire multi-tenant cloud product on it, and then at a London SaaS platform company in 2018 where it was the backbone of their microservices architecture. I know the technology intimately. Parts of it are genuinely brilliant. But the writing is on the wall, and it has been for at least a year.

Service Fabric Mesh, the managed version that was supposed to be the future, has been in preview since 2018 with no GA date in sight. Microsoft’s own teams are moving off Service Fabric — Azure DevOps, which was one of the flagship Service Fabric workloads, has been migrating to Kubernetes. Meanwhile, AKS gets a new feature announcement every other week. The investment delta is impossible to ignore.

So let’s do a proper retrospective. What did Service Fabric get right? What killed it? And what’s the lesson?

What Service Fabric genuinely got right

Stateful services

This was the killer feature, and nothing in the Kubernetes world has matched it yet. Service Fabric’s Reliable Collections — ReliableDictionary and ReliableQueue — gave you replicated, transactional, in-process state that survived node failures without any external database.

Think about what that means. You could write a service that held state in memory, served reads at in-process speed, and that state was automatically replicated across multiple nodes with configurable quorum. If a node died, the replica promoted, and the service continued with zero data loss. No Redis. No SQL Server. No external dependency to manage, monitor, back up, or pay for.

At the semantic technology company, we used this for tenant session state and metadata caches. Read latency was microseconds, not milliseconds. For a multi-tenant SaaS product handling semantic queries, that performance difference was the product’s competitive advantage.

var dictionary = await StateManager.GetOrAddAsync<IReliableDictionary<string, TenantMetadata>>("tenantCache");

using (var tx = StateManager.CreateTransaction())
{
    var result = await dictionary.TryGetValueAsync(tx, tenantId);
    if (result.HasValue)
    {
        return result.Value; // In-process read. Microseconds.
    }
    // Fall back to database, cache the result
    var metadata = await LoadFromDatabase(tenantId);
    await dictionary.AddOrUpdateAsync(tx, tenantId, metadata, (k, v) => metadata);
    await tx.CommitAsync();
    return metadata;
}

You simply can’t do this in Kubernetes. The closest equivalent is running a StatefulSet with some external state store, but it’s fundamentally different — you’re making network calls to a separate process, not reading from replicated in-process memory. Kubernetes is excellent at stateless workloads. For stateful patterns like this, Service Fabric was years ahead.

The actor model

Service Fabric’s Reliable Actors were a clean implementation of the virtual actor pattern (based on Microsoft Orleans, which is the research project that Service Fabric’s actor model grew out of). Actors are single-threaded, location-transparent, and automatically activated on demand.

For certain problem domains — IoT device management, game state, user session management — actors are a beautiful fit. Each device or user gets an actor. The runtime manages placement, activation, deactivation, and state persistence. You write simple, single-threaded code, and the framework handles distribution and failover.

At the SaaS platform company, we used actors to model individual customer accounts. Each account actor held configuration state, managed billing events, and processed webhooks. The concurrency model was trivially simple because actors are single-threaded — no locks, no race conditions, no distributed coordination nightmares.

Rolling upgrades with health monitoring

Service Fabric’s application upgrade model was sophisticated. You defined health policies — what percentage of nodes could be unhealthy, what error rate was acceptable — and the upgrade proceeded domain by domain, rolling back automatically if health checks failed.

This was 2015, 2016. Kubernetes didn’t have anything comparable. Kubernetes Deployment rolling updates existed, but the health checking was basic — readiness probes and liveness probes. Service Fabric’s health subsystem was far more granular. You could report custom health states from within your services, and the upgrade orchestrator would use them. If your service detected it was getting elevated error rates after an upgrade, it could report a warning or error, and the platform would roll back automatically.

I’ve seen this save us from bad deployments multiple times. A new version that passed all integration tests but had a subtle performance regression under load — Service Fabric’s health monitoring caught it in the first upgrade domain and rolled back before it affected more than a fraction of traffic.

Placement constraints

For multi-tenant architectures, Service Fabric’s placement constraints were invaluable. You could label nodes with properties and constrain services to run only on nodes with specific labels.

Kubernetes has this now with node selectors and affinities, but Service Fabric had it from the beginning, and it was deeply integrated with the resource manager. You could express things like “this tenant’s services must run on dedicated nodes” or “these services must not be co-located with those services” with simple constraint expressions. For regulated industries where data residency and isolation matter, this was a real selling point.

What killed it

The programming model: boilerplate hell

If Reliable Services and Reliable Actors were the carrot, the programming model was the stick. The amount of boilerplate required to create a Service Fabric service was staggering.

A simple stateless service required: a ServiceManifest.xml describing the service type, an ApplicationManifest.xml describing the application, a service class inheriting from StatelessService, a Program.cs to register the service type with the runtime, and a ServiceEventSource class if you wanted any structured logging (and you did).

That’s before you write a single line of business logic. Want to expose an HTTP endpoint? You need to implement ICommunicationListener, or use one of the built-in ones (Kestrel, WCF — yes, WCF, because this is Microsoft). Want to call another service? You need to use the service proxy, which requires interface definitions and registration.

The contrast with Kubernetes is brutal. In Kubernetes, you write your application however you want, package it in a container, and deploy it. Service Fabric imposed its programming model on you. Your code was coupled to the platform. Migrating away was a rewrite, not a replatform.

Local development: pain as a feature

The Service Fabric local cluster manager was a five-node cluster running on your development machine. It consumed gigabytes of RAM. It took minutes to start. Deploying an application to the local cluster took longer than deploying to a real cluster because the local cluster was usually running on a developer laptop that was also running Visual Studio, SQL Server, and seventeen browser tabs.

The feedback loop was: make a code change, build, deploy to local cluster (wait 30-60 seconds), test, repeat. Compare that to “run the container locally with docker run” — which is what Kubernetes-based development looks like. The local development experience alone drove people away.

And when the local cluster got into a bad state — which it did, regularly — the fix was to “Reset Local Cluster” from the system tray icon, which meant wiping everything and redeploying from scratch. There went your morning.

Linux support: too little, too late

Service Fabric on Linux was announced in 2016 and went GA in 2018. By that point, Kubernetes had been running on Linux since 2014, had a massive community, and was the default orchestrator for anyone not in the .NET ecosystem.

The late Linux support meant Service Fabric was effectively Windows-only for its critical years. That excluded the Go community, the Java community (mostly — there was Java support, but it was clearly second-class), the Python community, and anyone who didn’t want to pay Windows Server licensing costs for their container hosts. By the time Linux support was production-ready, the audience had already left.

Microsoft’s own teams abandoning ship

This is the one that really stings. When the organisation that built the technology starts migrating its own products off it, the message is unmistakable.

Azure DevOps (formerly VSTS/TFS Online) was one of the most prominent Service Fabric deployments. It was Microsoft’s proof point — “look, our own large-scale SaaS product runs on Service Fabric.” And then they started moving it to Kubernetes. The Azure DevOps blog has been documenting pieces of this journey, and while they’re diplomatic about it, the direction is clear.

Dynamics 365 moved. Parts of Azure itself moved. When your parent company is actively investing in a competing technology (AKS) and migrating flagship products away from yours, the ecosystem reads the signals.

The ecosystem gap

This is what actually killed Service Fabric. Not any single technical deficiency, but the lack of an ecosystem.

Kubernetes has Helm for packaging. Prometheus for monitoring. Grafana for dashboards. Istio and Linkerd for service mesh. ArgoCD and Flux for GitOps. OPA Gatekeeper for policy. Thousands of operators for deploying databases, message queues, caches, and every other piece of infrastructure. A job market where “Kubernetes experience” appears on every second cloud job spec.

Service Fabric had… Microsoft documentation. And Stack Overflow questions with two upvotes and no accepted answers. And a GitHub repository that was open-sourced in 2018, too late to build community momentum.

When I was running Service Fabric at the semantic technology company, debugging production issues meant reading the (admittedly good) official docs and, when that failed, raising a support ticket with Microsoft. There was no community to fall back on. No blog posts from other teams who’d hit the same issue. No GitHub issues with workarounds. Just you, the docs, and the Azure support engineer who might or might not have seen your specific problem before.

Cluster management complexity

Running a Service Fabric cluster — particularly a standalone one (not the Azure managed version) — required understanding node types, durability tiers, reliability tiers, upgrade domains, fault domains, and a certificate management model that could charitably be described as “comprehensive.”

Certificate rotation on a Service Fabric cluster was a multi-step process involving updating the cluster manifest, adding the new certificate as a secondary, waiting for propagation, swapping primary and secondary, removing the old certificate, and praying. Get the order wrong and you could take down the entire cluster. I’ve seen it happen.

Kubernetes isn’t simple either, but the managed services (EKS, AKS, GKE) handle most of this complexity for you. Service Fabric Managed Clusters — Microsoft’s answer to this — didn’t arrive until 2021. By then, nobody was choosing Service Fabric for new projects.

The lesson

Service Fabric’s story is a textbook case of a technology that was technically superior in specific dimensions but lost because the ecosystem chose something else.

In 2016, if you needed stateful microservices with reliable state replication and sophisticated deployment orchestration, Service Fabric was objectively the better technology. Kubernetes 1.4 was rudimentary by comparison. StatefulSets (then called PetSets) were in beta. There was no operator pattern, no custom resources, no sophisticated health-based rollout.

But Kubernetes was open. It ran on any cloud and on bare metal. It supported any programming language and any framework. It had Google’s backing and a rapidly growing community. It was, in the language of platform strategy, the technology that optimised for adoption rather than capability.

And adoption won. It always does.

I don’t regret the time I spent with Service Fabric. Reliable Collections taught me how to think about distributed state. The actor model influenced how I design certain kinds of systems to this day. The health monitoring model was ahead of its time. But if someone asked me today whether to start a new project on Service Fabric, I’d say no. Without hesitation.

Use Kubernetes. Use AKS if you’re on Azure. Accept that you’ll need external state stores for stateful patterns. Accept that you’ll rebuild some of what Service Fabric gave you for free. And accept that being technically right isn’t the same as being strategically right.

Service Fabric was technically right. It just didn’t matter.

Back to Blog

Related Posts

View All Posts »
EKS vs AKS vs GKE: I've Run Production on All Three

EKS vs AKS vs GKE: I've Run Production on All Three

Most Kubernetes comparisons are feature matrices written by people who've read the docs. This is what the three major managed Kubernetes services actually feel like when you're running production workloads, handling upgrades at 2am, and arguing with IAM policies.

Don't Get Caught in the Crossfire

Don't Get Caught in the Crossfire

The cloud wars are raging. The secret to coming out unscathed is to back open technologies, think carefully about IaaS vs PaaS, and not get locked in while the giants fight it out.

KubeFlow: Twelve Months of Pain

KubeFlow: Twelve Months of Pain

After a year of running KubeFlow across multiple client deployments, I can confidently say: the platform that promises end-to-end ML on Kubernetes delivers end-to-end frustration instead. Here's the post-mortem.