Last Updated: July 23, 2025 This guide demonstrates how to implement time-of-day scaling for SaladCloud container groups using scheduled serverless functions. Unlike queue-based autoscaling that reacts to workload, time-of-day scaling proactively adjusts resources based on predictable usage patterns, such as business hours, peak traffic times, or scheduled batch processing windows.

Prerequisites

Before you begin, ensure you have:
  • SaladCloud API Key: You’ll need a valid API key with permissions to manage container groups
  • Organization and Project: An active organization and project in SaladCloud
  • Container Group: An existing container group that you want to scale on a schedule
  • Serverless Platform Access: Account on one of the supported platforms (AWS Lambda, Cloudflare Workers, etc.)
  • Time Zone Planning: Clear understanding of your scaling schedule and time zones
Cost Optimization: Time-of-day scaling is particularly effective for workloads with predictable patterns, allowing you to scale to zero during off-hours and scale up before peak demand, optimizing both cost and performance.

Overview

Time-of-day scaling works by scheduling functions to run at specific times when you want to change your replica count:
  1. Schedule functions to trigger at exact times when scaling is needed
  2. Execute scaling actions directly without checking current time
  3. Call the SaladCloud API to set the desired replica count
  4. Handle state transitions (starting/stopping container groups as needed)
This approach is ideal for:
  • Business hours scaling: Scale up during work hours, down during nights/weekends
  • Batch processing windows: Prepare resources before scheduled jobs
  • Global applications: Adjust capacity based on regional peak times
  • Cost optimization: Predictably scale to zero during known low-usage periods

Step 1: Plan Your Scaling Schedule

Before implementing, define your scaling schedule:

Example Scaling Patterns

Business Hours Pattern:
Mon-Fri 8:00 AM - 6:00 PM: 10 replicas
Mon-Fri 6:00 PM - 8:00 AM: 2 replicas
Weekends: 1 replica
Global Coverage Pattern:
00:00-06:00 UTC: 5 replicas  (APAC peak)
06:00-12:00 UTC: 2 replicas  (Low traffic)
12:00-18:00 UTC: 8 replicas  (EMEA peak)
18:00-24:00 UTC: 10 replicas (Americas peak)
Batch Processing Pattern:
Daily 2:00 AM: Scale to 20 replicas for overnight processing
Daily 6:00 AM: Scale to 5 replicas for maintenance
Daily 8:00 AM: Scale to 15 replicas for business hours
Daily 10:00 PM: Scale to 0 replicas (overnight shutdown)

Key Considerations

  • Time Zones: Use UTC in your functions to avoid daylight saving complications
  • Startup Time: Account for SaladCloud’s container startup time (5-15 minutes)
  • Overlap Periods: Plan transitions to handle workload handoffs smoothly
  • Emergency Scaling: Keep manual override capabilities for unexpected load

Step 2: Choose Your Implementation

Select one of the following serverless platforms for implementing your time-of-day scaling:

Option A: AWS Lambda + EventBridge

  • Best for: AWS-heavy environments, complex logic, integration with other AWS services
  • Scheduling: EventBridge with cron expressions and event payloads
  • Cost: Pay-per-invocation, very cost-effective for periodic scaling

Option B: Cloudflare Workers + Cron Triggers

  • Best for: Global distribution, simple logic, edge computing integration
  • Scheduling: Built-in cron triggers with environment-based configuration
  • Cost: Generous free tier, low latency execution

Option C: Google Cloud Functions + Cloud Scheduler

  • Best for: Google Cloud environments, integration with GCP services
  • Scheduling: Cloud Scheduler with flexible cron expressions
  • Cost: Pay-per-invocation with generous free tier

AWS Lambda Implementation

Here’s a complete AWS Lambda implementation for time-of-day scaling:

Lambda Function Code

import json
import urllib.request
import os
from typing import Dict, Any, Optional

# Configuration from environment variables
SALAD_API_KEY = os.environ['SALAD_API_KEY']
SALAD_ORG = os.environ['SALAD_ORG']
SALAD_PROJECT = os.environ['SALAD_PROJECT']
CONTAINER_GROUP_NAME = os.environ['CONTAINER_GROUP_NAME']

SALAD_BASE_URL = "https://api.salad.com/api/public"

