Temporal schedules to replace cron jobs

Teams running recurring jobs in cron or Temporal Cron Jobs can move them to Temporal Schedules to get visible, pausable and controllable runs. This page explains the settings, the migration sequence and the costs and risks to plan.

On this page

When cron jobs stop being enough for Temporal schedules

Temporal Schedules start Temporal Workflows on a timetable that is stored and run by the Temporal service itself, so a team should consider moving cron work onto them when recurring jobs need a shared record of what ran, controlled handling of overlapping or missed runs, and one place to pause or change them. The usual starting point is familiar: crontab entries spread across many hosts, no central list of what exists, and runs that silently disappear when a host or network path is down during the scheduled time. Schedules do not fix a bad job, but they make the timing, history and failure behavior visible and governed.

This work only makes sense for teams that already run, or have firmly planned to run, self-hosted Temporal. A schedule executes on the cluster, so the cluster must exist and be designed before any cron job moves. That design covers the persistence backend, where Cassandra, PostgreSQL and MySQL are tested and SQLite is meant only for development and testing, and the advanced visibility store, where Elasticsearch or OpenSearch is recommended once executions reach more than a few. It also covers access control, because a server with no Authorizer configured accepts every API request, and network placement, because the Temporal deployment documentation expects services on hosts that are not reachable from the public internet.

Not every cron job belongs on a schedule. Jobs that call external systems should be idempotent or guarded by a business key, since a retried or backfilled run repeats side effects. Jobs where two runs must never overlap need an overlap policy chosen deliberately, and jobs that a team must pause during incidents need a named owner who can do it. Host-local housekeeping, such as clearing a temporary directory, may reasonably stay in cron. Before migrating, list each job, its owner, its tolerance for a missed or delayed run, and the alert that should fire when it fails, then confirm that the schedule's interval or calendar definition matches the business meaning of the job.

Plan for the operating load early. Every schedule adds workflow executions, history and visibility records, so the number of schedules and their frequency drive the size of the persistence and visibility stores. Confirm sizing with your database and search platform teams rather than assuming a default. Also plan server upgrades: Temporal documents that backward compatibility is guaranteed only between successive minor versions, so the cluster should move one minor version at a time, and Helm chart versions below 0.73.1 do not work with server images 1.30 and later. Check the current release notes before you commit a migration date.

Schedule settings and when each one fits

This table compares the main settings on a Temporal schedule and the situations each one suits. Read each row as a decision: what the job needs, and what you must accept in return. The main takeaway is that overlap, catch-up and time zone choices shape how a job behaves after a failure, so decide them before you migrate a cron job.

SettingTypical use caseTrade-off to plan for
Interval specFixed cadence jobs, such as polling a queue, where wall-clock alignment is unimportant.Run times are measured from a set interval, so they will not stay aligned to clock times. Use calendar specs for business-hour work.
Calendar spec and time zoneJobs tied to clock times, such as a nightly reconciliation or a month-end close.Confirm in the Temporal schedule docs how daylight saving transitions and your chosen time zone are handled before go-live.
Overlap policyDeciding what happens when the previous run is still active: skip, buffer, cancel the older run or allow concurrent runs.Skipping can hide a growing backlog, buffering can queue work indefinitely, and concurrent runs require idempotent activities.
Pause and unpauseHolding a schedule during maintenance, an incident or a data freeze without deleting its definition.Confirm what happens to runs missed during a pause, and document who is authorized to pause and resume each schedule.
Catch-up and backfillRecovering runs missed during an outage of workers or the cluster.A large backfill can flood downstream systems at once, so confirm the catch-up window and throttle the affected activities.
Workflow ID per runGiving each scheduled execution a predictable ID so operators can find, correlate and audit runs.IDs must stay unique across runs, so agree a naming pattern that keeps overlapping or retried runs easy to trace in visibility.
Task queueRouting scheduled runs to the worker pool that holds the credentials and capacity for that job.A shared queue lets one heavy job delay others, while dedicated queues add worker deployments to size, secure and monitor.

