Vlad Eremin
10/07/2025, 7:57 PMMarvin
10/07/2025, 7:57 PMMarvin
10/07/2025, 8:10 PMpip install "prefect[azure]"
2) Azure prerequisites and permissions
- Resource group in the region you want ACI to run
- Container image in ACR (same region as the RG if possible)
- Service principal or managed identity with:
- Container Instance Contributor (or Contributor) on the resource group
- AcrPull on the ACR
- Network Contributor on the subnet if you’ll attach a VNet
- If using a service principal, set these environment variables where you’ll run the worker:
export AZURE_TENANT_ID=...
export AZURE_CLIENT_ID=...
export AZURE_CLIENT_SECRET=...
3) Create an ACI work pool
prefect work-pool create aci-pool --type azure-container-instance
Tip: Inspect the ACI job template variables you can set:
prefect work-pool get-default-base-job-template --type azure-container-instance
You’ll see fields like resource_group_name, subscription_id, image, image_registry, cpu, memory, subnet_ids, identities, dns_servers, env, task_start_timeout_seconds, etc.
4) Start a worker that polls the pool
Run this from a machine (or container app/VM) with the Azure credentials above:
prefect worker start --pool aci-pool
5) Deploy your flow to the ACI pool with per-run variables
Here’s a minimal example using the programmatic deployment API. Note the job_variables aligned to the ACI worker’s template.
from prefect import flow
@flow
def my_job():
# your minute-level work here
...
if __name__ == "__main__":
my_job.deploy(
name="every-minute",
work_pool_name="aci-pool",
interval=60, # run every minute
concurrency_limit=1, # ensure only one active run if that’s desired
job_variables={
"subscription_id": "<your-subscription-id>",
"resource_group_name": "<your-rg>",
"image": "<http://myregistry.azurecr.io/myimage:tag|myregistry.azurecr.io/myimage:tag>",
# If using ACR username/password:
"image_registry": {
"registry": "<http://myregistry.azurecr.io|myregistry.azurecr.io>",
"username": "<acr-username-or-app-id>",
"password": "<acr-password-or-sp-secret>"
},
# Or prefer identities (user-assigned MI resource IDs) if supported in your setup:
# "identities": ["/subscriptions/.../resourcegroups/.../providers/Microsoft.ManagedIdentity/userAssignedIdentities/<name>"],
"cpu": 0.5,
"memory": 1.0,
# Optional networking:
# "subnet_ids": ["/subscriptions/.../resourceGroups/.../providers/Microsoft.Network/virtualNetworks/.../subnets/..."],
# Optional environment variables for your code:
"env": {"MY_APP_SETTING": "value"},
# Operational tuning (does not reduce cold start, but helps robustness):
# "task_start_timeout_seconds": 600,
# "task_watch_poll_interval": 5,
},
)
What to expect for latency and “overtime”
- ACI cold start includes:
- Provisioning the container group: commonly ~15–60sMarvin
10/07/2025, 8:10 PMfrom prefect import flow
import time
@flow
def daemon():
while True:
try:
# do work
pass
finally:
time.sleep(60)
- This keeps the container alive and eliminates per-run cold starts. You can still use Prefect for logging, retries, and orchestration signals, but it’s one long-lived run.
- Consider Kubernetes or Azure Container Apps Jobs with pre-warmed capacity
- If you already have AKS, a Kubernetes worker can start pods quickly (especially with image pre-pull and node autoscaling tuned).
- On ACA, you can run the Prefect worker as a always-on service and point deployments to a process/docker pool.
A couple of quick questions to tailor this:
- Is ACI a hard requirement for you, or are you open to a persistent worker (VM/ACA) to keep compute warm?
- Do you need VNet/private networking for the job? That can influence startup time and config.
- What’s your image size today and where is it hosted?
If you confirm ACI is required, I can help you pin down the exact job_variables for your networking/identity setup and review your image and registry placement to minimize cold starts.