serverpod_embedded_postgres 3.5.0-beta.9 copy "serverpod_embedded_postgres: ^3.5.0-beta.9" to clipboard
serverpod_embedded_postgres: ^3.5.0-beta.9 copied to clipboard

Runs a real PostgreSQL server as a child process for Serverpod local dev. No Docker, no port conflicts, UDS by default.

serverpod_embedded_postgres #

Run a real PostgreSQL server as a child process for Serverpod local development. Same PG dialect as production, no Docker dependency, no TCP port conflicts by default. One Dart call boots the cluster, persistent across restarts.

Quickstart #

import 'dart:io';

import 'package:postgres/postgres.dart';
import 'package:serverpod_embedded_postgres/serverpod_embedded_postgres.dart';

Future<void> main() async {
  final pg = await EmbeddedPostgres.start(
    EmbeddedPostgresOptions(
      dataDir: Directory('.serverpod/pgdata'),
      databaseName: 'projectname',
    ),
  );

  final conn = await Connection.open(pg.endpoint);
  final rs = await conn.execute('SELECT 1');
  print(rs.first.first); // 1

  await conn.close();
  await pg.stop();
}

The first call downloads ~30 MB of Zonky's embedded-postgres-binaries from Maven Central into the per-user cache (~/Library/Caches/serverpod/pg-binaries on macOS, $XDG_CACHE_HOME/serverpod/pg-binaries on Linux, %LOCALAPPDATA%\serverpod\Cache\pg-binaries on Windows). Subsequent starts reuse the cache and reach ready in under a second on a warm cluster.

Two transports #

Unix Domain Socket (default). Trust authentication; the project directory already gates filesystem access to the socket. PG chdirs to PGDATA before binding so unix_socket_directories = '../run' lands a ~20-byte path in sockaddr_un.sun_path, well under the 104-byte macOS cap regardless of how deep your project lives.

TCP loopback (TcpTransport). scram-sha-256 against 127.0.0.1, random 32-char password persisted to <.serverpod>/postgres.password for warm-restart consistency. TcpTransport(port: 0) (the default) gets an ephemeral port; explicit ports are honored. Port-race collision is retried up to 3 times before bubbling up.

// TCP variant:
final pg = await EmbeddedPostgres.start(
  EmbeddedPostgresOptions(
    dataDir: Directory('.serverpod/pgdata'),
    databaseName: 'projectname',
    transport: const TcpTransport(),
  ),
);

print(pg.endpoint.port);     // some ephemeral port like 49152
print(pg.endpoint.password); // random 32-char string

Detach + attach for cross-VM dev DBs #

Set detach: true to keep the postmaster alive after your Dart VM exits, then reconnect from a fresh process:

// First VM (e.g. `serverpod start --watch`):
await EmbeddedPostgres.start(
  EmbeddedPostgresOptions(
    dataDir: Directory('.serverpod/pgdata'),
    databaseName: 'projectname',
    detach: true,
  ),
);

// Second VM (e.g. `dart test`):
final pg = await EmbeddedPostgres.attach(Directory('.serverpod/pgdata'));
final conn = await Connection.open(pg.endpoint);
// ...

attach() reads the pidfile and embedded_postgres_state.json left by the original start(), verifies the recorded PID is still our postmaster (cmdline + cwd, NOT just PID - the OS recycles those), and hands back a fully usable handle. Stale pidfiles (process gone) are cleaned up; PID-recycled foreign processes are left strictly alone.

Prefetch for CI #

Pre-populate the cache without booting a postmaster:

dart run serverpod_embedded_postgres:prefetch
dart run serverpod_embedded_postgres:prefetch --version 16.13.0
dart run serverpod_embedded_postgres:prefetch --target linux-amd64

--target lets a CI runner warm the cache for non-host platforms, e.g. a macOS shared-cache job pre-extracting the linux-amd64 bundle for test workers to consume.

What's exposed #

abstract class EmbeddedPostgres {
  // Boot or reattach.
  static Future<EmbeddedPostgres> start(EmbeddedPostgresOptions opts);
  static Future<EmbeddedPostgres> attach(Directory dataDir);

  // Cache utilities.
  static Future<void> prefetch(Version version, {String? target});
  static Directory defaultBinaryCache();

  // Connection handles.
  pg.Endpoint get endpoint;        // package:postgres consumers
  String get connectionString;     // libpq URI for psql, pg_dump, etc.
  Uri get connectionUri;

  // Lifecycle.
  Version get version;
  int? get pid;
  bool get isRunning;
  Future<void> stop({Duration timeout = const Duration(seconds: 10)});
  Future<void> reset();            // wipe data dir, ready for fresh init
}

The Transport sealed class has two variants:

sealed class Transport { const Transport(); }
final class UnixTransport extends Transport { const UnixTransport(); }
final class TcpTransport extends Transport {
  final int port;          // 0 = ephemeral
  final String? password;  // null = generate random
  const TcpTransport({this.port = 0, this.password});
}

Errors are a sealed hierarchy rooted at EmbeddedPostgresException: BinaryFetchException, BinaryVerificationException, UnsupportedPlatformException, InitdbException, StartupTimeoutException, CrashedException (carries logTail), AttachException, StaleClusterException. switch over them exhaustively.

What's not included #

  • PostgreSQL extensions beyond what Zonky's stock binaries ship. Notably, the default Serverpod project template uses pgvector/pgvector:pg16, so this package isn't yet a drop-in for newly-created projects until pgvector artifacts ship - tracked separately for a follow-up release.
  • Replication / logical decoding / hot standby - out of scope for a dev-loop tool.
  • pg_dump / pgAdmin wrappers - use pg_dump directly against connectionString.
  • Encryption at rest - dev tool.
  • Connection pooling - delegated to package:postgres.

Design #

See docs/design/serverpod_embedded_postgres_spec.md for the full design, including the rationale for UDS-by-default, binary-source choice, supervisor lifecycle, error model, and verification plan.

Platform support #

See PLATFORMS.md for which (OS, arch) tuples have been verified end-to-end and where Windows currently degrades.

0
likes
150
points
0
downloads

Documentation

API reference

Publisher

verified publisherserverpod.dev

Weekly Downloads

Runs a real PostgreSQL server as a child process for Serverpod local dev. No Docker, no port conflicts, UDS by default.

Homepage
Repository (GitHub)
View/report issues
Contributing

License

BSD-3-Clause (license)

Dependencies

archive, config, crypto, http, meta, path, postgres, pub_semver, serverpod_shared

More

Packages that depend on serverpod_embedded_postgres