diff --git a/app/Console/Commands/Cloud/RestoreDatabase.php b/app/Console/Commands/Cloud/RestoreDatabase.php index 7c6c0d4c66..d2d5e9b246 100644 --- a/app/Console/Commands/Cloud/RestoreDatabase.php +++ b/app/Console/Commands/Cloud/RestoreDatabase.php @@ -144,7 +144,7 @@ class RestoreDatabase extends Command // Use pg_restore for custom format dumps $command = sprintf( - 'PGPASSWORD=%s pg_restore -h %s -p %s -U %s -d %s -v %s', + 'PGPASSWORD=%s pg_restore --no-owner --no-acl -h %s -p %s -U %s -d %s -v %s', escapeshellarg($password), escapeshellarg($host), escapeshellarg($port), diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index 20264d61b1..2e6d71f18b 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -698,7 +698,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue // Validate and escape database name to prevent command injection validateShellSafePath($database, 'database name'); $escapedDatabase = escapeshellarg($database); - $backupCommand .= " {$escapedContainerName} pg_dump --format=custom --no-acl --no-owner --username $escapedUsername $escapedDatabase > {$escapedBackupLocation}"; + // Keep owners and privileges in the archive: services such as Supabase need them, + // and the restore skips them unless "Keep owners and privileges" is selected. + $backupCommand .= " {$escapedContainerName} pg_dump --format=custom --username $escapedUsername $escapedDatabase > {$escapedBackupLocation}"; } $commands[] = $backupCommand; diff --git a/tests/Feature/BackupRunSafetyTest.php b/tests/Feature/BackupRunSafetyTest.php index 5a19a72a45..7d7464ca24 100644 --- a/tests/Feature/BackupRunSafetyTest.php +++ b/tests/Feature/BackupRunSafetyTest.php @@ -303,6 +303,30 @@ it('backs up a service PostgreSQL database as the user from PGUSER or POSTGRESQL 'POSTGRES_USER wins' => ["POSTGRES_USER=app\nPGUSER=pguser", 'app'], ]); +it('keeps owners and privileges in a single PostgreSQL database backup', function () { + $service = Service::factory()->create([ + 'server_id' => $this->server->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + 'environment_id' => $this->environment->id, + ]); + $database = ServiceDatabase::create([ + 'service_id' => $service->id, + 'name' => 'supabase-db', + 'image' => 'supabase/postgres:15.14.1.163', + 'status' => 'running:healthy', + ]); + $backup = backupRunSafetySchedule($database, $this->team); + $commands = backupRunSafetyFakeRemoteCommands(['| grep -E' => "POSTGRES_USER=postgres\nPOSTGRES_DB=postgres"]); + + (new DatabaseBackupJob($backup))->handle(); + + expect($commands->first(fn (string $command) => str_contains($command, 'pg_dump'))) + ->toContain('pg_dump --format=custom --username') + ->not->toContain('--no-acl') + ->not->toContain('--no-owner'); +}); + function backupRunSafetyServiceDatabase(Server $server, StandaloneDocker $destination, Environment $environment, string $image): ServiceDatabase { $service = Service::factory()->create([