Build a Serverless Cron Job System with Cloudflare Workers 3, DynamoDB 2026, and Sentry 7
Serverless cron jobs eliminate the need to manage dedicated servers for scheduled task execution, offering automatic scaling, reduced operational overhead, and pay-per-use pricing. This guide walks through building a fully serverless cron job system using Cloudflare Workers 3 for compute, DynamoDB 2026 for persistent job storage, and Sentry 7 for error monitoring and performance tracking.
Prerequisites
- Cloudflare account with Workers 3 enabled
- AWS account with DynamoDB 2026 access (IAM user with read/write permissions to a dedicated cron job table)
- Sentry 7 account with a Cloudflare Workers project configured
- Node.js 22+ and Wrangler 3 CLI installed locally
Step 1: Initialize Cloudflare Workers Project
Start by creating a new Cloudflare Workers project using Wrangler:
wrangler init serverless-cron-system
cd serverless-cron-system
Update wrangler.toml to enable Cron Triggers and set up environment variables for AWS and Sentry credentials:
name = "serverless-cron-system"
main = "src/index.js"
compatibility_date = "2026-01-01"
cron = ["* * * * *"] # Run every minute to check for pending jobs
[vars]
DYNAMODB_TABLE_NAME = "cron-jobs-2026"
[secrets]
AWS_ACCESS_KEY_ID = "your-aws-access-key"
AWS_SECRET_ACCESS_KEY = "your-aws-secret-key"
SENTRY_DSN = "your-sentry-dsn"
Step 2: Configure DynamoDB 2026 Table
Create a DynamoDB 2026 table named cron-jobs-2026 with the following schema:
-
jobId(String, Primary Key): Unique identifier for each cron job -
schedule(String): Cron expression defining job execution frequency (e.g.,0 0 * * *for daily midnight runs) -
payload(Map): Arbitrary data to pass to the job executor -
lastRunAt(Number, Optional): Unix timestamp of the last successful execution -
status(String): Current state of the job (active,paused,failed) -
nextRunAt(Number): Precomputed Unix timestamp of the next scheduled execution
Install the AWS SDK 2026 for JavaScript to interact with DynamoDB from Workers:
npm install @aws-sdk/client-dynamodb-2026 @aws-sdk/lib-dynamodb-2026
Step 3: Implement Cron Job Logic
Create the main worker logic in src/index.js to fetch pending jobs from DynamoDB, execute them, and update their status:
import { DynamoDBClient } from "@aws-sdk/client-dynamodb-2026";
import { DynamoDBDocumentClient, ScanCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb-2026";
import * as Sentry from "@sentry/cloudflare-7";
// Initialize Sentry 7
Sentry.init({
dsn: "SENTRY_DSN",
environment: "production",
release: "serverless-cron-system@1.0.0",
});
// Initialize DynamoDB client
const dynamoClient = new DynamoDBClient({
region: "us-east-1",
credentials: {
accessKeyId: "AWS_ACCESS_KEY_ID",
secretAccessKey: "AWS_SECRET_ACCESS_KEY",
},
});
const docClient = DynamoDBDocumentClient.from(dynamoClient);
export default {
async scheduled(event, env, ctx) {
try {
const now = Math.floor(Date.now() / 1000);
// Fetch all active jobs with nextRunAt <= now
const scanCommand = new ScanCommand({
TableName: env.DYNAMODB_TABLE_NAME,
FilterExpression: "#status = :active AND nextRunAt <= :now",
ExpressionAttributeNames: { "#status": "status" },
ExpressionAttributeValues: { ":active": "active", ":now": now },
});
const { Items: jobs } = await docClient.send(scanCommand);
// Execute each pending job
for (const job of jobs) {
ctx.waitUntil(
executeJob(job, env)
.then(() => updateJobStatus(job.jobId, now, env))
.catch((error) => {
Sentry.captureException(error, { tags: { jobId: job.jobId } });
updateJobStatus(job.jobId, now, env, "failed");
})
);
}
} catch (error) {
Sentry.captureException(error);
}
},
};
async function executeJob(job, env) {
// Add custom job execution logic here (e.g., send email, call API)
console.log(`Executing job ${job.jobId} with payload:`, job.payload);
// Simulate async work
await new Promise((resolve) => setTimeout(resolve, 1000));
}
async function updateJobStatus(jobId, lastRunAt, env, status = "active") {
const nextRunAt = computeNextRun(jobId); // Implement cron parser logic
const updateCommand = new UpdateCommand({
TableName: env.DYNAMODB_TABLE_NAME,
Key: { jobId },
UpdateExpression: "SET lastRunAt = :lastRun, #status = :status, nextRunAt = :nextRun",
ExpressionAttributeNames: { "#status": "status" },
ExpressionAttributeValues: {
":lastRun": lastRunAt,
":status": status,
":nextRun": nextRunAt,
},
});
await docClient.send(updateCommand);
}
Step 4: Integrate Sentry 7
Install the Sentry 7 Cloudflare Workers SDK:
npm install @sentry/cloudflare-7
The Sentry initialization is already included in the worker code above. Sentry 7 will automatically capture unhandled exceptions, track job execution performance, and send alerts for failed jobs. Configure Sentry dashboards to monitor:
- Job execution success/failure rates
- Average job execution duration
- Worker invocation frequency
Step 5: Test the System
Add a test cron job to DynamoDB 2026 using the AWS CLI:
aws dynamodb put-item \
--table-name cron-jobs-2026 \
--item '{
"jobId": {"S": "test-job-1"},
"schedule": {"S": "* * * * *"},
"payload": {"M": {"message": {"S": "Hello from serverless cron!"}}},
"status": {"S": "active"},
"nextRunAt": {"N": "'$(date +%s)'"}
}'
Trigger a local test of the worker using Wrangler:
wrangler dev --test-scheduled
Check Sentry 7 for captured events and verify job execution logs in the Cloudflare Workers dashboard.
Step 6: Deploy and Monitor
Deploy the worker to Cloudflare Workers 3:
wrangler deploy
Cloudflare will automatically run the worker every minute per the cron trigger in wrangler.toml. Use Sentry 7's alerting features to notify your team via Slack or email when jobs fail, and DynamoDB 2026's built-in monitoring to track table throughput and storage usage.
Conclusion
This serverless cron job system combines the low-latency compute of Cloudflare Workers 3, the scalable storage of DynamoDB 2026, and the robust monitoring of Sentry 7 to deliver a reliable, maintenance-free scheduled task platform. Extend the system by adding support for one-off jobs, job retries, or a REST API for managing cron jobs via DynamoDB.
Top comments (0)