intuto.build()

Pulling the Plug on Windows: From C: to /opt

Written by Aaron Leggett | Nov 3, 2025 9:59:59 PM

The move from an old Windows VM to a managed Linux App Service environment is a daunting "black box" for any small dev team. Most high-level guides talk about IaC (we did that with Terraform/Azure) or architectural shifts, but they often skip the crucial, painful part: the code itself.

We knew we had to scale, but we delayed the migration because we didn't know the full scope. Every guide we found glossed over the sticky details, so we decided to share our experience. This is our unglamorous account of the platform dependencies we had to remove from our Dotnet application, complete with the two weeks of post-launch chaos that nearly took us out.

 

 

The Preparation Trap

As application developers, we wanted to stay focused on the product. To manage the infrastructure scope—the part we don't do daily—we engaged a partner, Parallo, to lead the Azure/DevOps work. We did a thorough discovery workshop and felt prepared.

This was a necessary step; it gave us the map for the infrastructure. But the real work was in the trenches, making surgical code changes to remove the platform dependencies that were naturally embedded when our Dotnet application was first developed on Windows.

 

The Unglamorous List: Dependencies We Had to Kill

If you're making this jump, chances are your application relies on some of this too. Here is the non-exhaustive list of the most important changes we had to make to get our app to run smoothly on a restrictive Linux environment:

 

The Path Problem (Case Sensitivity)

Say goodbye to Windows paths, backslashes, and the generous, forgiving way IIS handles them. We had to refactor all file access to use platform-agnostic paths and eliminate our reliance on IIS Virtual Directories for customer files.

The biggest headache? Linux is case-sensitive. The good news is that because we already tracked all system-used files in our database across two to three tables, the path update itself was a simple, controlled replace in most cases. Crucially, this good discipline meant we encountered very few of the dreaded mixed-case resource references that typically sink these projects.

 

Offloading Configuration to the Edge

We decided to eliminate maintenance headaches and reduce costs by shifting configuration responsibility where possible. We got rid of cumbersome URL rewrite rules in the web.config by moving them to the edge with Cloudflare Rules. Similarly, we shifted manual SSL certificate management from IIS to automated provisioning with Cloudflare. This move not only freed up massive amounts of infrastructure overhead but also helped us manage our costs, and keep our security tight. We covered our essential WAF setup here.

 

Migrating Logging to a Sidecar Model

On the Windows VM, our Datadog logging was configured directly. On the Linux App Service, we had to overhaul our entire logging approach to send logs to standard output, relying on a sidecar container to pick up the traffic and forward it to Datadog. It was an essential, non-negotiable architectural change to ensure our observability wasn't lost.

 

Modernizing Asset Bundles

We still had a few older pages that were using ASP.NET Web Optimization bundles instead of modern front-end tooling (like Angular for the rest of the app). Since we couldn't rely on the full framework context for IIS bundling, we used the migration as the forced cleanup opportunity: we moved the final pages over to Webpack for proper, platform-independent asset management.

 

Killing the Windows-Only Feature

Our most pragmatic trade-off involved external tooling. We relied on an external, Windows-based library for PDF generation. We first tried moving this logic to a Windows Function App, but we quickly found it cumbersome and slow. After realising that any attempt to migrate or replace that dependency would derail the entire project timeline, we made the final call to remove the feature entirely, opting instead for a simpler, user-driven print-screen alternative. Sometimes, the best fix is elimination.

 

The Reality Check: The Socket Swamp

Despite our partnership, our checklist, and our launch, we hit a wall. After the migration was complete, our application began periodically failing. The diagnosis? Socket exhaustion.

It wasn't a flaw in our refactoring or the contractor's setup; it was the direct consequence of a small-team capacity trade-off. Due to the demands of our launch schedule, we had to be ruthless with prioritization. While feature testing was completed, our team's available time and resources meant the intensive phase of performance and load testing was inevitably constrained. This lack of resource depth led to two weeks of chaotic "just reboot it" fixes while we frantically tracked down the resource leak. Even with a detailed map, a small, high-pressure team can still wander into the swamp.

 

Final Takeaway: Your Pre-Migration To-Do List

The work of migration isn't about one grand feature or clever hack; it's about relentlessly fixing platform dependencies.

Don't let the unknown lead to procrastination. You can significantly shrink the eventual migration scope by tackling these dependencies before the project even starts:

  • Eliminate web.config Dependencies: Move URL rewrites and SSL management to edge services (like Cloudflare) now, and ensure all configuration is handled by appsettings.json and Environment Variables.

  • Modernize Asset Management: Get rid of ASP.NET Web Optimization and move to a modern bundler like Webpack.

  • Remove Windows Libraries: Identify and remove or replace any Windows-dependent libraries (like our PDF generator) that are blocking your path to Linux.

Start today, focus on those core configuration and file system changes, and trust your team's ability to navigate the swamp. The reduced maintenance burden and improved scalability on the other side are absolutely worth the effort.

We finally pulled the plug on our Windows VM, but the fear of the unknown was the hardest part of the migration. For teams who haven't moved to Linux App Services yet, what's the one hurdle you are MOST worried about, and is it in your plan? Tweet us on X with #intutobuild and tag us @intutohq