Setting up the first schedule on a self-hosted cluster

This sequence takes a self-hosted Temporal cluster from a confirmed server, storage and visibility setup to one tested schedule that replaces a cron job. Each step ends with a check your team can record, so the schedule is proven in a non-production namespace before any production job moves. The order matters because each later check depends on the earlier ones.

  1. Confirm cluster version and backends

    Start with the platform. Confirm the Temporal Server version is the latest patch of the minor line your team has chosen, and that any earlier upgrade moved one minor version at a time, with schema upgrades applied first where the release notes call for them. Check that persistence runs on a supported database and that advanced visibility runs on Elasticsearch or OpenSearch, which the visibility documentation recommends beyond a small number of workflow executions. If you deploy with Helm, confirm the chart version supports your server image. Done when the cluster reports healthy from the CLI and the Web UI loads.

  2. Create or confirm the namespace

    The platform owner creates a namespace for this schedule or confirms one already exists for the team. Set the retention period to match how long your team needs to inspect run history, and confirm that no other workload shares the namespace. Before any production data flows, confirm an Authorizer and a ClaimMapper are configured, because a self-hosted cluster without an Authorizer allows every API request with no access control. Done when describing the namespace shows the expected retention and access is limited to the owning team.

  3. Deploy the workflow and worker

    The engineer who owns the job deploys the workflow code and a worker that registers on the task queue the schedule will target. Confirm the worker points at the correct namespace, frontend address and TLS settings for internode and frontend traffic, and that it uses the same task queue name as the schedule's action. Keep this first workflow simple and idempotent so later tests can run it safely several times. Done when the worker appears as polling the task queue and a manually started run completes with the expected result.

  4. Create one schedule with a test spec

    Create a single schedule in the staging or non-production namespace, using an interval spec or a calendar spec that matches the cron expression you are replacing. Set the overlap policy explicitly and record why you chose it. Point the action at the workflow and task queue from the previous step. Done when the schedule is listed and its upcoming run times match the spec, checked against a calendar you worked out by hand.

  5. Verify run creation and history

    Wait for the first scheduled time, then check that a workflow run was created under the schedule's identifier. Open the run in the Web UI or list runs with the CLI, and confirm the history shows the start, activity or task completions and the close event you expect. Allow for visibility indexing to catch up before concluding a run is missing. If no run appears, check worker logs and the schedule's recent actions. Done when the run is visible in the workflow list with a complete history and the schedule records the action as taken.

  6. Test pause, unpause and overlap

    Pause the schedule, wait through at least one scheduled time, and confirm no new run starts. Unpause it and confirm runs resume. Then test overlap deliberately by making a run last longer than the interval, for example by holding the worker or adding a sleep in a test build (illustrative). Confirm the run count and outcome match the overlap policy you chose earlier, and record what you observed. Done when no runs started during the paused window, runs resumed after unpause, and the overlap result matches the policy.

  7. Rehearse missed runs in staging

    Simulate an outage by stopping the worker or pausing processing across several scheduled times in the staging namespace. Restart the worker and observe how the schedule handles the missed times under the catch-up settings you configured. Compare the runs that were created with what the job should do, including whether the job produces correct results when a backlog runs. Done when the team has written down how missed runs were handled and has agreed whether the production schedule needs different settings.

Migrating existing cron entries without double runs

The cutover is where duplicate runs happen, so the plan has to guarantee that exactly one trigger is active for each job at any moment. Start with an inventory of every crontab entry on every host that runs recurring work, plus every Temporal Cron Job already defined in your namespaces. For each item, record the command or workflow, the schedule expression, the time zone the host uses, the account it runs as, the systems it writes to or calls, the owner, and the alert that fires on failure. Then map each job to a Temporal schedule specification and choose an overlap policy. A job that can run longer than its interval needs an explicit decision about what happens to the next start, and that decision should be written into the migration record.

