Azure Function App con runtime unreachable: la solución real
En pocas palabras: El “Runtime Unreachable” en una Function App EP1 se soluciona configurando VNet Integration hacia el storage privado y agregando un private endpoint al storage account de contenido de negocio; según Microsoft, esta función outbound no está disponible en el plan Consumption estándar.
Una Function App Linux en el plan Elastic Premium (EP1) quedó con el runtime inaccesible en el portal de Azure sin tener ni una línea de código deployada. La causa real: faltaba VNet Integration hacia el storage privado que usa la app, y un segundo storage account de contenido de negocio ni siquiera tenía private endpoint. Con esas dos piezas resueltas, el runtime volvió a responder en minutos, según el caso documentado en este walkthrough real publicado en dev.to.
Vale la pena separar bien los dos conceptos antes de seguir, porque la confusión entre ambos es la que arma la mayoría de estos incidentes: VNet Integration es una función de salida (outbound) que le da a una Function App un camino de red hacia una Virtual Network, permitiéndole llegar a recursos protegidos por private endpoints o service endpoints. No hace que la Function App sea accesible desde adentro de la VNet: solo controla cómo sale el tráfico. Según la documentación oficial de Microsoft, está disponible en los planes Flex Consumption, Elastic Premium, Dedicated y Container Apps, pero no en el plan Consumption estándar.
En este artículo:
- En 30 segundos
- ¿Por qué el portal de Azure no muestra la versión del runtime si todavía no deployaste código?
- VNet Integration vs. Private Endpoint: dos puertas distintas
- ¿Cómo conectar una Function App a la VNet paso a paso?
- ¿Por qué la Function App seguía sin conectar al storage account después de la VNet Integration?
- ¿Cómo crear un Private Endpoint para Azure Blob Storage?
- ¿Cómo verificar que la Private DNS Zone resuelve correctamente al Private Endpoint?
- Criterios para decidir qué configurar en cada caso
- Errores comunes al combinar VNet Integration y Private Endpoints en Function Apps
- Preguntas Frecuentes
- Conclusión
- Fuentes
En 30 segundos
- Una Function App EP1 no mostraba la versión de runtime y el ZIP deployment fallaba sin logs de aplicación porque el problema era de red, no de código.
- El storage principal estaba protegido con private endpoint y la nueva Function App no tenía VNet Integration configurada hacia esa VNet.
- Tras conectar la app con una subnet delegada a Microsoft.Web/serverFarms, el runtime volvió a responder después de unos minutos.
- Un segundo storage account de contenido seguía fallando con “Failed to resolve hostname”, un error de DNS, no de permisos ni autenticación.
- La solución final fue crear un private endpoint Blob con integración a la Private DNS Zone privatelink.blob.core.windows.net.
¿Por qué el portal de Azure no muestra la versión del runtime si todavía no deployaste código?
Porque el problema no está en tu aplicación sino en la infraestructura que la Function App necesita para arrancar. Si el portal no puede mostrar la versión del runtime y no deployaste nada todavía, la sospecha lógica —function_app.py mal escrito, requirements.txt incompleto, versión de Python incorrecta— queda descartada de entrada. No hay código corriendo, así que no hay código que romper.
En el caso documentado, el ZIP deployment fallaba sin devolver logs de aplicación útiles. Tiene sentido: Azure Functions depende del storage account configurado para operaciones core del runtime, y en planes Premium también usa Azure Files para contenido y configuración de la app. Microsoft documenta AzureWebJobsStorage como la conexión de storage por default y WEBSITE_CONTENTAZUREFILECONNECTIONSTRING como la variable que usa Azure Files en planes Elastic Premium y Consumption.
¿Y si ese storage está protegido con restricciones de red? Ahí aparece el problema. Una Function App nueva, sin VNet Integration configurada, no puede llegar a un storage privado, aunque exista otra Function App en el mismo entorno que funcione bien porque ya está integrada a la VNet correcta.
VNet Integration vs. Private Endpoint: dos puertas distintas
Una forma simple de no confundirlos: VNet Integration es la puerta de salida de tu Function App hacia la red; Private Endpoint es la puerta de entrada del recurso al que querés llegar. Si falta cualquiera de las dos, la conexión no se establece, aunque del otro lado esté todo bien configurado.
Conectar tu Function App a una VNet no le da automáticamente conectividad privada a cualquier recurso de Azure. El recurso destino también tiene que estar configurado del lado suyo. Regional VNet Integration, según la documentación de networking de Functions, es la modalidad recomendada y permite alcanzar recursos en la misma VNet, en VNets peereadas, servicios con service endpoints y también private endpoints, siempre que esos private endpoints ya existan.
Ejemplo hipotético (no es el caso real documentado en la fuente): imaginá que tenés una Function App EP1 que ya está integrada a una VNet y funciona sin problemas contra su storage principal. Un mes después, un desarrollador agrega una segunda dependencia: la función ahora necesita escribir en un Storage Account distinto, usado por otro equipo para archivar reportes. La VNet Integration existente no te salva acá, porque esa integración solo abre la salida de tu app; el nuevo storage account, si está detrás de restricciones de red, necesita su propio private endpoint y su propia entrada en la Private DNS Zone correspondiente. Si el error que ves es de tipo “no se pudo resolver el hostname” y no uno de permisos, ese es justamente el síntoma de esta situación.
¿Cómo conectar una Function App a la VNet paso a paso?
Se hace desde Function App → Networking → Virtual Network Integration → Add virtual network integration, eligiendo la VNet y la subnet dedicada a integración. Para una Function App Linux en Elastic Premium, esa subnet tiene que estar delegada a Microsoft.Web/serverFarms.
Microsoft documenta /28 como el tamaño mínimo de subnet para VNet Integration en Elastic Premium, aunque para Linux Premium recomienda una subnet más grande porque cada instancia en ejecución consume una IP y el escalado puede necesitar direcciones adicionales de forma temporal, según la sección de Subnets de functions-networking-options. Si tu entorno no espera picos grandes de escalado, el tamaño default alcanza.
Un detalle que rompe cabezas si no lo tenés presente: no reutilices la subnet de private endpoints como subnet de integración de la Function App. Son subnets con propósitos distintos, y los propios samples de Microsoft para Function App networking usan subnets separadas para cada una.
| Característica | VNet Integration | Private Endpoint |
|---|---|---|
| Dirección del tráfico | Saliente (outbound) | Entrante (inbound) hacia el recurso |
| Qué asigna | Camino de red hacia la VNet | IP privada dentro de la VNet |
| Planes compatibles | Flex Consumption, Elastic Premium, Dedicated, Container Apps | Flex Consumption, Elastic Premium, Dedicated |
| Delegación de subnet requerida | Microsoft.Web/serverFarms (o Microsoft.App/environments en Flex) | No aplica delegación, subnet dedicada distinta |
| Tamaño mínimo de subnet | /28 según documentación oficial | Depende de la cantidad de endpoints planeados |
| Necesita DNS privado | No | Sí, Private DNS Zone por servicio |

