Skip to content
TypeUnlocked
StageFleet
DifficultyAdvanced
JobMulti-tenant SaaS
ForDev teams
ForBackend devs
ServicesCron
ServicesContainers
Why HoodyContainer economics
Why HoodyHTTP-native
IndustrySaaS
TypeUnlocked
StageFleet
DifficultyAdvanced
JobMulti-tenant SaaS
ForDev teams
ForBackend devs
ServicesCron
ServicesContainers
Why HoodyContainer economics
Why HoodyHTTP-native
IndustrySaaS
TypeUnlocked
StageFleet
DifficultyAdvanced
JobMulti-tenant SaaS
ForDev teams
ForBackend devs
ServicesCron
ServicesContainers
Why HoodyContainer economics
Why HoodyHTTP-native
IndustrySaaS
TypeUnlocked
StageFleet
DifficultyAdvanced
JobMulti-tenant SaaS
ForDev teams
ForBackend devs
ServicesCron
ServicesContainers
Why HoodyContainer economics
Why HoodyHTTP-native
IndustrySaaS
BYO cron / per-tenant schedules

Let your customers BYO their own cron schedule.

Your customers type the cron expression. You POST it to their container's crontab. There's no shared queue to fair-share, no minimum interval to enforce, no support ticket about "why my job didn't run on peak Monday."

The customer types into your UI. You POST to their container.

Your settings page renders an input field. Their tenant container exposes a Cron API. Submit forwards one POST. No global scheduler, no per-tenant filtering logic, no "max 100 jobs across all customers" cap.

your-backend → tenant-container
POST /users/root/entries
// the form submit relays the customer's expression unchanged
POST https://acme-cron.hoody.com/users/root/entries
Content-Type: application/json

{
  "schedule": "0 9 * * 1-5",
  "command": "/jobs/sync_crm.sh",
  "comment": "Sync Salesforce contacts",
  "enabled": true
}
tenant-container → your-backend
201 Created
HTTP/1.1 201 Created
Content-Type: application/json

{
  "id": "sch_8a3f1c",
  "schedule": "0 9 * * 1-5",
  "next_run": "2026-05-04T09:00:00Z",
  "enabled": true
}

// the schedule is now in this tenant's crontab and nowhere else

The Hoody Cron service runs inside each tenant container with managed entries and per-user isolation. The schedule lives where the work runs.

What BYO cron gives you that a shared scheduler can't.

When the schedule lives next to the work, the rules of multi-tenant scheduling change. The constraints are local. The blast radius is local. The features are local.

no shared queue

No fair-queueing tax

There's no global thread pool to fight over. Your most demanding tenant runs every minute and never starves your quiet ones — they're not on the same crontab.

self-service

Customers self-serve, you don't validate

You stop being the gatekeeper of "is */1 * * * * allowed for your tier?" Their container, their cron, their CPU bill. Your support inbox empties out.

container-bound

The schedule ships with the tenant

Snapshot the tenant container, you snapshot the crontab. Roll back, restore, fork — the schedules go with it. No external scheduler state to sync.

Shared multi-tenant scheduler vs container-bound cron.

The difference shows up in three places: the customer's experience, your support load, and the engineering surface area.

axisshared schedulerBYO container cron
What the customer can pick
5 minute minimum, weekdays only, no concurrent runstier-gated, fair-share rules, peak-hour deferrals
Any 5-field cron expression their container can handle*/1 * * * * if they want it; their CPU pays for it
Where validation happens
Your code, before persist, against a global queue modelregex + capacity check + tier check + overlap check
The container's cron daemon parses the expressionsyntax-only; capacity is one container's, not the fleet's
Failure blast radius
One tenant's slow job stalls the queue for everyonenoisy-neighbor incidents, alerting on queue depth
One container's cron lags one tenantkernel-level isolation; the rest of the fleet doesn't notice

The shared-scheduler version of this feature is a sea of caveats. The BYO version is a five-field input box.

What goes away when the cron is per-tenant.

Three numbers that change the day you stop running a global queue. Each maps to a feature you no longer have to write or operate.

  1. validation rules to maintain0

    No more tier-gated minimum interval, no max-jobs-per-tenant, no fair-share knobs. The container is the limit.

  2. POST per schedule create

    Customer types, you forward, the container parses. The settings page submit is a single REST call, not an orchestration.

  3. fields the customer owns5

    minute · hour · day-of-month · month · day-of-week. Plus macros (@hourly, @daily). Standard POSIX, not your DSL.

Numbers reflect the BYO container-bound model — actual cron entries scale with each container's CPU and the customer's plan.

The customer's cron expression is the customer's, not yours to validate against a global queue.

shared schedulerBYO per-tenant cron
beforeif (tier !== 'enterprise' && interval < 5min) reject()validation tax: every customer paid for the worst customer
afterPOST /users/root/entries → 201 Createdthe schedule lives in their container; their CPU, their rules
See the Cron API

What this replaces.

The infrastructure pieces a BYO container-bound cron quietly retires.

  • shared multi-tenancy schedulersno global queue to fair-share
  • Quartz with tenant filteringno tenant_id column on every job row
  • custom cron-as-config UIscustomers type, the container parses
  • vendor-managed Airflowno DAG service for a 5-field expression
  • shared Sidekiq with throttlingno global rate limiter across tenants
  • BullMQ with tenant queuesno Redis to babysit per customer

Stop being the gatekeeper of someone else's schedule. Hand them the cron field, hand the work to their container.

Read the docs

Read the others