Run the new schedule alongside the old trigger for a defined number of cycles before cutover, but only if the workflow can run during that period without causing side effects. Many teams add a mode flag so the workflow records its intended actions and skips external writes. Compare the Temporal execution history with the cron logs for each cycle, checking that start times, inputs and outcomes match. Agree the acceptance criteria with the job owners before the parallel run starts, including how many consecutive matching cycles count as verified. Only after verification should you comment out the crontab line or pause the old Temporal Cron Job. Keep the original definition in version control so the rollback path is a single change, and record who approved the switch and when.

Time zones are the most common source of subtle drift. Cron evaluates expressions in the host's local time, while a Temporal schedule specification carries its own time zone setting, so an entry that looked correct in a crontab can fire at a different instant after migration if the zones differ. Confirm the time zone each job should follow, then write it explicitly in every spec. Also test the behavior around daylight saving transitions in both directions. Ask the platform team to document what the schedule does with a local time that is skipped in spring and with one that occurs twice in autumn, then compare that with what your existing crontab does on the same hosts. Jobs tied to business cutoffs may need to run in UTC instead, which is a policy question for the business owner to settle before cutover.

Idempotency limits the damage when two triggers overlap, a worker restarts mid-run, or an operator re-enables a paused schedule. Derive a deterministic key for each run from the job name and its scheduled time, so every retry sees the same identity. Before any side effect, the workflow checks whether that key has already been processed in the system of record, and it passes the same key to external APIs that accept idempotency keys. Illustrative example: a nightly export keyed by business date writes a file named for that date, so a second run replaces the same file with identical content and does not create a second posting. Treat the key design as a review item for each job, because a key built from execution time will not protect against duplicates.

Before you retire any old job, test the failure and restart paths in a non-production namespace using the same worker build you plan to deploy. Stop a worker in the middle of a run and confirm the run resumes without repeating completed side effects. Force an activity to fail until its retry policy is exhausted, then check that the alert fires and that the next scheduled run behaves as the overlap policy specifies. Pause and unpause the schedule, and simulate an outage long enough to create missed runs, then confirm how catch-up runs interact with your idempotency keys. Keep a short written record of each test, its expected result and its actual result. Retire the cron entry only when every path has passed and the job owner has signed off on the results.

Securing schedule access in a self-hosted cluster

Schedules control when production work starts, so access has to be enforced at the Temporal frontend, the network edge and the data layer together. The cards below cover the authorization gap that the default configuration leaves open, the roles that should hold schedule changes, and the transport and payload protections that keep schedule inputs confidential.

Frontend access through an Authorizer

The frontend service receives every schedule creation, update, pause and deletion, so it is the main access control point. An Authorizer decides whether each API call is allowed, and a ClaimMapper turns identity claims from JWTs or client certificates into namespace roles that the Authorizer can evaluate. Confirm that each namespace holding schedules maps identity provider groups to roles, and test that a call without the right role is denied before go-live.

Risk of the default noopAuthorizer

If no Authorizer is configured, self-hosted Temporal uses the default noopAuthorizer, which allows every API request with no authentication or access control. On a cluster replacing cron jobs, any client that can reach the frontend could create a schedule that starts workflows, change its spec, or delete a schedule that finance or operations depends on. Check the server configuration for an explicit Authorizer setting before any schedule reaches production, and treat its absence as a failed review item.

Who may create, pause or delete

Schedules change what runs and when, so write permissions should be narrower than read permissions. Assign creation and deletion to the platform team or the accountable service owner for each job, and grant pause and resume to on-call operators who need to stop a faulty job during an incident. Developers and auditors can hold read access. Record the approved role for each schedule in your change process and review membership on the same cadence as other privileged access.

Isolate services from the public internet

The Temporal deployment documentation says services should run on hosts not reachable from the public internet, with network access limited to trusted internal networks. This matters for schedules because the frontend receives every schedule API call, and the persistence and visibility stores hold schedule history and inputs. Confirm that load balancers, ingress rules and security groups expose the frontend only to internal networks used by workers and approved clients, and that database ports accept traffic only from Temporal services.

mTLS between Temporal services

