Home · About · Services · Industries · Projects · Products · Process · Blog · FAQ · Contact
Azure App Service or containers on a VM? How to choose in 2026 When App Service's managed platform is the better home for your app, when Docker on an Azure VM wins, the limits that decide it, and where Container Apps fits.
A team has a FastAPI backend, an Angular frontend and a PostgreSQL database, and the business has decided it will live on Azure. One developer wants App Service because it is managed. Another wants a Linux VM running Docker Compose because that is how it runs today and nobody has to learn anything new. Both are reasonable, and the wrong choice tends to show up months later as either a surprise bill or a weekend spent patching.
The short answer: choose Azure App Service when your app is a standard web app or API that serves HTTP, keeps state in a database or storage, and you want slots, scaling and patching handled for you. Choose containers on a VM when you need OS control, long-running or non-HTTP processes, or several tightly coupled services on one host.
We have shipped both. The RAG assistants we built on Azure run on App Service alongside Azure OpenAI and Azure AI Search. Other workloads we look after run as Docker containers on Linux servers, because they need things App Service does not do well. This guide is how we decide.
What you are actually comparing App Service is a managed platform for web apps. You buy an App Service plan, which is a set of instances at a chosen tier, and run one or more apps on it. You can deploy code directly on a built-in runtime (Python, Node, .NET, Java, PHP) or deploy your own container image. Microsoft patches the host OS and runtime, terminates TLS, and gives you deployment slots, autoscale, built-in authentication and managed certificates, depending on the tier.
Containers on a VM means an Azure virtual machine, usually Ubuntu, with Docker and Compose installed and a reverse proxy such as NGINX or Traefik in front. You own everything from the kernel up: patching, TLS, log rotation, backups of the disk, firewall rules and the deploy process. In return, nothing is hidden from you.
Note that this is not really a question of containers against no containers. App Service runs containers well. The real question is managed platform against a server you run yourself. There is also a middle option, Azure Container Apps, which we cover alongside the decision table.
Where App Service wins Deployment slots. On Standard tier and above you deploy to a staging slot, warm it up, test it, then swap it into production. Rollback is another swap. This alone removes most deploy-day downtime for small teams. No OS to look after. Host patching, runtime updates and the load balancer in front are Microsoft's job. There is no unattended-upgrades config to forget about. Scaling without re-architecture. Scale up by changing tier, or scale out to more instances with autoscale rules, as long as the app is stateless. Built-in pieces you would otherwise assemble: free managed certificates for custom domains, health check pings that take bad instances out of rotation, App Service authentication with Microsoft Entra ID, and Application Insights integration. Managed identity and Key Vault references, so connection strings and keys do not need to sit in files on a disk. A slot-based release from a pipeline is a few lines of Azure CLI:
az webapp deploy --resource-group rg-erp --name erp-api --slot staging \
--src-path dist/app.zip --type zip
# run smoke tests against https://erp-api-staging.azurewebsites.net
az webapp deployment slot swap --resource-group rg-erp --name erp-api \
--slot staging --target-slot productionFor a custom container, point the app at your registry image and tell App Service which port the container listens on:
az webapp config container set --resource-group rg-erp --name erp-api \
--container-image-name myregistry.azurecr.io/erp-api:1.8.2
az webapp config appsettings set --resource-group rg-erp --name erp-api \
--settings WEBSITES_PORT=8000Where App Service gets in the way Most frustration with App Service comes from treating it like a server. It is not one, and these limits are worth knowing before you commit:
Requests have a hard timeout of about 230 seconds at the front end. Long report generation, large exports or slow AI calls need to move to a background job and a polling or webhook pattern. The file system is not a place for data. On Linux, only /home survives restarts, and for custom containers even that is off unless WEBSITES_ENABLE_APP_SERVICE_STORAGE is true. It is shared storage, slower than a local disk. Uploads belong in Blob Storage. It is built for HTTP. WebJobs can run queue workers and scheduled tasks, but they share the plan's CPU and memory with the web app. Anything listening on raw TCP or UDP needs a different service. Multi-container apps are constrained. The old Docker Compose preview on App Service is being retired in favour of sidecar containers, so a five-service Compose file does not lift and shift. No root access to the host. You cannot install a kernel module, tune sysctl, or run an agent that needs the host. You pay for the plan, not the app. An oversized Premium plan running one quiet app costs the same as a busy one. Where containers on a VM win A VM with Docker is the right tool more often than cloud marketing suggests. It fits when:
The application is already a Compose stack, with workers, a scheduler, Redis and a reverse proxy that all need to sit together. You run processes that are not request-response: queue consumers, long ETL jobs, media processing, or a self-hosted tool such as n8n or MinIO. You need OS-level control, specific system packages, custom NGINX configuration, or software licensed per machine. You want the same deployment on Azure, another cloud and an on-premises server, without rewriting configuration for each. The load is steady and predictable, so a reserved VM or savings plan gives a lower monthly cost than an equivalent App Service plan. The cost of that freedom is operations work. A VM that hosts production needs, at a minimum, the following, whether you do it or someone does it for you:
Automatic security updates, with a reboot window for kernel patches. SSH locked down: key-only, ideally reached through Azure Bastion or a private network rather than a public port 22, plus a network security group allowing only 80 and 443 from the internet. TLS certificates issued and renewed automatically by the reverse proxy. Container restart policies and health checks, and Docker log rotation so disks do not fill. Azure Backup or disk snapshots for the VM, and separate, tested database backups. Monitoring with alerts on disk, memory, certificate expiry and the health endpoint. Docker's default json-file logs grow until the disk is full. One setting in /etc/docker/daemon.json prevents a common outage. Restart Docker afterwards; it applies to containers created after the change:
{
"log-driver": "json-file",
"log-opts": { "max-size": "20m", "max-file": "5" }
}A decision table we use When a client brings us a workload, we go through these questions in order. The first one that clearly applies usually decides it.
Does it only serve HTTP, finish requests in well under four minutes, and keep state outside the app? Lean App Service. Are there background workers, queues or schedulers that must run constantly next to the app? Lean VM, or Container Apps. Is zero-downtime deployment important and the team small? App Service slots are the cheapest way to get it. Does it need root, custom kernel settings or per-machine licensing? VM. Does it need to run identically on Azure and somewhere else? Containers on a VM, or images that can target both. Is nobody available to patch and monitor a server? App Service, unless you have a support arrangement that covers the VM. Mixed answers are common, and mixing platforms is fine. A typical result is the API and frontend on App Service, with a small VM or a Container Apps job running the worker that handles slow tasks. Both use the same image and the same managed PostgreSQL flexible server.
Azure Container Apps sits between the two options. It runs your container images with managed scaling, including scale to zero, revisions for traffic splitting, and support for background jobs and event-driven scaling. It suits teams that already think in containers and have workers as well as web endpoints, and do not want to manage a VM. It brings its own concepts to learn, such as environments, revisions and ingress settings, so for a single straightforward web app App Service is still usually simpler.
Costs: what actually drives the bill We do not quote list prices here because they vary by region and change. The drivers matter more.
App Service cost is the plan tier multiplied by the instance count. Several small apps can share one plan, which is often the biggest saving available. Slots run on the same plan instances, so a staging slot adds load but not a separate bill. VM cost is the VM size plus managed disks, public IP and backup storage. Shutting down from inside the OS still bills compute; only a deallocated VM stops that, and its disks and IP keep charging until deleted. Both benefit from reservations or savings plans for steady workloads, and both need budgets with alerts in Cost Management. The cost people forget on a VM is staff time: patching, upgrades and the occasional evening spent on a full disk. Before you decide List every process the application runs, not just the web server. Workers and cron jobs change the answer. Check the longest request your app serves against the App Service timeout. Find everything the app writes to local disk and plan where it should go instead. Decide who patches, monitors and restores, by name, before choosing a VM. Build the environment with Bicep or Terraform, so switching later is a change in code rather than a rebuild by hand. Keep the application in a container image either way. It is the cheapest insurance against choosing wrong. If the list turns up a mix of needs or nobody to own the operations work, that is the point to get a second opinion before the first invoice arrives, not after.
Infigini IT Services · 11 Kanwali Road, Dehradun 248001, India · info@infigini.com · +91 95684 87789