¿Por qué la Function App seguía sin conectar al storage account después de la VNet Integration?
Porque VNet Integration resuelve el acceso general a la VNet, pero cada recurso protegido necesita su propio private endpoint. En el caso real, después de arreglar el runtime, apareció un segundo problema: un storage account de contenido de negocio, distinto del storage principal de la app, usado para guardar documentos procesados.
Había una health-check function que probaba conectividad contra varias dependencias. El chequeo de Key Vault pasaba sin drama. El chequeo de Blob Storage fallaba con un error del tipo:
Failed to resolve ‘contentarchive.blob.core.windows.net’ — No address associated with hostname
¿Por qué ese error es tan útil para diagnosticar? Porque no dice AuthorizationPermissionMismatch ni 403 Forbidden. Dice que el hostname no se puede resolver. Eso apunta directo a DNS y a private endpoint, no a permisos ni a credenciales mal configuradas. Es una distinción que conviene memorizar: un error de autorización te manda a revisar roles e identidades; un error de resolución de nombre te manda a revisar la cadena DNS → private endpoint → VNet link.
¿Cómo crear un Private Endpoint para Azure Blob Storage?
Se crea desde el storage account, en Networking → Private Endpoint Connections, eligiendo Blob como sub-recurso destino y la subnet dedicada a private endpoints, nunca la subnet de integración de la Function App. Durante la configuración conviene habilitar integración con Private DNS Zone.
Para Azure Blob Storage, la zona DNS estándar es privatelink.blob.core.windows.net, y Microsoft recomienda esa convención de nombres para los private endpoints de Blob. El tema es que Blob, File, Queue y Table tienen zonas DNS separadas y private endpoints independientes: crear un private endpoint de Blob no te da conectividad privada automática a Azure Files o Queue.
- Blob: usa la zona privatelink.blob.core.windows.net.
- File: usa la zona privatelink.file.core.windows.net.
- Queue: usa la zona privatelink.queue.core.windows.net.
- Table: usa la zona privatelink.table.core.windows.net.
Esta distinción importa particularmente en Function Apps porque el runtime y la aplicación pueden usar servicios de storage distintos al mismo tiempo, y cada uno necesita su propio private endpoint si está detrás de restricciones de red.
¿Cómo verificar que la Private DNS Zone resuelve correctamente al Private Endpoint?
Se verifica revisando el registro A dentro de la Private DNS Zone y confirmando que apunte a la misma IP privada asignada al private endpoint. Sin esa coincidencia, el hostname público sigue intentando resolver por DNS normal en vez de usar la ruta privada.
El chequeo concreto: entrás a Private DNS Zones → privatelink.blob.core.windows.net, revisás los record sets y buscás el registro A del storage account, algo como contentarchive → 10.x.x.x. Esa IP tiene que ser idéntica a la IP privada del private endpoint que creaste antes.
También hay que revisar la sección de Virtual Network Links. La VNet usada por la Function App tiene que estar linkeada a la Private DNS Zone para que las cargas de trabajo dentro de esa VNet puedan resolver el nombre privado. Cuando el private endpoint se crea con la configuración de DNS recomendada, Azure puede manejar automáticamente el registro DNS correspondiente a través del DNS zone group del propio endpoint, lo cual ahorra un paso manual.
Criterios para decidir qué configurar en cada caso
No siempre hace falta la combinación completa de VNet Integration + Private Endpoint. Algunos criterios, basados en lo que documenta Microsoft, para decidir qué necesitás realmente:
- Si tu Function App solo necesita salir hacia recursos protegidos por service endpoints (no private endpoints), alcanza con Regional VNet Integration configurando la subnet de destino con service endpoints habilitados; no hace falta crear ningún private endpoint.
- Si el recurso destino ya está detrás de un private endpoint (storage, Service Bus, Key Vault, etc.), necesitás sí o sí VNet Integration en la Function App para que el tráfico saliente entre a la VNet, además del private endpoint ya existente del lado del recurso.
- Si tenés múltiples storage accounts con distintos niveles de exposición, evaluá cada uno por separado: cada storage account privado necesita su propio private endpoint y, si usa varios sub-recursos (Blob, File, Queue, Table), un private endpoint por cada sub-recurso que tu app realmente consuma.
- Si estás en el plan Consumption estándar y necesitás estas capacidades, la opción documentada por Microsoft es migrar a Flex Consumption, que sí soporta VNet Integration outbound y private endpoints inbound.
Errores comunes al combinar VNet Integration y Private Endpoints en Function Apps
El error más frecuente es confundir VNet Integration con Private Endpoint y asumir que una resuelve lo que hace la otra. Van tres más que aparecen seguido en entornos reales:
- Reutilizar la subnet de integración como subnet de private endpoints. Son propósitos distintos: una es para salida de la Function App, la otra aloja IPs privadas de recursos. Mezclarlas rompe el diseño de red y complica el troubleshooting.
- Asumir que un private endpoint de Blob cubre File, Queue o Table. No es así. Cada sub-recurso de storage tiene su propia zona DNS y necesita su propio private endpoint si tu app lo usa detrás de restricciones de red.
- No linkear la Private DNS Zone a la VNet correcta. Podés tener el private endpoint perfecto y el registro DNS bien armado, pero si la VNet de la Function App no está vinculada a esa zona, la resolución sigue fallando.
- Ignorar el plan de hosting. El plan Consumption estándar no soporta VNet Integration outbound ni private endpoints inbound, según la tabla de compatibilidad de functions-networking-options.
Como alternativa parcial a VNet Integration para casos puntuales de salida controlada, Microsoft también documenta Hybrid Connections, aunque esa opción solo corre en Windows y no está disponible en Flex Consumption ni en Consumption plan.
Preguntas Frecuentes
¿Qué significa el error “runtime unreachable” en una Azure Function App?
Significa que el portal de Azure no puede comunicarse con el host de Functions para leer datos básicos como la versión del runtime. Si pasa sin haber deployado código todavía, el origen casi siempre está en networking o storage, no en la aplicación.
¿VNet Integration tiene costo adicional en Azure Functions?
La documentación de Microsoft no lista un cargo aparte por habilitar VNet Integration, pero solo está disponible en planes Flex Consumption, Elastic Premium, Dedicated y Container Apps, no en el plan Consumption estándar. El costo real depende del plan de hosting elegido, no de la función de red en sí.
¿Puedo usar VNet Integration en el plan Consumption de Azure Functions?
No, según la tabla de compatibilidad oficial el plan Consumption estándar no soporta VNet Integration outbound ni private endpoints inbound. Si necesitás esas capacidades con facturación por consumo, la opción es Flex Consumption, que sí las incluye.
¿Qué subnet necesito para conectar una Function App Elastic Premium a una VNet?
Necesitás una subnet delegada a Microsoft.Web/serverFarms, con un tamaño mínimo de /28 según Microsoft. Para cargas Linux Premium con escalado agresivo conviene una subnet más grande, porque cada instancia consume una dirección IP.
¿Por qué falla la resolución DNS de un storage account con private endpoint?
Falla cuando la Private DNS Zone no tiene el registro A correcto apuntando a la IP del private endpoint, o cuando esa zona no está linkeada a la VNet donde corre la Function App. El síntoma típico es un error de “no se pudo resolver el hostname”, distinto a un error de permisos.
Conclusión
El caso real repasado acá deja una lección simple: si tu Function App no arranca y todavía no deployaste código, dejá de mirar function_app.py y empezá a mirar networking y storage. VNet Integration te da la salida hacia la VNet, Private Endpoint le da entrada privada al recurso destino, y ninguna de las dos reemplaza a la otra.
Si trabajás con Function Apps en entornos con storage restringido, conviene documentar de antemano qué subnet usa cada integración y qué private endpoints existen por servicio: el error de DNS del segundo storage account en este caso pudo evitarse revisando esa checklist antes de deployar, en lugar de descubrirlo con un health-check en producción.






