Don't Let Your Staging Server Die: Separate Task Scheduling in Laravel

Published: (November 29, 2025 at 05:39 PM EST)
2 min read
Source: Dev.to

Source: Dev.to

The Problem

If you’re building real apps, you need a staging environment. I run a simple one on a Digital Ocean droplet (2 vCPUs) for a Laravel 10 app. One day the server froze; htop showed both CPU cores at 100 % and memory nearly full:

1[||||||||||||||||||100.0%]   
2[||||||||||||||||||100.0%]     
Mem[||||||||||||||| 3.2G/4.0G]

Both CPUs were pegged and memory was hanging at 3.2 GB of 4 GB.

Why It Matters

A quick ps aux and log scan pointed to the scheduler. Heavy scheduled commands that make sense in production—price exports, supplier syncs with thousands of products, huge dataset crunching—were killing the smaller staging server. Staging doesn’t need those jobs running as often, if at all.

The Solution

Separate your schedule() method based on environment. In Laravel 10 you can edit app/Console/Kernel.php (Laravel 11+ moves this to routes/console.php).

final class Kernel extends ConsoleKernel
{
    protected function schedule(Schedule $schedule): void
    {
        $this->scheduleCommon($schedule);

        if ($this->app->environment(AppEnvironmentEnum::PRODUCTION->value)) {
            $this->scheduleProduction($schedule);
        }

        if ($this->app->environment(AppEnvironmentEnum::STAGING->value)) {
            // Less frequent or reduced workload for staging
            $this->scheduleStaging($schedule);
        }
    }
}

How to Structure It

Common Schedule

Tasks that should run everywhere: cleanup, pruning, basic maintenance.

private function scheduleCommon(Schedule $schedule): void
{
    // Keep DB lean everywhere
    $schedule->command(PruneCommand::class, ['--model' => [CartItem::class]])->hourly();
    $schedule->command(PruneCommand::class, ['--model' => [OrderItemNotification::class]])->daily();

    // Clear expired tokens
    $schedule->command(PruneExpired::class)->dailyAt('03:00');

    // Basic notifications
    $schedule->command(TelegramNotificationSendCommand::class)->hourly()->withoutOverlapping();
    $schedule->command(UserSendConfirmationNotificationCommand::class)->hourly();
}

Production Schedule

Heavy operations that need frequent execution and have proper resources.

private function scheduleProduction(Schedule $schedule): void
{
    // Search index sync
    $schedule->command(SearchIndexSyncDiff::class)->dailyAt('05:00');

    // Auto price exports - multiple times per day
    $schedule->command(PriceExportAutoDispatchCommand::class)
             ->cron('15 6,8,10,12,14,16,18 * * *');

    // Supplier sync - runs 7 times per day
    $schedule->command(TmSyncPriceExportProduct::class)
             ->cron('1 6,8,10,12,14,16,18 * * *');

    // Product availability updates
    $schedule->command(ProductAvailabilityUpdateCommand::class)
             ->hourlyAt(15)
             ->between('08:00', '19:00');
}

Staging Schedule

Same commands, but less frequent—only what you actually need for testing.

private function scheduleStaging(Schedule $schedule): void
{
    // Price exports - once per day is enough
    $schedule->command(PriceExportAutoDispatchCommand::class)
             ->cron('15 6 * * *');

    // Supplier sync - once per day
    $schedule->command(TmSyncPriceExportProduct::class)
             ->cron('1 6 * * *');

    // Test suspended orders more frequently for debugging
    $schedule->command(OrderProcessSuspendedCommand::class)
             ->everyMinute()
             ->between('08:00', '19:00')
             ->withoutOverlapping();
}

The Results

After splitting the workloads, the staging server’s CPU dropped to 15‑20 % and memory usage stabilized around 1.5 GB.

EnvironmentCPU UsageMemory Usage
Staging (before)100 %3.2 GB / 4 GB
Staging (after)15‑20 %1.5 GB / 4 GB

Production continued operating at full capacity without impact.

Conclusions

Separate task schedules by environment from day one. Let staging run only essential maintenance routines and throttle heavy jobs. Copying production schedules blindly to staging quickly leads to resource exhaustion. Adjust frequencies to match your server resources, and your infrastructure will stay healthy.

Back to Blog

Related posts

Read more »