Who else has been heavily relying on randomized scheduling via expressions here?
Have you found any feasable solution? Why did n8n remove this suuuuper handy feature to use expressions in the scheduled-trigger node?
Please share your workflow
Information on your n8n setup
n8n version: 2.31.6
Database (default: SQLite): sqlite
Running n8n via (Docker, npm, n8n cloud, desktop app): docker
The issue stems from a change in how n8n handles the Schedule Trigger; it now requires static values for the internal cron-scheduler rather than evaluating expressions dynamically. Because expressions are only processed at the moment of workflow activation, they cannot create a truly ârandomâ recurring trigger. This shift effectively removes the ability to use Math.random() directly within the trigger node to vary execution times.
For high-volume or long-term scheduling, an external database pattern is more robust. By storing target execution dates in a table (like PostgreSQL or Airtable) and using a frequent âcheckerâ workflow to trigger pending tasks, you avoid bloating the n8n database with thousands of suspended executions. This approach is significantly more scalable than relying on internal wait states.
Since you are currently running n8n on SQLite via Docker, be cautious with long-term wait nodes. Large numbers of suspended executions can degrade SQLite performance and may be deleted if your execution TTL settings are too aggressive. For production-grade randomized scheduling over several months, migrating to PostgreSQL is highly recommended to ensure stability.
@kjooleng Thanks for your (AI-generated) response. I already thought about this. But actually I often need to have flows run at random execution dateTimes that are in a range of multiple weeks or months. You canât let those executions run for weeks or months, waiting for the wait node to complete, since your approach just bloat up the running executions list the server has to handle, causing a lot of resources to get used unnecessarily or maybe even crash the server.
@nm5182 my understanding is that we never officially supported this due to how the triggers are scheduled to run. Did it actually work or did it just appear to work?
Hey @nm5182, good morning!
I hope youâre doing well.
the Schedule Trigger was designed to register a fixed schedule (by interval, time, or cron expression) and trigger an active workflow according to that rule. The official documentation describes it exactly like this: execution at defined intervals and times, similar to cron. Schedule Trigger | Nodes | n8n Docs
I noticed you put Math.Random inside the hour and minute fields, which requires the scheduler itself to re-evaluate a dynamic expression and change the next execution over time. This was never a supported/guaranteed behavior by the Schedule Trigger. (not according to whatâs documented).
Hi @nm5182
Keep the trigger rule fixed and hold the randomness in a next-run date the workflow rolls for itself. Schedule Trigger once a day, then a Code node that decides whether today is the day and picks the next date before it exits:
const store = $getWorkflowStaticData('global');
const now = Date.now();
if (store.nextRun && now < store.nextRun) {
return [];
}
const days = 1 + Math.floor(Math.random() * 60);
const next = new Date(now + days * 86400000);
next.setHours(9 + Math.floor(Math.random() * 4), Math.floor(Math.random() * 60), 0, 0);
store.nextRun = next.getTime();
return [{ json: { nextRun: next.toISOString() } }];
Returning an empty array ends the branch on the days it isnât due, so the rest of the workflow runs only on the random date and nothing is held open in between. Change the 60 to widen or narrow the window in days. Static data saves only on a published workflow, so test it published rather than with Execute workflow.
Hello @nm5182,
I remember this used to work in the past, I even suggested one myself here:
Actually, there is a very simple workaround, since the new behaviour is that n8n evaluates the expression only once at publish time, and locks in whatever value it produced:
So you just need to unpublish the workflow then publish it again, so every republish re-evaluates the expression and locks in a fresh, genuinely random value..
And you donât even need to do that manually of course, using n8n API by just adding the n8n node at the end of the workflow to Unpublish/Publish the workflow, so every run locks in a new random value for the next one,
Thereâs also a workaround inside the cron expression itself (irregular, but not truly random): define multiple seconds (intervals) manually, e.g. 4,8,15,16,23,42 * * * * *, so it fires at those specific seconds every minute, this approach doesnât need the unpublish/publish trick at all.
A note for anyone taking the static-data approach @Anshul_Namdev suggested above â this is where it bit us in production.
Inside a Code node, new Date() does not honor the workflow/instance timezone; it gives you UTC. The Schedule Trigger does honor the timezone setting. So a daily trigger firing at your local time, compared against a next-run date computed with new Date(), is off by your UTC offset â and it fails quietly, because nothing errors. Use $now, which is timezone-aware:
One more silent failure worth guarding against if you end up building a weeks-based schedule instead: if weeksInterval is missing from the node parameters, the node validates, the workflow saves, and it simply never fires. There is no error anywhere and you cannot see it from the canvas.
Neither of these answers the âwhy was it removedâ question â they are just the two potholes on the road the workaround takes.
Hi @Jon,
Yes, it actually worked for years. Iâve used it in dozens of flows. As a n8n user I might not know the exact technical details of how n8n works under the hood. I only see that it worked for such a long time. It was amazingly easy and intuitive to set up random schedules via expressions in the schedule-trigger and suddenly it stopped working I (and while I canât speak for others I strongly assume there are some) would love to see it back working as intuitive as it was, because thatâs at least what I love n8n for.
@mohamed3nan Thanks a lot for sharing your temporary workaround, that is simple and reliable. The only problem with workarounds is that they always come with trade-offs. In the case of your definately elegant approach itâs the fact that itâs âmisusingâ the publish functionality to get the desired random-execution-schedule-effect. But if I had e.g. another flow that pushes every flow to a git repo every time it was published, I would create a lot of commits in that repo for the flows that use your workaround.
I guess the point thatâs Iâm trying to make is: Really thanks a lot for sharing it. And at the same time I would love to see n8n actually bringing back the simple way of using expressions in a schedule-trigger.