DEV Community

Humayun
Humayun

Posted on

From Azure VM to Azure Container Apps: How We Reduced Hosting Costs by 70% Without Rewriting Our FastAPI Backend

A few weeks ago, a routine Azure billing email made me take a closer look at our infrastructure.

At GoPanda (https://gopanda.in), we're still an early-stage startup. Our traffic is relatively low, and our FastAPI backend was comfortably running on an Azure Virtual Machine.

The setup worked.

The problem was that the VM cost the same whether we received 10 requests a day or 10,000.

We were paying for uptime, not usage.

That realization led me down a rabbit hole that eventually ended with a complete migration from Azure Virtual Machines to Azure Container Apps (ACA).

What started as a cost optimization exercise ended up being a great lesson in modern cloud infrastructure.

The Original Architecture

Our backend stack was fairly straightforward:

  • FastAPI
  • Docker
  • Azure Virtual Machine
  • GitHub Actions for deployments

The application itself was already containerized, so deployment wasn't particularly difficult.

However, the VM came with a few drawbacks:

  • Fixed monthly cost
  • OS maintenance and patching
  • Paying for idle compute
  • Manual capacity planning
  • Infrastructure tightly coupled to a specific machine

For a startup still validating and growing its product, these tradeoffs didn't feel ideal.

Why Azure Container Apps?

Initially, I evaluated several options:

  • Continue using Azure VM
  • Move to Azure Functions
  • Use Azure Kubernetes Service (AKS)
  • Use Azure Container Apps

Azure Container Apps ended up being the most practical choice.

The simplest way I can describe ACA is:

Kubernetes without having to manage Kubernetes.

Azure handles:

  • Cluster management
  • Networking
  • Ingress
  • Scaling
  • Control plane operations

Meanwhile, developers focus on:

  • Containers
  • Deployments
  • Application logic

You still get features such as:

  • Revision-based deployments
  • Autoscaling
  • Traffic splitting
  • Service discovery
  • Managed ingress

Without operating a Kubernetes cluster yourself.

For small teams, that's a very attractive tradeoff.

Why We Didn't Choose Azure Functions

This was the most obvious alternative.

After all, if the goal is lower costs and serverless infrastructure, Azure Functions seems like the natural choice.

The problem was that our backend is a traditional FastAPI application.

It relies on:

  • Routing
  • Middleware
  • Authentication
  • Long-lived API patterns

Moving to Functions would have meant adapting the application around a different execution model.

Our objective wasn't to become serverless at the code level.

Our objective was to become serverless at the infrastructure level.

Azure Container Apps allowed us to keep the existing architecture largely unchanged while gaining:

  • Managed infrastructure
  • Autoscaling
  • Better cost efficiency
  • Container portability

Most importantly, our deployment artifact remains a standard Docker image.

Challenges During Migration

1. Azure Student Subscription Restrictions

One challenge I wasn't expecting involved Azure Student subscription limitations.

Azure Container Registry cloud builds (az acr build) were unavailable in our subscription.

Instead, we built images locally and pushed them directly to Azure Container Registry.

Not a major issue, but definitely something to be aware of.

2. Apple Silicon Architecture Mismatch

This consumed more time than I'd like to admit.

I was building Docker images on an M-series MacBook.

By default, Docker generated ARM64 images.

Azure Container Apps expected AMD64 images.

The fix was surprisingly simple:

docker build --platform linux/amd64 .
Enter fullscreen mode Exit fullscreen mode

A one-line change solved the deployment failures.

3. Configuration Management

The VM-based deployment relied heavily on server-side .env files.

Moving to ACA encouraged a cleaner approach:

  • Environment variables
  • Managed secrets
  • Stateless deployments

This made the application easier to deploy and reproduce across environments.

4. Updating CI/CD

We already had GitHub Actions in place.

However, the workflow needed to be adapted for Azure Container Apps.

The deployment flow now looks like this:

GitHub Push
    ↓
Build Docker Image
    ↓
Push to Azure Container Registry
    ↓
Deploy New ACA Revision
Enter fullscreen mode Exit fullscreen mode

This significantly simplified production deployments.

Results

After the migration, we now have:

  • Infrastructure that scales based on actual demand
  • Revision-based deployments
  • Managed HTTPS ingress
  • No VM maintenance
  • No OS patching
  • Cleaner configuration management
  • Portable containerized deployments

And as a side effect:

Our monthly hosting costs dropped by more than 70%.

Final Thoughts

The most interesting takeaway from this migration is that cloud-native infrastructure isn't only useful when you're operating at massive scale.

For early-stage startups, it can be equally valuable before scale.

At this stage, my biggest constraint isn't server capacity.

It's time.

Every hour spent maintaining infrastructure is an hour not spent improving the product or talking to users.

Azure Container Apps helped reduce that operational burden while lowering costs at the same time.

For our current stage, it turned out to be a better fit than both a traditional VM and Azure Functions.

If you're building a containerized application and don't want the operational overhead of managing Kubernetes, ACA is definitely worth exploring.

You can check out what we're building at GoPanda:

https://gopanda.in

Top comments (0)