From 994eee83777c1ac7399c187e605d52ad7c99ed6a Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Sun, 19 Jul 2026 22:35:33 +0200 Subject: [PATCH] fix(backups): retain volume backups without server --- app/Jobs/ScheduledJobManager.php | 15 +++++++++++++-- tests/Feature/VolumeBackupTest.php | 28 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/app/Jobs/ScheduledJobManager.php b/app/Jobs/ScheduledJobManager.php index 01b122a8d..6e2fab14a 100644 --- a/app/Jobs/ScheduledJobManager.php +++ b/app/Jobs/ScheduledJobManager.php @@ -2,6 +2,7 @@ namespace App\Jobs; +use App\Actions\Shared\DeleteScheduledVolumeBackup; use App\Models\ScheduledDatabaseBackup; use App\Models\ScheduledTask; use App\Models\ScheduledVolumeBackup; @@ -397,8 +398,8 @@ private function processScheduledVolumeBackup(ScheduledVolumeBackup $backup): vo return; } - if (! $backup->backupable || ! $server) { - $backup->delete(); + if (! $backup->backupable) { + DeleteScheduledVolumeBackup::run($backup, $server); $this->skippedCount++; $this->logSkip('volume_backup', 'resource_deleted', [ 'backup_id' => $backup->id, @@ -408,6 +409,16 @@ private function processScheduledVolumeBackup(ScheduledVolumeBackup $backup): vo return; } + if (! $server) { + $this->skippedCount++; + $this->logSkip('volume_backup', 'server_missing', [ + 'backup_id' => $backup->id, + 'team_id' => $backup->team_id, + ]); + + return; + } + if (! $server->isFunctional()) { $this->skippedCount++; $this->logSkip('volume_backup', 'server_not_functional', [ diff --git a/tests/Feature/VolumeBackupTest.php b/tests/Feature/VolumeBackupTest.php index e00841110..256dbb5e5 100644 --- a/tests/Feature/VolumeBackupTest.php +++ b/tests/Feature/VolumeBackupTest.php @@ -2010,6 +2010,34 @@ function signInForVolumeBackups($testCase, Team $team): User ); }); +it('retains scheduled volume backups and archive metadata when the server is missing', function () { + config(['constants.coolify.self_hosted' => true]); + Queue::fake(); + $team = Team::factory()->create(); + [$application, $volume] = createVolumeBackupApplication($team); + $backup = $volume->scheduledBackups()->create([ + 'team_id' => $team->id, + 'frequency' => '* * * * *', + 'enabled' => true, + ]); + $execution = ScheduledVolumeBackupExecution::create([ + 'scheduled_volume_backup_id' => $backup->id, + 'status' => 'success', + 'filename' => '/data/coolify/backups/volumes/test/archive.tar.gz', + ]); + $application->update([ + 'destination_id' => null, + 'destination_type' => null, + ]); + + (new ScheduledJobManager)->handle(); + + expect($backup->fresh())->not->toBeNull() + ->and($execution->fresh())->not->toBeNull() + ->and($execution->fresh()->filename)->toBe('/data/coolify/backups/volumes/test/archive.tar.gz'); + Queue::assertNotPushed(VolumeBackupJob::class); +}); + it('dispatches pending recovery without starting another volume backup', function () { config(['constants.coolify.self_hosted' => true]); Queue::fake();