Schedule background functions in Firebase using Cloud Functions v2 with the onSchedule() trigger and a cron expression. The function runs automatically at the specified interval using Google Cloud Scheduler. Common use cases include nightly data cleanup, daily email digests, periodic API syncs, and usage report generation. Scheduled functions require the Blaze pay-as-you-go plan because Cloud Scheduler is a paid Google Cloud service.
Scheduling Background Functions in Firebase
Firebase Cloud Functions v2 lets you schedule functions to run at regular intervals using cron expressions. Under the hood, Firebase creates a Google Cloud Scheduler job that triggers your function via Pub/Sub. This tutorial covers creating scheduled functions, writing cron expressions, handling timezones, managing long-running tasks, and monitoring executions in production.
Prerequisites
- A Firebase project on the Blaze pay-as-you-go plan
- Firebase CLI installed and logged in
- Cloud Functions initialized in your project (firebase init functions)
- Node.js 18, 20, or 22 installed locally
Step-by-step guide
Create a basic scheduled function with onSchedule()
Create a basic scheduled function with onSchedule()
Import onSchedule from firebase-functions/v2/scheduler and define your function. The first argument is the schedule configuration including the cron expression. The second argument is the async handler function. The handler receives a ScheduledEvent object with the schedule time. Deploy with firebase deploy --only functions and Firebase automatically creates the Cloud Scheduler job.
1import { onSchedule } from 'firebase-functions/v2/scheduler'2import { logger } from 'firebase-functions'34// Run every day at midnight UTC5export const dailyCleanup = onSchedule('every day 00:00', async (event) => {6 logger.info('Running daily cleanup', { scheduledTime: event.scheduleTime })78 // Your cleanup logic here9 await deleteExpiredSessions()10 await archiveOldRecords()1112 logger.info('Daily cleanup completed')13})Expected result: After deploying, the function runs automatically every day at midnight UTC. You can see executions in the Cloud Functions logs.
Write cron expressions for common schedules
Write cron expressions for common schedules
Cron expressions have five fields: minute, hour, day-of-month, month, and day-of-week. Firebase also supports the more readable App Engine format. Here are common scheduling patterns you can use directly in your onSchedule() definition.
1import { onSchedule } from 'firebase-functions/v2/scheduler'23// Every 5 minutes4export const frequentCheck = onSchedule('*/5 * * * *', async () => {5 // Runs at :00, :05, :10, :15, etc.6})78// Every hour at minute 309export const hourlySync = onSchedule('30 * * * *', async () => {10 // Runs at 0:30, 1:30, 2:30, etc.11})1213// Every Monday at 9:00 AM14export const weeklyReport = onSchedule('0 9 * * 1', async () => {15 // 1 = Monday16})1718// First day of every month at 6:00 AM19export const monthlyBilling = onSchedule('0 6 1 * *', async () => {20 // Day 1 of each month21})2223// App Engine format alternatives24export const everyHour = onSchedule('every 1 hours', async () => {})25export const every15Min = onSchedule('every 15 minutes', async () => {})Expected result: Each function runs at its specified interval. Cloud Scheduler manages the timing automatically.
Configure timezone and function options
Configure timezone and function options
By default, scheduled functions use UTC. Pass a configuration object instead of a string to set the timezone, memory, timeout, and retry behavior. Use the IANA timezone identifier (like 'America/New_York') to schedule functions in a specific timezone. Set timeoutSeconds for functions that need more than the default 60 seconds.
1import { onSchedule } from 'firebase-functions/v2/scheduler'23export const morningDigest = onSchedule(4 {5 schedule: 'every day 08:00',6 timeZone: 'America/New_York',7 timeoutSeconds: 300, // 5 minutes8 memory: '512MiB',9 retryCount: 310 },11 async (event) => {12 // Runs at 8:00 AM Eastern Time every day13 // Automatically handles EST/EDT transitions14 await sendDailyDigestEmails()15 }16)Expected result: The function runs at 8:00 AM Eastern Time regardless of DST changes. Failed executions are retried up to 3 times.
Interact with Firestore from a scheduled function
Interact with Firestore from a scheduled function
Scheduled functions commonly perform database maintenance tasks. Use the Firebase Admin SDK to access Firestore without security rule restrictions. The Admin SDK is automatically initialized in Cloud Functions — just import it and use it. Common patterns include deleting expired documents, aggregating daily stats, and archiving old data.
1import { onSchedule } from 'firebase-functions/v2/scheduler'2import { logger } from 'firebase-functions'3import { getFirestore, Timestamp } from 'firebase-admin/firestore'4import { initializeApp } from 'firebase-admin/app'56initializeApp()7const db = getFirestore()89export const cleanupExpiredSessions = onSchedule(10 {11 schedule: 'every 1 hours',12 timeoutSeconds: 120,13 memory: '256MiB'14 },15 async () => {16 const now = Timestamp.now()17 const expiredQuery = db18 .collection('sessions')19 .where('expiresAt', '<', now)20 .limit(500) // Batch to stay within limits2122 const snapshot = await expiredQuery.get()2324 if (snapshot.empty) {25 logger.info('No expired sessions to clean up.')26 return27 }2829 const batch = db.batch()30 snapshot.docs.forEach((doc) => {31 batch.delete(doc.ref)32 })3334 await batch.commit()35 logger.info(`Deleted ${snapshot.size} expired sessions.`)36 }37)Expected result: Every hour, the function checks for expired sessions and deletes them in batches, logging the number of documents removed.
Deploy and monitor scheduled functions
Deploy and monitor scheduled functions
Deploy your scheduled functions with the Firebase CLI. After deployment, Firebase creates the Cloud Scheduler job automatically. Monitor executions in the Firebase Console under Functions > Logs, or in Google Cloud Console under Cloud Scheduler to see job history and next run times. You can also manually trigger a scheduled function from Cloud Scheduler for testing.
1# Deploy all functions2firebase deploy --only functions34# Deploy a specific function5firebase deploy --only functions:dailyCleanup67# View function logs8firebase functions:log --only dailyCleanup910# List Cloud Scheduler jobs (via gcloud)11gcloud scheduler jobs list1213# Manually trigger for testing14gcloud scheduler jobs run firebase-schedule-dailyCleanup-us-central1Expected result: The function is deployed and Cloud Scheduler shows the job with its next run time. Logs confirm successful executions.
Complete working example
1import { onSchedule } from 'firebase-functions/v2/scheduler'2import { logger } from 'firebase-functions'3import { initializeApp } from 'firebase-admin/app'4import { getFirestore, Timestamp, FieldValue } from 'firebase-admin/firestore'56initializeApp()7const db = getFirestore()89// Daily cleanup: delete expired sessions and old notifications10export const dailyCleanup = onSchedule(11 {12 schedule: 'every day 02:00',13 timeZone: 'UTC',14 timeoutSeconds: 300,15 memory: '512MiB',16 retryCount: 217 },18 async () => {19 const now = Timestamp.now()20 let totalDeleted = 02122 // Delete expired sessions in batches23 let hasMore = true24 while (hasMore) {25 const snapshot = await db26 .collection('sessions')27 .where('expiresAt', '<', now)28 .limit(500)29 .get()3031 if (snapshot.empty) {32 hasMore = false33 break34 }3536 const batch = db.batch()37 snapshot.docs.forEach((doc) => batch.delete(doc.ref))38 await batch.commit()39 totalDeleted += snapshot.size4041 if (snapshot.size < 500) hasMore = false42 }4344 logger.info(`Daily cleanup: deleted ${totalDeleted} expired sessions`)45 }46)4748// Hourly stats aggregation49export const hourlyStats = onSchedule(50 {51 schedule: '0 * * * *',52 timeoutSeconds: 120,53 memory: '256MiB'54 },55 async () => {56 const oneHourAgo = Timestamp.fromMillis(Date.now() - 60 * 60 * 1000)5758 const snapshot = await db59 .collection('events')60 .where('createdAt', '>', oneHourAgo)61 .get()6263 const today = new Date().toISOString().split('T')[0]64 await db.doc(`stats/${today}`).set(65 {66 hourlyEvents: FieldValue.increment(snapshot.size),67 lastUpdated: Timestamp.now()68 },69 { merge: true }70 )7172 logger.info(`Hourly stats: ${snapshot.size} events in the last hour`)73 }74)Common mistakes when scheduling Background Functions in Firebase
Why it's a problem: Trying to deploy scheduled functions on the Spark free plan, which does not support Cloud Scheduler
How to avoid: Upgrade to the Blaze pay-as-you-go plan. Cloud Scheduler and Cloud Functions are Blaze-only services. Cloud Scheduler costs $0.10 per job per month.
Why it's a problem: Not handling the case where a scheduled function takes longer than the default 60-second timeout
How to avoid: Set timeoutSeconds in the function options. V2 scheduled functions support up to 1800 seconds (30 minutes). For longer tasks, break the work into smaller batches.
Why it's a problem: Creating infinite loops by writing to a collection that triggers another Cloud Function, which writes back
How to avoid: Use conditional checks in Firestore triggers to prevent re-processing. For scheduled functions, write to a separate output collection or use a processed flag to avoid loops.
Why it's a problem: Assuming the function runs at the exact scheduled second — Cloud Scheduler has a delivery tolerance
How to avoid: Cloud Scheduler guarantees at-least-once delivery but may trigger slightly late (usually within a few seconds). Do not rely on exact-second timing. Use the event.scheduleTime property for the intended execution time.
Best practices
- Always set a timezone explicitly for user-facing schedules to avoid confusion with UTC and daylight saving time
- Set appropriate timeoutSeconds based on the expected duration of your task — do not rely on the default
- Use batched operations when processing large datasets to stay within Firestore's 500-operation batch limit
- Log the start and completion of scheduled tasks for monitoring and debugging
- Set retryCount to at least 2 for critical tasks like billing or email sends to handle transient failures
- Use gcloud scheduler jobs run to test scheduled functions manually during development
- Keep scheduled functions idempotent — they should produce the same result if run twice with the same data
- Monitor Cloud Scheduler job history in Google Cloud Console to catch silent failures
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I need to create several scheduled Firebase Cloud Functions v2: a daily cleanup that deletes expired Firestore documents, an hourly stats aggregator, and a weekly email digest. Show me how to write these using onSchedule with cron expressions, timezone configuration, proper timeout settings, and Firestore Admin SDK operations including batched deletes.
Create a Firebase Cloud Functions v2 scheduled function that runs every night at 2 AM Eastern Time. It should query a Firestore 'sessions' collection for documents where expiresAt is in the past, delete them in batches of 500, and log the total count deleted. Include timezone configuration, error handling, timeout settings, and retry configuration.
Frequently asked questions
Do scheduled functions require the Blaze plan?
Yes. Cloud Scheduler is a paid Google Cloud service, and Cloud Functions require the Blaze plan. Cloud Scheduler costs $0.10 per job per month. The Blaze plan includes a generous free tier for Cloud Functions (2 million invocations per month).
What is the maximum timeout for a scheduled function?
V2 scheduled functions support a maximum timeout of 1800 seconds (30 minutes). V1 functions are limited to 540 seconds (9 minutes). If your task needs more than 30 minutes, break it into smaller sub-tasks triggered in sequence.
How do I test a scheduled function without waiting for the schedule?
Use the Firebase Emulator Suite for local testing. For deployed functions, use gcloud scheduler jobs run followed by the job name to trigger the function manually. The job name follows the pattern firebase-schedule-functionName-region.
Can I schedule a function to run every second or every 10 seconds?
No. The minimum interval for Cloud Scheduler is 1 minute. For sub-minute intervals, consider using a Pub/Sub-triggered function with a Cloud Tasks queue, or run a persistent process on a reserved VM.
What happens if a scheduled function fails?
Cloud Scheduler retries the delivery based on your retryCount setting. The default is 0 retries. Set retryCount in the function options to enable automatic retries. Each retry is a separate function invocation.
Can RapidDev help me set up scheduled background tasks in Firebase?
Yes. RapidDev can design and implement scheduled Cloud Functions for data cleanup, report generation, API synchronization, and other recurring tasks, including monitoring and error handling.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation