blog.dopana

Back

Need to automate periodic tasks like backups, email sending, or data processing? AWS Lambda combined with CloudWatch Events/EventBridge is the perfect serverless solution to replace traditional cron jobs.

Overview of Lambda Scheduled Jobs#

AWS Lambda is a serverless compute service that allows you to run code without managing servers. When combined with scheduling services, you can:

  • Automate tasks: Backups, report generation, data cleanup
  • Save costs: Pay only when code executes
  • Auto-scale: Handle increased load without configuration
  • High availability: AWS ensures availability

[!NOTE] AWS currently recommends using EventBridge Scheduler instead of legacy CloudWatch Events for new scheduled jobs.

EventBridge Scheduler is the modern scheduling service with enhanced features compared to CloudWatch Events.

Advantages of EventBridge Scheduler#

  • Timezone support: Not limited to UTC only
  • Flexible time windows: Run within flexible time ranges
  • Built-in retry: Automatic retry on failure
  • One-time schedules: Support for single execution
  • Dead Letter Queue: Better failure handling

Step 1: Create Lambda Function#

Step 2: Create IAM Role#

Lambda needs permissions to execute the job and write logs:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
json

Policy permissions:

Step 3: Create Schedule with EventBridge Scheduler#

Using AWS Console#

  1. Go to EventBridge Scheduler console
  2. Click Create schedule
  3. Select Recurring schedule
  4. Configure schedule expression:
# Run daily at 9:00 AM UTC
cron(0 9 * * ? *)

# Run every 15 minutes
rate(15 minutes)

# Run weekly on Monday 9:00 AM
cron(0 9 ? * MON *)

# Run on first day of each month
cron(0 0 1 * ? *)
bash
  1. Select Lambda function as target
  2. Configure timezone (if needed):
# 9:00 AM Eastern Time
cron(0 9 ? * MON *)
timezone: America/New_York
bash
  1. Configure flexible time window (optional):
# Run within 15 minutes after scheduled time
mode: FLEXIBLE
maximum_window_in_minutes: 15
bash
  1. Setup retry policy:
maximum_retry_attempts: 3
maximum_event_age_in_seconds: 3600
bash

Using AWS CLI#

aws scheduler create-schedule \
  --name daily-job \
  --schedule-expression 'cron(0 9 * * ? *)' \
  --schedule-expression-timezone 'UTC' \
  --target '{
    "Arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function",
    "RoleArn": "arn:aws:iam::123456789012:role/scheduler-role"
  }' \
  --flexible-time-window '{
    "Mode": "FLEXIBLE",
    "MaximumWindowInMinutes": 15
  }'
bash

Using Terraform#

Method 2: CloudWatch Events (Legacy)#

CloudWatch Events is the traditional method, still supported but with fewer features.

Step 1: Create Lambda Function#

Same as Scheduler method.

Step 2: Create CloudWatch Rule#

Using AWS Console#

  1. Go to CloudWatch console
  2. Select Events → Rules
  3. Click Create rule
  4. Select Schedule expression
  5. Enter cron expression:
# 9:00 AM UTC daily
0 9 * * ? *

# Every 5 minutes
rate(5 minutes)
bash
  1. Select Lambda function as target
  2. Configure permission (AWS will auto-create)

Using AWS CLI#

Cron Expression Guide#

EventBridge uses 6-field cron expression:

cron(Minutes Hours Day-of-month Month Day-of-week Year)
text

Example Cron Expressions#

Field Values#

FieldValuesSpecial Characters
Minutes0-59, - * /
Hours0-23, - * /
Day of month1-31, L, W, - * / ? L W
Month1-12, JAN-DEC, - * /
Day of week1-7, SUN-SAT, ?, L, #, - * / ? L #
Year1970-2199, - * /

[!WARNING] Use ? when day-of-month or day-of-week is constrained, don’t use both fields simultaneously.

Best Practices#

1. Idempotency#

Ensure your handler is idempotent for safe retries:

def lambda_handler(event, context):
    # Check if job already ran
    if is_job_already_processed(event):
        logger.info("Job already processed, skipping")
        return {"status": "skipped"}
    
    # Process job
    result = process_job(event)
    
    # Mark job as processed
    mark_job_as_processed(event)
    
    return result
python

2. Dead Letter Queue (DLQ)#

Setup DLQ to handle failed invocations:

import boto3
import json

sqs = boto3.client('sqs')

def send_to_dlq(error_message, event):
    """Send failed event to DLQ"""
    sqs.send_message(
        QueueUrl='YOUR_DLQ_URL',
        MessageBody=json.dumps({
            'error': error_message,
            'event': event,
            'timestamp': str(datetime.utcnow())
        })
    )
python

3. Overlap Prevention#

Prevent overlapping executions with DynamoDB lock:

4. Monitoring and Logging#

Use CloudWatch Logs and Metrics:

5. Error Handling#

Handle errors gracefully:

Comparison: EventBridge Scheduler vs CloudWatch Events#

FeatureEventBridge SchedulerCloudWatch Events
Schedule typescron + rate + one-timecron + rate
Timezone support✅ Any timezone❌ UTC only
Flexible windows✅ Yes❌ No
Built-in retry✅ Yes❌ No (need DLQ)
Cost$1.00/M invocationsFree (first 5M/month)
Use caseProduction jobsSimple recurring jobs

Practical Example: Daily Database Cleanup#

Schedule with EventBridge Scheduler:

# Run daily at 2:00 AM UTC
cron(0 2 * * ? *)
bash

Troubleshooting#

Job not running#

  1. Check schedule expression: Ensure cron syntax is correct
  2. Verify IAM permissions: Lambda has necessary permissions
  3. Check CloudWatch Logs: Look for error messages
  4. Verify target ARN: Ensure Lambda function ARN is correct

Job runs but fails#

  1. Review CloudWatch Logs: Find error messages
  2. Check timeout: Lambda might be timing out
  3. Verify resource permissions: Database, S3, etc. access
  4. Test locally: Run Lambda locally to debug

Overlapping executions#

  1. Implement lock mechanism: Use DynamoDB or Redis
  2. Use reserved concurrency: Limit concurrent executions
  3. Add idempotency: Ensure handler is idempotent

Cleanup Resources#

When no longer needed, clean up resources:

# Delete schedule
aws scheduler delete-schedule --name daily-job

# Delete CloudWatch rule
aws events delete-rule --name daily-lambda-rule

# Delete Lambda function
aws lambda delete-function --function-name my-function

# Delete IAM role
aws iam delete-role --role-name scheduler-role
bash

Conclusion#

AWS Lambda combined with EventBridge Scheduler/CloudWatch Events provides a powerful solution for scheduled jobs:

  • EventBridge Scheduler: For production jobs with timezone support, retry, flexible windows
  • CloudWatch Events: For simple recurring jobs with cost efficiency

Key takeaways:

  1. Use EventBridge Scheduler for production
  2. Implement idempotency and error handling
  3. Setup monitoring with CloudWatch
  4. Use DLQ for failed invocations
  5. Prevent overlapping executions

References#