You do not need cron infrastructure to run scheduled jobs on DenkOps. Your app runs in an always-on slot, the process never stops, so a plain in-process scheduler, setInterval in Bun or APScheduler in Python, fires on time every time. There is no platform cron primitive because a long-lived process makes one unnecessary.
Why does an always-on slot replace cron?
Cron services exist to solve one problem: your code is not running when the schedule fires, so something external has to wake it up. Serverless platforms make this worse, every tick cold-starts a fresh function with no memory of the last run, capped at a few minutes of runtime.
A slot is a process, not a function. It stays up between requests, which means a timer you start at boot keeps ticking indefinitely. No trigger configuration, no separate service, no timeout on how long a job may run.
What does a scheduled job look like in Bun?
A timer next to your routes, in the same file you already deploy:
import { Hono } from "hono";
import denkops from "@denkopsai/sdk";
const app = new Hono();
function nightlyReport() {
const last = denkops.store.get("report:last-run");
console.log("running nightly report, last run:", last);
// ... do the work ...
denkops.store.set("report:last-run", new Date().toISOString());
}
// fire once a day from inside the long-lived process
setInterval(nightlyReport, 24 * 60 * 60 * 1000);
app.get("/health", (c) => c.json({ ok: true }));
export default { port: 3000, fetch: app.fetch };In Python the same shape works with APScheduler inside FastAPI: add a job in the lifespan hook, start the scheduler, done. The FastAPI cron guide has the full snippet, and the Express version shows node-cron if you want cron-expression syntax instead of a fixed interval.
What happens to my schedule on a redeploy?
The timer is in-memory, so a redeploy restarts it with the fresh process. That is why the example above checkpoints to denkops.store: the KV store lives on the durable /persist disk and survives redeploys. The pattern is simple:
- Write a checkpoint when a run finishes: a timestamp, a cursor, the last processed ID.
- Read it at the start of each run and continue from there.
- If a redeploy lands mid-run, the next tick picks up from the last checkpoint instead of starting over or double-processing.
For a nightly job the worst case after a redeploy is that the first tick fires up to one interval later than usual. If the exact hour matters, check the clock inside a shorter interval: run setInterval every minute, compare against the checkpoint, and do the work only when the target hour arrives and the last run is stale.
When would I still want external scheduling?
If nothing else about your app needs a server, a scheduled job alone may feel heavy. But in practice scheduled work rarely lives alone: the same process usually serves an API, a webhook, or an MCP endpoint, and the scheduler rides along for free. One deploy, one URL, one place to read logs, and state shared in memory between your routes and your jobs.