Self-hosted Temporal supports mutual TLS with separate settings for internode traffic between cluster services and for frontend traffic from clients and workers. With it enabled, each side presents a certificate that the other verifies, so a process without a trusted certificate cannot call the frontend or join the cluster. Plan certificate issuance, rotation and revocation before go-live, and confirm that both the internode and frontend settings are configured, since enabling only one leaves the other path open.

Encrypt schedule payloads with codecs

Schedule inputs are passed to the workflows they start, and they can carry customer identifiers, account references or credentials that should not be readable in persistence. Custom payload codecs encrypt that data before it is stored, and a codec server supports decryption for authorized viewers. Decide which schedule inputs count as sensitive, keep encryption keys in your secrets manager, and test that an unauthorized viewer sees only encrypted content. Confirm that every worker and client reading these payloads uses the same codec settings.

Operating schedules at scale

Once recurring jobs run as Temporal Schedules, the operational risk shifts from a crontab on one host to a registry of schedule objects spread across namespaces. The first task is an inventory: list every schedule ID, its namespace, its spec type (interval or calendar), its overlap policy, its paused state, and the workflow type it starts. Adopt a naming convention that encodes the owning team, the business purpose and the environment, for example billing-invoice-export-prod, so that a schedule found in a search can be traced to an owner without opening the code. Store the inventory in your configuration repository and generate it from the server rather than from memory, because schedules created through the UI or ad hoc CLI calls are the ones most likely to drift from what the team believes is running.

Monitoring needs four signals. Track run creation, meaning whether each schedule is starting workflow executions at the frequency its spec implies. Track missed runs, which Temporal records when a run is skipped or delayed, so the catch-up behavior you chose during design is visible rather than silent. Track workflow failures for every workflow a schedule starts, since a schedule that starts successfully but fails every run looks healthy at the scheduler level. Finally, alert on paused schedules. A pause is a deliberate control, but a pause left in place after an incident is an outage nobody is watching. Route paused-schedule alerts to the owning team with an expiry or review date, and confirm with your monitoring stack which Temporal metrics and visibility queries your version exposes for these signals before you build dashboards on them.

Capacity grows with run frequency in two places. Worker capacity grows because every run consumes task slots, worker memory and downstream calls, so a schedule that moves from hourly to every minute multiplies that load even when each run is cheap. Visibility store load grows because every run creates workflow executions that the visibility store indexes and that operators search. High-frequency schedules are therefore the main reason visibility sizing changes. Temporal documents Elasticsearch or OpenSearch as the recommended advanced visibility store for anything beyond a few workflow executions, and the choice of store shapes how quickly operators can find a schedule's recent runs or filter failures. Test your query patterns against the store you plan to run, not against a development setup.

Confirm the schedule limits for your server version before you commit to a design. Check the documented maximum number of schedules per namespace, the number of actions a schedule may buffer, and any limits on spec complexity or history size for schedules, because these limits change between releases and determine whether one namespace can hold your whole inventory. Also confirm how your version reports missed-run and buffer behavior in visibility, and whether the Temporal Cloud pricing model would change the cost of high-frequency schedules if you later move the workload. Record the answers in the inventory so the next upgrade review checks them again.

Limits to confirm before moving production jobs

These items decide whether Temporal schedules can carry your recurring jobs without surprises. Each one is a limit or behavior that documentation may describe differently by server or SDK version, so confirm it for your exact versions. Run the staging checks before moving any production job, and record the answers as part of the design record.

Schedule and action limits per namespace

Confirm the ceiling on schedules and scheduled actions that one namespace can hold before you map jobs onto namespaces. Ask for the documented limit for your server version, and find out whether your deployment can change it. In a staging cluster, load the planned number of schedules at production-like frequencies, then record creation, pause and update timings alongside database load. Use that measurement, not an assumed figure, to decide whether one namespace or several fits your job inventory.

Catch-up window behavior

A schedule that was paused or unavailable can launch missed actions when it resumes, and the catch-up window controls how far back it looks. Ask the vendor how the window is set, what its maximum value is, and how many missed actions start when the window is exceeded. In staging, stop workers for a planned interval, restart them, and compare the executions started on recovery with the expected count. Check the result against what each job can tolerate, such as duplicate invoices or repeated data loads.

