An Azure service that provides an event-driven serverless compute platform.
Hello Adam,
Greetings! Thanks for raising this question in the Q&A forum.
The log line Function group target is http is the key clue here, and it confirms this is a known scaling behavior on the Flex Consumption plan rather than an issue with your NCRONTAB expression or code. Flex Consumption uses per-function scaling, where the platform groups certain triggers together for scaling decisions. Timer triggers are scaled individually under the convention function:<NAMED_FUNCTION>, which is exactly what you see in your working two-minute deployment (Function group target is function:reconciliation). When your daily timer deployment instead shows Function group target is http, it means the instance that started up was allocated to the HTTP scale group, not to your Reconciliation function's own scale group, so the timer listener for Reconciliation never registers or starts on that instance. That is why there is no "next occurrences" log line and no invocation, and it is also why the host later shows DrainMode and stops, since with no HTTP traffic keeping that instance alive, it scales back down.
This happens because with a once-a-day schedule, there is a very long gap between invocations. On Flex Consumption, non-HTTP triggers rely on the platform's scale controller to know an instance needs to be running at the right moment, and for infrequent or sparse trigger schedules the platform can fail to allocate or keep a warm instance in the correct function-specific scale group in time for the next scheduled run. Multiple similar reports describe the identical symptom with Flex Consumption on queue, Service Bus, and timer triggers, where an instance is simply never brought up in the correct group unless it is deliberately pinned there.
Here is how to resolve it:
Confirm you are on the Flex Consumption plan. Go to your Function App in the Azure portal, then Overview, and check the App Service Plan or Hosting plan value. If it reads Flex Consumption, the behavior above applies directly.
Configure an Always Ready instance for the Reconciliation function specifically. In the portal, go to your Function App, then Scale and Concurrency, and add an Always Ready instance targeted at function:Reconciliation (not the generic HTTP group). This keeps a dedicated warm instance assigned to that function's own scale group, so the TimerListener is registered and reliably fires on schedule.
Equivalently through the CLI:
az functionapp scale config always-ready set \
--name <your-function-app-name> \
--resource-group <your-resource-group> \
--settings "function:Reconciliation=1"
- Redeploy after making the change and monitor the next scheduled run. After the always-ready instance is applied, check Application Insights traces again for the
The next 5 occurrences of the 'Reconciliation' schedulelog line at startup, this confirms the TimerListener registered correctly this time. - If cost is a concern for keeping an instance always warm just for a once-daily job, consider moving this specific workload to a Premium or Dedicated (App Service) plan with Always On enabled, since those plans do not depend on the same event-driven scale controller behavior for sparse triggers, or alternatively pair the timer function with a lightweight HTTP-triggered warmup call on a schedule slightly before the timer is due, though pinning always-ready on the function group is the more reliable fix.
If this answer helps you kindly accept the answer which will help others who have similar questions.
Best Regards,
Jerald Felix.