def make_salad_request(method: str, path: str, data: Optional[Dict] = None) -> Dict[str, Any]:
    """Make a request to the SaladCloud API"""
    url = f"{SALAD_BASE_URL}{path}"

    headers = {
        'Content-Type': 'application/json',
        'Salad-Api-Key': SALAD_API_KEY
    }

    if method == 'PATCH':
        headers['Content-Type'] = 'application/merge-patch+json'

    req_data = None
    if data:
        req_data = json.dumps(data).encode('utf-8')

    request = urllib.request.Request(url, data=req_data, headers=headers, method=method)

    try:
        with urllib.request.urlopen(request) as response:
            return json.loads(response.read().decode('utf-8'))
    except urllib.error.HTTPError as e:
        error_body = e.read().decode('utf-8')
        raise Exception(f"SaladCloud API error {e.code}: {error_body}")

def get_container_group() -> Dict[str, Any]:
    """Get current container group status"""
    path = f"/organizations/{SALAD_ORG}/projects/{SALAD_PROJECT}/containers/{CONTAINER_GROUP_NAME}"
    return make_salad_request('GET', path)

def start_container_group():
    """Start the container group"""
    path = f"/organizations/{SALAD_ORG}/projects/{SALAD_PROJECT}/containers/{CONTAINER_GROUP_NAME}/start"
    return make_salad_request('POST', path)

def stop_container_group():
    """Stop the container group"""
    path = f"/organizations/{SALAD_ORG}/projects/{SALAD_PROJECT}/containers/{CONTAINER_GROUP_NAME}/stop"
    return make_salad_request('POST', path)

def set_replicas(replicas: int):
    """Set the number of replicas for the container group"""
    path = f"/organizations/{SALAD_ORG}/projects/{SALAD_PROJECT}/containers/{CONTAINER_GROUP_NAME}"
    return make_salad_request('PATCH', path, {'replicas': replicas})

def lambda_handler(event, context):
    """Main Lambda handler function"""
    try:
        # Get desired replicas from the event (passed by EventBridge rule)
        desired_replicas = event.get('replicas', 0)
        action = event.get('action', 'scale')  # 'scale', 'start', or 'stop'

        print(f"Scaling action: {action}, desired replicas: {desired_replicas}")

        # Get current container group status
        container_group = get_container_group()
        current_replicas = container_group['replicas']
        current_state = container_group['current_state']['status']

        print(f"Current replicas: {current_replicas}")
        print(f"Current state: {current_state}")

        # Apply scaling logic based on action
        if action == 'stop' or desired_replicas == 0:
            if current_state == 'running':
                print("Stopping container group...")
                stop_container_group()
            else:
                print("Container group already stopped")

        elif action == 'start' or desired_replicas > 0:
            if current_state == 'stopped':
                print("Starting container group...")
                start_container_group()

            # Set replicas if different from current
            if desired_replicas != current_replicas:
                print(f"Setting replicas to {desired_replicas}")
                set_replicas(desired_replicas)
            else:
                print("Replicas already at desired count")

        return {
            'statusCode': 200,
            'body': json.dumps({
                'message': 'Scaling operation completed successfully',
                'action': action,
                'desired_replicas': desired_replicas,
                'current_replicas': current_replicas,
                'current_state': current_state
            })
        }

    except Exception as e:
        print(f"Error during scaling operation: {str(e)}")
        return {
            'statusCode': 500,
            'body': json.dumps({
                'error': str(e),
                'message': 'Scaling operation failed'
            })
        }

