coollabsio/coolify · error · RuntimeException

The selected S3 storage no longer exists. S3 backup has been

Error message

The selected S3 storage no longer exists. S3 backup has been disabled.

What it means

uploadToS3() loads the S3 storage referenced by the volume backup's s3_storage_id. When the relation resolves to null (the S3 storage row was deleted after the schedule was created), the job flips save_s3 off on the backup, clears s3_storage_id, and throws so the execution is marked failed instead of silently skipping the upload.

Source

Thrown at app/Jobs/VolumeBackupJob.php:310

        try {
            VolumeBackupRecoveryJob::recover($execution);

            return '';
        } catch (Throwable $exception) {
            VolumeBackupRecoveryJob::dispatch($execution);

            return ' Container recovery failed: '.$exception->getMessage();
        }
    }

    private function uploadToS3(string $backupLocation, string $backupDirectory, Server $server): void
    {
        $s3 = $this->backup->s3;

        if (! $s3) {
            $this->backup->update(['save_s3' => false, 's3_storage_id' => null]);

            throw new \RuntimeException('The selected S3 storage no longer exists. S3 backup has been disabled.');
        }

        $s3->testConnection(shouldSave: true);
        $containerName = 'volume-upload-'.$this->execution->uuid;
        $image = coolifyHelperImage().':'.getHelperVersion();
        $resolveOptions = collect(SafeWebhookUrl::minioClientResolveOptions($s3->endpoint, $s3->trustedInternalHosts()))
            ->map(fn (string $option): string => '--resolve '.escapeshellarg($option))
            ->implode(' ');
        $resolveOptions = $resolveOptions === '' ? '' : ' '.$resolveOptions;

        try {
            instant_remote_process([
                'docker rm -f '.escapeshellarg($containerName).' >/dev/null 2>&1 || true',
                'docker run -d --name '.escapeshellarg($containerName).' --rm -v '
                    .escapeshellarg($backupLocation.':'.$backupLocation.':ro').' '.escapeshellarg($image),
                'docker exec '.escapeshellarg($containerName).' mc alias set'.$resolveOptions.' temporary '
                    .escapeshellarg($s3->endpoint).' '.escapeshellarg($s3->key).' '.escapeshellarg($s3->secret),
                'docker exec '.escapeshellarg($containerName).' mc cp '.escapeshellarg($backupLocation).' '

View on GitHub (pinned to 70b9acc424)

Solutions

  1. Edit the volume backup schedule, select a valid S3 storage, and re-enable 'Save to S3' - the job auto-disabled both when it threw
  2. Re-create the S3 storage if it was removed by mistake, then relink the schedule
  3. Before deleting an S3 storage in the future, reassign or remove schedules that point at it

Example fix

// before - detected only at backup time
$s3 = $this->backup->s3;
if (! $s3) {
    throw new \RuntimeException('The selected S3 storage no longer exists. S3 backup has been disabled.');
}
// after - reject stale references when the schedule is saved
$validated = $request->validate([
    's3_storage_id' => ['nullable', Rule::exists('s3_storages', 'id')],
]);
Defensive patterns

Strategy: validation

Validate before calling

use Illuminate\Validation\Rule;

// when saving the scheduled volume backup
$data = $request->validate([
    's3_storage_id' => ['nullable', Rule::exists('s3_storages', 'id')],
]);

Type guard

function backupHasValidS3($backup): bool
{
    return $backup->s3_storage_id === null || $backup->s3()->exists();
}

Prevention

When it happens

Trigger: A scheduled volume backup has save_s3 = true and an s3_storage_id whose S3 storage row no longer exists; on the next run `$this->backup->s3` is null.

Common situations: Deleting an S3 storage definition in Settings > S3 Storage without reassigning or deleting the scheduled backups that reference it.

Related errors


AI-assisted analysis of coollabsio/coolify@70b9acc424 (2026-08-17). Data as JSON: /api/errors/da413fa129b9fc1a. Report an issue: GitHub.