Migrating Bubble Scheduled Workflows: Keep Pending Jobs and Avoid Duplicate Actions
A rebuild can pass its screen-by-screen tests and still miss tomorrow’s appointment reminder. The reminder was scheduled last week, so clicking through the new app today never exercises it.
To migrate Bubble scheduled workflows safely, inventory both the workflow definitions and the work already waiting to run. Give every pending business action a destination, preserve its timing and cancellation rules, and test what happens when an attempt fails halfway through. Recreating a schedule is only part of that job.
This guide is for founders and technical leads accepting a Bubble-to-code rebuild. It produces a transfer record you can review with an engineer, rather than prescribing one queue product or promising that code automatically makes background work reliable.
What exactly needs to move?
Separate three things: the rule that creates work, the scheduled instance waiting to execute, and the business result it should produce. A daily scheduler, one customer’s queued reminder and the reminder actually sent are different records. Losing any of those distinctions makes reconciliation difficult.
Start with Bubble’s scheduler. It separates Live and Development schedules and shows the event, scheduled time, ID, scheduling user and parameters. Times are displayed in the browser’s timezone. Immediate external Workflow API requests do not appear in that view, so it is not an inventory of every backend execution.
Confirm how your team will capture and reconcile the inventory before planning an import. Use the scheduler alongside any scheduling records your app retained. Do not assume a database export includes pending jobs or historical workflow definitions. Record the capture time and account for jobs created or completed afterward.
Then inspect the workflows that create more work: recurring events, recursive workflows that schedule their next run, bulk scheduling actions and integrations. A recursive chain may have only its next execution queued. Counting visible jobs will not reveal its full future behavior.
Keep this review separate from the wider Bubble data migration checklist. Matching customer records does not prove that the reminders, exports or delayed status changes associated with those records survived.
Why can the current editor be an incomplete specification?
Bubble documents that a scheduled API workflow uses the structure and actions saved when it was scheduled. Editing the workflow later does not update the waiting instance; applying the new definition requires cancelling and rescheduling. That is a workflow-definition snapshot, not a promise that every database value it references is frozen.
Ask which behavior the replacement must preserve for existing jobs. A reminder scheduled under an older rule may have different wording or eligibility from a newly scheduled one. Silently rebuilding every pending job from the latest editor definition can change a commitment without anyone approving it.
Also distinguish selection time from execution time. For Schedule API workflow on a list, Bubble evaluates list membership when scheduling. A later change that would exclude a record from that search does not automatically remove its scheduled instance. Decide whether the target worker must recheck eligibility before acting, and make any intentional behavior change explicit.
Build a transfer record around the business action
Use one entry per pending business occurrence: one intended reminder, report or status change. Keep links to the original and replacement jobs. Start with these fields; add app-specific details where they affect the outcome:
- Business occurrence key: a stable identity for this action, such as a reminder for one booking revision. Keep it distinct from a queue’s delivery or job ID.
- Source: Live or Development, Bubble scheduled ID where available, event name, scheduling identity, whether “Ignore privacy rules” was enabled, and the applicable workflow behavior/version.
- Target and timing: mapped record ID, required parameters, due time in UTC and the intended business timezone or recurrence anchor.
- Eligibility: what must still be true when the job runs, and what change should cancel, replace or suppress it.
- Outcome and transfer decision: expected database change or external action; drain in Bubble, recreate in code, cancel as obsolete, or hold for review.
- Evidence: replacement job ID, source cancellation or completion evidence, latest attempt, business completion status and recovery owner.
Do not turn the transfer record into a new copy of sensitive payloads. Prefer controlled references to the minimum data the worker needs. Preserve tenant and authorization context without copying user session tokens into jobs. Map that execution context to explicit permissions in the replacement; do not make every migrated worker an unrestricted administrator.
Treat missing IDs or unclear old behavior as unresolved entries, not as permission to discard work. Bubble returns an instance ID when scheduling an API workflow, which can be stored for later cancellation before its scheduled execution. Check what your application actually retained.
Choose an outcome for every pending job
For each workflow family, decide whether existing work finishes in Bubble or moves to the replacement. Do not leave both systems free to perform the same customer-facing action. The transfer boundary must cover both creating new jobs and executing jobs already queued.
Draining means Bubble completes the selected backlog while new work is created elsewhere. Recreating means the replacement receives the outstanding business occurrences and the old execution path is disabled or otherwise prevented from acting on them. Obsolete work can be cancelled only after its reason is verified. Ambiguous work stays on hold for a named owner.
A cancellation request is not proof that an in-flight action stopped. Account for work already running or completing while the inventory is taken. Reconcile source status, target status and external-provider results before retrying an uncertain action. The broader cutover plan covers launch and recovery; this transfer record covers each outstanding obligation.
Run the import twice in a safe test environment. The second import should not create a second business occurrence. That test catches a common operational problem: a migration script is rerun after a timeout, but nobody knows which rows were accepted the first time.
Make timing and recurrence explicit
Record whether “9 a.m.” means a fixed UTC instant, a customer’s local time, or a schedule anchored to its first run. Preserve the intended rule, including daylight-saving transitions, rather than translating every recurring workflow into the same cron expression.
Bubble’s recurring events and recursive workflows are different mechanisms. A recurring schedule has its own frequency and start behavior; a recursive workflow creates its next execution itself. Replacing either with a scheduler requires a decision about missed runs, overlapping runs and when the next occurrence is created.
Define an overdue-work policy by business consequence. A delayed report may still be useful. A reminder for an appointment that ended yesterday may be confusing. Choose whether to run, skip, consolidate or request review, and record that outcome instead of silently dropping overdue jobs.
A due time is not a completion guarantee. Bubble documents delays under load; queue systems also have timing constraints. For example, BullMQ says a delayed job may not start at the exact requested time when workers are busy. Agree the acceptable delay for the business action and alert when it is exceeded.
Design retries around effects, not just job IDs
A retry should finish the intended result without repeating an effect already completed. This property is called idempotency. It matters when a worker sends an email or changes an external system, then crashes before recording success locally.
Queue-level duplicate suppression is useful but limited. BullMQ, for example, rejects a new job with an ID already present in the same queue. Once that job is removed, the ID can be used again. A queue ID alone therefore does not prove that a business action can never repeat.
Keep durable completion evidence keyed to the business occurrence. Two workers must not both claim the same occurrence because neither sees a completion record yet. Use an atomic claim or equivalent concurrency control, and test overlapping attempts. This still does not remove the uncertain window around an external action. Use a provider’s idempotency feature where it supports the operation, and preserve the same key across retries. When the provider offers no reliable duplicate protection or result lookup, route ambiguous outcomes to reconciliation rather than blindly replaying them.
A local database transaction cannot roll back an email already sent. Separate recoverable steps and record their outcomes so a retry does not restart the entire sequence indiscriminately. BullMQ’s idempotent-job guidance similarly recommends small, atomic jobs; the exact implementation depends on the effect and the systems involved.
Specify the retry limit, delay between attempts, terminal failure state and person responsible for recovery. Keep enough history to explain what happened. A useful operations view shows the occurrence, due time, attempt count, last error, next retry and whether the business result is confirmed, failed or still uncertain.
An illustrative reminder transfer
Consider fictional booking DEMO-42, revision 3. Its reminder is due at 08:00 UTC for an appointment whose local timezone is Europe/Warsaw. This is an example, not a client case.
The transfer record links the old Bubble scheduled ID to the new job and uses a business key such as appointment-reminder/DEMO-42/revision-3. Before sending, the worker checks that the booking still exists, is active and is still revision 3. If the appointment is rescheduled, the old occurrence is suppressed and the new revision gets its own occurrence.
Now interrupt the worker after the test email provider accepts the message but before local completion is saved. A retry must either obtain the existing result through supported provider behavior or leave the outcome for reconciliation. Simply checking a local “sent” flag, finding it blank and sending again fails the test.
The acceptance evidence is a single intended reminder, or an explicit suppressed/held outcome with a reason. “The new job ran successfully” is too weak: the job might have produced the wrong action twice.
What should pass before you retire the old scheduler?
Use test recipients, provider sandboxes and isolated data. Do not shadow-run both systems against real customers merely to compare results. For each important workflow family, collect evidence for these checks:
- A future job arrives with the right mapped record, parameters and timing rule; importing the same source inventory again adds no duplicate occurrence.
- An edited, cancelled or deleted business record produces the agreed suppression or replacement outcome.
- An overdue occurrence follows its documented catch-up policy; a recurring job keeps the intended timezone and occurrence boundary.
- A partial failure, duplicate delivery and overlapping attempts do not repeat a confirmed external effect.
- A source job already running during transfer is reconciled before its target equivalent is released.
- A failed job exposes its attempts, next action and owner; an operator can recover it without losing completion history.
- Every inventoried occurrence is accounted for as completed, transferred, intentionally cancelled or held. Every source of future jobs has an identified owner.
Do not retire Bubble scheduling while unexplained differences remain. Keep the transfer evidence alongside the runbook so support can answer a customer’s “Why did I receive this?” after the migration team leaves.
Scope the work before choosing the tool
If scheduling is working well on Bubble and no business problem justifies a move, there may be no reason to replace it yet. If a rebuild is already needed, include the backlog, recurring rules, retry behavior and recovery ownership in scope. They are product behavior, even when no screen displays them.
Bubble to Code’s published process includes rebuilding cron jobs and background queues, while its team description brings together practicing Bubble developers and software engineers. Book a migration review and bring one scheduled workflow, its pending-job inventory and its cancellation rules. Use the call to agree what evidence would show the business outcome survived.
Frequently asked questions
- Can I replace every scheduled Bubble workflow with cron?
- No. Cron is suitable for recurring triggers, but a one-off reminder, an existing backlog and a recursive chain have different semantics. Choose the scheduling mechanism after documenting due times, cancellation, recurrence, missed-run policy and the required business outcome.
- Does changing a Bubble workflow update jobs already scheduled?
- Bubble documents that already-scheduled instances retain the workflow structure/actions saved at scheduling time. To apply changes, cancel and reschedule those instances. Check referenced data separately; do not assume the documentation freezes every value the job may read.
- Does a queue guarantee that an email is sent exactly once?
- Do not assume it. Delivery, worker execution and the external effect are separate events. Duplicate protection and recovery must cover the business occurrence and provider behavior, including the uncertain window after an external action succeeds but before local success is recorded.
Sources
- 01Bubble’s scheduler · Bubble Docs
- 02Bubble documents that a scheduled API workflow · Bubble Docs
- 03Schedule API workflow on a list · Bubble Docs
- 04“Ignore privacy rules” · Bubble Docs
- 05Bubble’s recurring events · Bubble Docs
- 06BullMQ says a delayed job · BullMQ Docs
- 07BullMQ, for example, rejects a new job · BullMQ Docs
- 08Atomic claim · PostgreSQL Docs
- 09BullMQ’s idempotent-job guidance · BullMQ Docs
Keep reading
Bubble to Code is an independent service. Not affiliated with, endorsed by, or sponsored by Bubble Group, Inc.