Deployment Configuration

  1. Create the Lambda Function:
    • Runtime: Python 3.9 or later
    • Timeout: 30 seconds
    • Memory: 128 MB (sufficient for API calls)
  2. Set Environment Variables:
    SALAD_API_KEY=your_salad_api_key
    SALAD_ORG=your_organization_name
    SALAD_PROJECT=your_project_name
    CONTAINER_GROUP_NAME=your_container_group_name
    
  3. Create Multiple EventBridge Rules for Your Schedule: Business Hours Start (8 AM Monday-Friday):
    aws events put-rule \
      --name salad-scale-business-start \
      --schedule-expression "cron(0 8 ? * MON-FRI *)"
    
    aws events put-targets \
      --rule salad-scale-business-start \
      --targets "Id"="1","Arn"="arn:aws:lambda:region:account:function:function-name","Input"='{"action":"scale","replicas":15}'
    
    Business Hours End (6 PM Monday-Friday):
    aws events put-rule \
      --name salad-scale-business-end \
      --schedule-expression "cron(0 18 ? * MON-FRI *)"
    
    aws events put-targets \
      --rule salad-scale-business-end \
      --targets "Id"="1","Arn"="arn:aws:lambda:region:account:function:function-name","Input"='{"action":"scale","replicas":3}'
    
    Batch Processing Start (2 AM Daily):
    aws events put-rule \
      --name salad-scale-batch-start \
      --schedule-expression "cron(0 2 * * ? *)"
    
    aws events put-targets \
      --rule salad-scale-batch-start \
      --targets "Id"="1","Arn"="arn:aws:lambda:region:account:function:function-name","Input"='{"action":"scale","replicas":25}'
    
    Batch Processing End (6 AM Daily):
    aws events put-rule \
      --name salad-scale-batch-end \
      --schedule-expression "cron(0 6 * * ? *)"
    
    aws events put-targets \
      --rule salad-scale-batch-end \
      --targets "Id"="1","Arn"="arn:aws:lambda:region:account:function:function-name","Input"='{"action":"scale","replicas":15}'
    
    Weekend Scale Down (Saturday 12 AM):
    aws events put-rule \
      --name salad-scale-weekend \
      --schedule-expression "cron(0 0 ? * SAT *)"
    
    aws events put-targets \
      --rule salad-scale-weekend \
      --targets "Id"="1","Arn"="arn:aws:lambda:region:account:function:function-name","Input"='{"action":"scale","replicas":1}'
    
Alternative: Infrastructure as Code (CloudFormation/Terraform)
# CloudFormation template snippet
Resources:
  BusinessHoursStartRule:
    Type: AWS::Events::Rule
    Properties:
      ScheduleExpression: 'cron(0 8 ? * MON-FRI *)'
      Targets:
        - Arn: !GetAtt ScalingFunction.Arn
          Id: 'BusinessHoursStart'
          Input: '{"action":"scale","replicas":15}'

  BusinessHoursEndRule:
    Type: AWS::Events::Rule
    Properties:
      ScheduleExpression: 'cron(0 18 ? * MON-FRI *)'
      Targets:
        - Arn: !GetAtt ScalingFunction.Arn
          Id: 'BusinessHoursEnd'
          Input: '{"action":"scale","replicas":3}'

Testing Your Lambda Implementation

Unit Testing:
def test_scaling_events():
    """Test your scaling logic with different events"""
    test_events = [
        {'action': 'scale', 'replicas': 15},  # Business hours start
        {'action': 'scale', 'replicas': 3},   # Business hours end
        {'action': 'scale', 'replicas': 25},  # Batch processing start
        {'action': 'stop', 'replicas': 0},    # Overnight shutdown
    ]

    for event in test_events:
        print(f"Testing event: {event}")
        # Simulate the lambda_handler call
        result = lambda_handler(event, {})
        print(f"Result: {result['statusCode']}")
        print("---")

# Run tests
test_scaling_events()
Manual Testing:
# Test scaling to 15 replicas
aws lambda invoke \
  --function-name your-scaling-function \
  --payload '{"action":"scale","replicas":15}' \
  response.json

# Test stopping the container group
aws lambda invoke \
  --function-name your-scaling-function \
  --payload '{"action":"stop","replicas":0}' \
  response.json
Dry-Run Mode: Add a dry-run mode for testing without actual scaling:
DRY_RUN = os.environ.get('DRY_RUN', 'false').lower() == 'true'

def apply_scaling_action(action_type, *args):
    """Apply scaling action with dry-run support"""
    if DRY_RUN:
        print(f"DRY RUN: Would execute {action_type} with args: {args}")
        return

    if action_type == 'start':
        start_container_group()
    elif action_type == 'stop':
        stop_container_group()
    elif action_type == 'scale':
        set_replicas(args[0])

Cloudflare Workers Implementation

Cloudflare Workers provides a clean single-worker approach where all scaling configuration is defined in the wrangler.toml file through environment variables:

Worker Script (src/index.js)

export default {
  async scheduled(event, env, ctx) {
    try {
      const scalingConfig = {
        action: env.SCALING_ACTION,
        replicas: parseInt(env.SCALING_REPLICAS || '0'),
        name: env.SCALING_NAME || 'Unknown scaling action',
      }

      console.log(`Executing: ${scalingConfig.name}`)
      await executeScalingAction(env, scalingConfig)
    } catch (error) {
      console.error('Scaling operation failed:', error)
    }
  },
}