Time zone database handling

Calendar specs that use named time zones depend on a time zone database, and an outdated database can shift run times around daylight saving changes. Ask which time zone data version your server image and each SDK use, and how your team receives updates. In staging, create schedules in every time zone your jobs use, set them to cross an upcoming transition, and compare observed run times with the expected local times. Record which team owns each update.

Failover and archival behavior

Namespace failover and archival can change what a schedule does next, which matters for jobs that must fire exactly once. Ask the vendor or documentation how schedule state, pause status and pending actions carry through a planned handover and a forced failover, and whether archived run history stays queryable. In a staging pair of clusters, pause a schedule, trigger each failover type you plan to use, and check that the schedule keeps its state and fires the expected number of times. Record the result for each type.

SDK versions that expose schedule APIs

Schedule APIs are not necessarily available in every SDK release, and workers and clients may run different versions during a rolling deployment. Ask each SDK maintainer which release first exposes create, update, pause, backfill and delete for schedules, and which server version it requires. In staging, build a minimal worker and client for each language your team uses and run the full schedule lifecycle. Pin the versions that pass and record them in your deployment baseline.

Feature support for your job patterns

Check that schedules support the specific behaviors your jobs depend on: overlap policies, pausing, backfilling missed runs, calendar and interval specs, and jitter. Ask the vendor which of these work on your server and SDK versions and whether any are documented as experimental. In staging, write a test for each job pattern in your inventory, including the case where a run is still active when the next one is due. Keep the results with the design decision for each job.

What does moving cron jobs to Temporal schedules cost and take?

What drives the cost of moving cron jobs to Temporal Schedules?

The main drivers are how many jobs you have, how much logic sits inside each one, and how ready the Temporal cluster is to receive them. Job count and inventory: each cron entry needs an owner, a schedule definition, a target and a decision about overlapping and missed runs, so a large inventory means more design and test time. Workflow complexity: a job that runs one script is simpler than one that calls several APIs, waits for approval or starts child workflows. Cluster readiness: persistence, visibility and access control must be in place before production load arrives. Testing: comparing old and new results for each job in staging adds effort per job, and security and migration testing add more.

Which costs are one-time and which recur after go-live?

Discovery, design, worker code, cutover and verification are one-time costs. Recurring costs are cluster operation, which covers compute, the persistence database, the visibility store, backups, monitoring, on-call coverage and upgrades. The Temporal Server is licensed under the MIT license, so the server software itself carries no license fee, but infrastructure and staff time still do. If you use Temporal Cloud, recurring costs are usage fees plus any support add-on. Temporal Cloud Business support starts at 500 US dollars per month, according to the Temporal pricing page, while Enterprise and Mission Critical support are quoted by sales. Ask for each recurring item to be listed separately in a quote.

How does Temporal Cloud pricing change the total cost compared with self-hosting?

Temporal Cloud is a managed service from Temporal Technologies, so it removes cluster operation from your team but adds usage fees. The Temporal pricing page lists pay-as-you-go pricing starting at 50 US dollars per million actions, active storage at 0.042 US dollars per GB-hour and retained storage at 0.00105 US dollars per GB-hour. Cron-style schedules create an action on every run, so the number of runs per day, the number of schedules and the amount of history retained all affect the bill. Confirm on the pricing page how actions and storage are counted for your run frequency. Also check where your data must live, since self-hosting keeps all data in infrastructure you control.

Can our own platform team move cron jobs to Temporal without outside help?

A team with Temporal experience can do the work alone, and the MIT license places no restriction on that. The harder question is whether you have the capacity and the skills for design, testing and cutover. Some risks are easy to miss. If no Authorizer is configured, self-hosted Temporal uses a default that allows every API request with no authentication or access control, so access must be designed before anything runs in production. Server upgrades must also move one minor version at a time. Consider a partner when the inventory is large, when audit evidence is required, or when your team cannot staff staging and cutover. DeployOpen plans and implements this work under a written, fixed scope.

