Deploying Docker Containers to Azure Without Orchestration Overhead
Critically, not all workloads belong on an orchestrator. For many small services, event-driven processes, and dev/prototype environments, Kubernetes introduces needless complexity. Azure Container Instances (ACI) provides a low-friction alternative: single- or multi-container deployments via a true serverless experience, billed down to the second.
Why Use ACI Instead of Kubernetes or Virtual Machines?
Kubernetes: designed for robust, scalable, distributed systems. But for stateless APIs, ad-hoc data processors, or ephemeral tools? Overkill.
Virtual Machines: fully customizable, but you manage OS lifecycle, patching, and VM sprawl. Not great for short-lived or variable workloads.
ACI bridges the gap: Immediate container lift, minimal ops burden.
Core features:
- On-demand container execution. No VM or orchestrator prep.
- Granular billing—CPU, memory, and seconds.
- Simple exposure: built-in DNS, easily accessed via public FQDN.
- Integration: Azure CLI, ARM/Bicep, or Portal. Azure DevOps and GitHub Actions support native ACI steps.
Direct Example—Minimal HTTP Container Deployment
There’s no need for theory if the process takes 2 minutes. Assume the following:
- Azure CLI
2.53.1
or newer (az --version
) - Docker image:
mcr.microsoft.com/azuredocs/aci-helloworld
(public, <30MB pull, HTTP GET returns a greeting) - Azure account (resource group location:
eastus
)
A deployment sequence:
az login
az group create --name aci-rg --location eastus
az container create \
--resource-group aci-rg \
--name helloworld-acidemo \
--image mcr.microsoft.com/azuredocs/aci-helloworld \
--dns-name-label helloworld-acidemo-$RANDOM \
--ports 80
Result:
ACI schedules container creation and assigns a DNS name:
helloworld-acidemo-<suffix>.eastus.azurecontainer.io
Query status (ensure ProvisioningState == Succeeded):
az container show \
--resource-group aci-rg \
--name helloworld-acidemo \
--query '{FQDN:ipAddress.fqdn,State:provisioningState}' \
--output table
Access via standard browser or curl
. Example output should resemble:
Welcome to Azure Container Instances!
Side note: DNS labels under each Azure region must be globally unique. A collision yields Code: Conflict
or similar.
Deploying Custom Images
Prerequisite: Image must be accessible—either via Docker Hub or a private Azure Container Registry (ACR).
Example using Docker Hub:
docker build -t mydockerhubuser/mywebapp:1.3.0 .
docker push mydockerhubuser/mywebapp:1.3.0
az container create \
--resource-group aci-rg \
--name mywebapp-prod \
--image mydockerhubuser/mywebapp:1.3.0 \
--dns-name-label mywebapp-prod-$RANDOM \
--ports 8080 \
--environment-variables ENV=production LOG_LEVEL=warn
Note: For ACR-hosted images, set --registry-login-server
, --registry-username
, --registry-password
.
Practical Tips and Common Pitfalls
Persistent storage:
By default, containers are stateless. For write persistence:
az container create \
... \
--azure-file-volume-share-name myshare \
--azure-file-volume-account-name <storage_account> \
--azure-file-volume-account-key <key> \
--azure-file-volume-mount-path /mnt/data
Logs and Debug:
Retrieve real-time output:
az container logs --resource-group aci-rg --name mywebapp-prod
For interactive troubleshooting:
az container exec --resource-group aci-rg --name mywebapp-prod --exec-command "/bin/sh"
Resource Limits:
CPU and memory are capped per container group (as of June 2024: up to 16GB RAM, 4vCPU). For anything beyond, consider AKS or batch solutions.
Networking:
Single group isolation. VNet integration is possible, but adds complexity; public IP + port exposure suffices for dev/test.
Scaling:
No horizontal scaling within ACI itself. For scale-out and load balancing, incorporate ACI into Azure Container Apps or behind Application Gateway—but then you’re approaching orchestrator territory.
Known Issues and Trade-Offs
- ACI container startup sometimes spikes 5–15 seconds, especially for large images or first pull after region cold start.
- No internal service discovery. One container group = one instance endpoint.
- Quota errors can surface (
Code: ResourceQuotaExceeded
) if container limits per region (default 100 groups/region) are reached. - Inbound traffic only on explicitly opened ports. Unexposed ports are blocked (good for isolation, but can confuse if a web service runs on 8080 and you forget
--ports 8080
).
Conclusion
For projects requiring minimal bootstrapping, per-second billing, and cloud-based container hosting—without orchestrator or VM headaches—ACI often outpaces AKS or VMSS (Virtual Machine Scale Sets). Its simplicity comes with limits, but for event-driven jobs, short-lived workers, or nimble public endpoints, the trade-off is advantageous.
Not Perfect: Some custom networking and scaling scenarios will require AKS, Container Apps, or external automation.
Non-obvious tip: You can use ACI as a build runner for ephemeral CI jobs—GitHub Actions and Azure DevOps both allow task-level ACI containers, reducing both cost and queue time for self-hosted runners.
Skip the bloat where you can. For fast deployments and controlled costs, ACI earns its place in the Azure container toolbox.