async function executeScalingAction(env, config) {
  const { action, replicas, name } = config

  const containerGroup = await getContainerGroup(env)
  const currentReplicas = containerGroup.replicas
  const currentState = containerGroup.current_state.status

  console.log(`Current: ${currentReplicas} replicas, state: ${currentState}`)

  if (action === 'stop' || replicas === 0) {
    if (currentState === 'running') {
      await stopContainerGroup(env)
      console.log(`${name}: Container group stopped`)
    }
  } else if (action === 'scale') {
    if (currentState === 'stopped') {
      await startContainerGroup(env)
    }

    if (replicas !== currentReplicas) {
      await setReplicas(env, replicas)
      console.log(`${name}: Scaled to ${replicas} replicas`)
    }
  }
}

async function getContainerGroup(env) {
  const url = `https://api.salad.com/api/public/organizations/${env.SALAD_ORG}/projects/${env.SALAD_PROJECT}/containers/${env.CONTAINER_GROUP_NAME}`
  const response = await fetch(url, {
    headers: { 'Salad-Api-Key': env.SALAD_API_KEY },
  })

  if (!response.ok) {
    throw new Error(`Failed to get container group: ${response.status} ${await response.text()}`)
  }

  return response.json()
}

async function startContainerGroup(env) {
  const url = `https://api.salad.com/api/public/organizations/${env.SALAD_ORG}/projects/${env.SALAD_PROJECT}/containers/${env.CONTAINER_GROUP_NAME}/start`
  const response = await fetch(url, {
    method: 'POST',
    headers: { 'Salad-Api-Key': env.SALAD_API_KEY },
  })

  if (!response.ok) {
    throw new Error(`Failed to start container group: ${response.status} ${await response.text()}`)
  }
}

async function stopContainerGroup(env) {
  const url = `https://api.salad.com/api/public/organizations/${env.SALAD_ORG}/projects/${env.SALAD_PROJECT}/containers/${env.CONTAINER_GROUP_NAME}/stop`
  const response = await fetch(url, {
    method: 'POST',
    headers: { 'Salad-Api-Key': env.SALAD_API_KEY },
  })

  if (!response.ok) {
    throw new Error(`Failed to stop container group: ${response.status} ${await response.text()}`)
  }
}

async function setReplicas(env, replicas) {
  const url = `https://api.salad.com/api/public/organizations/${env.SALAD_ORG}/projects/${env.SALAD_PROJECT}/containers/${env.CONTAINER_GROUP_NAME}`
  const response = await fetch(url, {
    method: 'PATCH',
    headers: {
      'Salad-Api-Key': env.SALAD_API_KEY,
      'Content-Type': 'application/merge-patch+json',
    },
    body: JSON.stringify({ replicas }),
  })

  if (!response.ok) {
    throw new Error(`Failed to set replicas: ${response.status} ${await response.text()}`)
  }
}

Wrangler Configuration

The key is to deploy the same worker multiple times with different names and environment variables. Create separate wrangler.toml files for each scaling action: Business Hours Start (wrangler-business-start.toml):
name = "salad-business-start"
main = "src/index.js"
compatibility_date = "2024-07-01"

[triggers]
crons = ["0 8 * * 1-5"]  # 8 AM Monday-Friday

[vars]
SALAD_ORG = "your-organization"
SALAD_PROJECT = "your-project"
CONTAINER_GROUP_NAME = "your-container-group"
SCALING_ACTION = "scale"
SCALING_REPLICAS = "15"
SCALING_NAME = "Business hours start"
Business Hours End (wrangler-business-end.toml):
name = "salad-business-end"
main = "src/index.js"
compatibility_date = "2024-07-01"

[triggers]
crons = ["0 18 * * 1-5"]  # 6 PM Monday-Friday

[vars]
SALAD_ORG = "your-organization"
SALAD_PROJECT = "your-project"
CONTAINER_GROUP_NAME = "your-container-group"
SCALING_ACTION = "scale"
SCALING_REPLICAS = "3"
SCALING_NAME = "Business hours end"
Batch Processing Start (wrangler-batch-start.toml):
name = "salad-batch-start"
main = "src/index.js"
compatibility_date = "2024-07-01"

[triggers]
crons = ["0 2 * * *"]  # 2 AM daily