How long does a cron to Temporal Schedules migration take?

Duration depends on scope, and no fixed timeline applies to every migration. The main inputs are the number of jobs, their complexity, the state of your cluster and the change windows your organization allows. A typical plan moves through inventory, design, build, staging tests, a parallel run where old and new schedules are compared, and then cutover. The length of the parallel run depends on how many business cycles your owners want to observe before retiring cron. Most DeployOpen engagements are fixed-scope, so scope, deliverables and timelines are agreed in writing before work starts. Ask any partner to show the phases, the dependencies on your change calendar and the decision points that could extend the plan.

What does the client need to provide before a Temporal schedule migration starts?

The work depends on several inputs from your side. Name an owner for each cron job who can approve its new definition and its cutover. Provide a job inventory that lists each schedule, its command or target, its runtime, its dependencies and its current failure handling. Provide a staging environment that matches production closely enough to test overlap behavior, pausing and recovery. Share the change windows and freeze periods for each system the jobs touch. Name a security reviewer for the Temporal access model, including Authorizer rules, mTLS and single sign-on for the Web UI. Finally, agree who signs off each cutover and who can trigger a rollback.

Which cron jobs need the most design work before they move?

Jobs that can overlap with themselves, jobs that must not run during an outage, and jobs that must catch up after one need explicit decisions. Each of these maps to a Schedules setting: an overlap policy that controls what happens when a run is still active, a pause state for maintenance windows, and a backfill or catch-up choice for missed runs after an outage. Jobs that call external systems without idempotency keys are also risky, because a retried or backfilled run can repeat a payment, email or record update. Long-running jobs, jobs with side effects in several systems and jobs with unclear owners usually need the most review. Record each decision in the inventory so it can be tested and audited later.

What should testing cover before cutting over cron jobs to Temporal?

Testing should prove that each schedule does what the cron entry did, and that it fails safely. For each job, compare outputs in staging against the cron baseline. Test the overlap policy by making a run exceed its interval. Test pause and resume during a maintenance window. Simulate an outage and confirm the missed-run behavior matches the decision you recorded. Confirm that the persistence and visibility stores are on versions Temporal documents as supported, such as PostgreSQL 13 to 16 for persistence, and that Elasticsearch or OpenSearch is used for visibility once execution volume grows. Verify the access controls, encryption settings and a rollback path that re-enables the cron entry.

How DeployOpen scopes and delivers schedule migrations

DeployOpen plans and implements Temporal schedules as a replacement for cron jobs in infrastructure you control. We start by inventorying each recurring job, then agree on a written, fixed scope before any build work begins, with ongoing support available as a retainer.

How we scope

  • Inventory every cron entry and Temporal Cron Job, recording its schedule, runtime, owner and downstream effects.
  • Classify each job by overlap tolerance, catch-up needs after an outage and whether it must pause for maintenance windows.
  • Agree the written scope in advance, listing which schedules, workers, namespaces and cutover steps are included.

How we build

  • Define interval and calendar specs, time zone handling and overlap policies for each schedule, documented in the design record.
  • Build the workflows and activities with idempotent steps, then test missed-run backfill and pause behavior in a non-production namespace.
  • Confirm the self-hosted server, persistence and visibility configuration and access controls match your security baseline before cutover.

How we hand over

  • Run parallel execution with the old cron jobs disabled step by step, then record the cutover and rollback steps.
  • Document schedule definitions, ownership, alert routes and runbooks for pausing, resuming and backfilling schedules.
  • Train your operators on reviewing schedule history, handling failed runs and changing specs through version-controlled changes.

How we support

  • Plan Temporal Server upgrades one minor version at a time, with schema upgrades run before server upgrades when release notes require them.
  • Review schedule volume and visibility store sizing as the number of recurring jobs grows over time.
  • Offer ongoing support, upgrades and testing as a retainer, with scope for each period agreed in writing.

Sources and further reading

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.