fix(backup): keep privileges in PostgreSQL database backups

pg_dump ran with --no-acl --no-owner, so single-database backups lost
all GRANTs and a restored Supabase database did not work. The import
already skips owners and privileges unless "Keep owners and privileges"
is selected, so the backup now keeps them.

Closes #12244

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-10-23 22:35:45 +02:00
co-authored by Claude Opus 5.5
parent 1e71561239
commit 19afd36084
3 changed files with 28 additions and 2 deletions
@@ -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),
+3 -1
View File
@@ -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;
+24
View File
@@ -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([