[vars]
SALAD_ORG = "your-organization"
SALAD_PROJECT = "your-project"
CONTAINER_GROUP_NAME = "your-container-group"
SCALING_ACTION = "scale"
SCALING_REPLICAS = "25"
SCALING_NAME = "Batch processing start"
Overnight Shutdown (wrangler-shutdown.toml):
name = "salad-overnight-shutdown"
main = "src/index.js"
compatibility_date = "2024-07-01"

[triggers]
crons = ["0 22 * * *"]  # 10 PM daily

[vars]
SALAD_ORG = "your-organization"
SALAD_PROJECT = "your-project"
CONTAINER_GROUP_NAME = "your-container-group"
SCALING_ACTION = "stop"
SCALING_REPLICAS = "0"
SCALING_NAME = "Overnight shutdown"
Weekend Scale Down (wrangler-weekend.toml):
name = "salad-weekend-scale"
main = "src/index.js"
compatibility_date = "2024-07-01"

[triggers]
crons = ["0 0 * * 6"]  # Saturday midnight

[vars]
SALAD_ORG = "your-organization"
SALAD_PROJECT = "your-project"
CONTAINER_GROUP_NAME = "your-container-group"
SCALING_ACTION = "scale"
SCALING_REPLICAS = "1"
SCALING_NAME = "Weekend scale down"

Deployment Commands

Deploy each worker with its specific configuration:
# Install Wrangler CLI
npm install -g wrangler

# Login to Cloudflare
wrangler login

# Set your API key as a secret for each worker
wrangler secret put SALAD_API_KEY --config wrangler-business-start.toml
wrangler secret put SALAD_API_KEY --config wrangler-business-end.toml
wrangler secret put SALAD_API_KEY --config wrangler-batch-start.toml
wrangler secret put SALAD_API_KEY --config wrangler-shutdown.toml
wrangler secret put SALAD_API_KEY --config wrangler-weekend.toml

# Deploy all workers
wrangler deploy --config wrangler-business-start.toml
wrangler deploy --config wrangler-business-end.toml
wrangler deploy --config wrangler-batch-start.toml
wrangler deploy --config wrangler-shutdown.toml
wrangler deploy --config wrangler-weekend.toml

Simplified Deployment Script

Create a deploy.sh script to automate the process:
#!/bin/bash
set -e

echo "Setting up SaladCloud time-of-day scaling workers..."

# Array of worker configurations
workers=("business-start" "business-end" "batch-start" "shutdown" "weekend")

# Set API key for all workers
for worker in "${workers[@]}"; do
    echo "Setting API key for $worker..."
    wrangler secret put SALAD_API_KEY --config "wrangler-$worker.toml"
done

# Deploy all workers
for worker in "${workers[@]}"; do
    echo "Deploying $worker..."
    wrangler deploy --config "wrangler-$worker.toml"
done

echo "All workers deployed successfully!"
echo "Your time-of-day scaling is now active."
Make it executable and run:
chmod +x deploy.sh
./deploy.sh

Testing Your Cloudflare Workers Implementation

Manual Trigger Testing:
# Trigger a specific worker manually (bypasses cron schedule)
wrangler triggers deploy --config wrangler-business-start.toml

# View worker logs in real-time
wrangler tail salad-business-start

# View logs for a specific worker
wrangler tail salad-business-end --format pretty
Testing with Dry-Run Mode: Add a DRY_RUN environment variable to your wrangler.toml for testing:
# Add to any wrangler-*.toml file for testing
[vars]
SALAD_ORG = "your-organization"
SALAD_PROJECT = "your-project"
CONTAINER_GROUP_NAME = "your-container-group"
SCALING_ACTION = "scale"
SCALING_REPLICAS = "15"
SCALING_NAME = "Business hours start"
DRY_RUN = "true"  # Add this for testing
Then update your worker code to support dry-run:
async function executeScalingAction(env, config) {
  const { action, replicas, name } = config
  const isDryRun = env.DRY_RUN === 'true'

  if (isDryRun) {
    console.log(`DRY RUN: ${name} - Would ${action} to ${replicas} replicas`)
    return
  }

  // ... rest of your scaling logic
}
Testing Individual Workers:
# Test each worker configuration
wrangler deploy --config wrangler-business-start.toml --dry-run
wrangler deploy --config wrangler-business-end.toml --dry-run
wrangler deploy --config wrangler-batch-start.toml --dry-run

# Deploy in test mode first
wrangler deploy --config wrangler-business-start.toml --env staging

Step 3: Best Practices

Scheduling Considerations

  1. Account for Startup Time: SaladCloud containers can take 5-15 minutes to start
    • Schedule scale-up 15-30 minutes before you need the capacity
    • Use multiple scaling events rather than trying to predict exact timing
  2. Minimize Unnecessary Executions:
    • Only schedule functions when you need to change replica counts
    • Each cron trigger should have a specific scaling purpose
    • Avoid overlapping schedules that might conflict
  3. Handle Time Zones Properly:
    • Use UTC in your cron expressions to avoid daylight saving issues
    • Convert business hours to UTC when setting up schedules
    • Document your schedule clearly for future maintenance

Error Handling and Reliability

import time
from functools import wraps

def retry_on_failure(max_retries=3, delay=5):
    """Decorator to retry failed operations"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_retries - 1:
                        raise
                    print(f"Attempt {attempt + 1} failed: {e}. Retrying in {delay}s...")
                    time.sleep(delay)
            return None
        return wrapper
    return decorator

@retry_on_failure(max_retries=3, delay=5)
def reliable_set_replicas(replicas):
    """Set replicas with automatic retry on failure"""
    return set_replicas(replicas)

Cost Optimization

  1. Scale to Zero: Use 0 replicas during guaranteed low-usage periods
  2. Gradual Scaling: Implement stepped scaling instead of jumping to max replicas
  3. Weekend Patterns: Reduce capacity during weekends unless needed
def calculate_stepped_replicas(base_replicas, current_replicas):
    """Implement gradual scaling to avoid sudden cost spikes"""
    max_step = 5  # Maximum replicas to add/remove at once

    if base_replicas > current_replicas:
        return min(base_replicas, current_replicas + max_step)
    elif base_replicas < current_replicas:
        return max(base_replicas, current_replicas - max_step)

    return current_replicas

Troubleshooting

Common Issues

  1. Function Not Triggering:
    • Verify cron expressions are correct
    • Check function permissions and environment variables
    • Review platform-specific logs
  2. API Authentication Errors:
    • Ensure API key is correctly set as environment variable
    • Verify API key has necessary permissions
    • Check for trailing spaces in environment variables
  3. Incorrect Scaling:
    • Test your time calculation logic with various dates
    • Verify timezone handling (use UTC consistently)
    • Check for off-by-one errors in hour comparisons
  4. Container Group Not Responding:
    • Allow 5-15 minutes for scaling operations to complete
    • Check container group status in SaladCloud portal
    • Verify container group name matches exactly

Debugging Tools

def debug_scaling_decision(current_time, desired_replicas):
    """Print detailed debugging information"""
    print(f"Debug Info:")
    print(f"  Current UTC time: {current_time}")
    print(f"  Weekday: {current_time.weekday()} (Monday=0)")
    print(f"  Hour: {current_time.hour}")
    print(f"  Desired replicas: {desired_replicas}")

    # Test each schedule rule
    for name, schedule in SCALING_SCHEDULE.items():
        weekday = current_time.weekday()
        hour = current_time.hour

        if weekday in schedule['days']:
            start = schedule['start_hour']
            end = schedule['end_hour']

            if start > end:  # Overnight
                matches = hour >= start or hour < end
            else:
                matches = start <= hour < end

            print(f"  Rule '{name}': {'MATCHES' if matches else 'no match'}")
            print(f"    Days: {schedule['days']}, Hours: {start}-{end}, Replicas: {schedule['replicas']}")

Integration with Existing Autoscaling

You can combine time-of-day scaling with queue-based autoscaling:
def hybrid_scaling_logic(current_time, queue_length=None):
    """Combine time-based and queue-based scaling"""
    # Get base replicas from time-of-day schedule
    base_replicas = calculate_desired_replicas(current_time)

    # If queue data is available, adjust based on demand
    if queue_length is not None:
        # Scale up if queue is growing
        if queue_length > 10:
            queue_replicas = min(queue_length // 2, 20)  # 2 jobs per replica, max 20
            return max(base_replicas, queue_replicas)

    return base_replicas

Next Steps

  • 📊 Monitor Performance: Set up dashboards to track scaling effectiveness
  • 🔧 Optimize Schedule: Adjust timing based on actual usage patterns
  • 🚨 Add Alerting: Implement notifications for scaling failures
  • 📈 Cost Analysis: Track cost savings from optimized scaling
  • 🔄 Backup Strategy: Consider hybrid queue-based scaling for unexpected load