MysticMind.PostgresEmbed
5.0.0
See the version list below for details.
dotnet add package MysticMind.PostgresEmbed --version 5.0.0
NuGet\Install-Package MysticMind.PostgresEmbed -Version 5.0.0
<PackageReference Include="MysticMind.PostgresEmbed" Version="5.0.0" />
<PackageVersion Include="MysticMind.PostgresEmbed" Version="5.0.0" />
<PackageReference Include="MysticMind.PostgresEmbed" />
paket add MysticMind.PostgresEmbed --version 5.0.0
#r "nuget: MysticMind.PostgresEmbed, 5.0.0"
#:package MysticMind.PostgresEmbed@5.0.0
#addin nuget:?package=MysticMind.PostgresEmbed&version=5.0.0
#tool nuget:?package=MysticMind.PostgresEmbed&version=5.0.0
MysticMind.PostgresEmbed Postgres embedded database equivalent for .Net applications

Run a real PostgreSQL server, extensions included, from .NET code and tests on Windows, Linux (glibc and Alpine/musl) and macOS, on both x64 and arm64. No Docker and no local Postgres install required.
By default, this library uses the minimal binaries published by zonkyio/embedded-postgres-binaries. They are much smaller than the official downloads. A list of all available Postgres versions is here: https://mvnrepository.com/artifact/io.zonky.test.postgres/embedded-postgres-binaries-bom. Click a version to see the platforms it is published for.
The library detects the OS and CPU architecture and downloads the matching binaries.
If you have benefitted from this library and it has saved you a bunch of time, please feel free to sponsor my work!<br> <a href="https://github.com/sponsors/mysticmind" target="_blank"><img height="30" style="border:0px;height:36px;" src="https://img.shields.io/static/v1?label=GitHub Sponsor&message=%E2%9D%A4&logo=GitHub" border="0" alt="GitHub Sponsor" /></a>
Requirements
- .NET 8 or later (the package targets .NET 8, 9, 10 and 11)
- Windows x64, Linux x64/arm64 (glibc or musl), macOS x64/arm64
- Postgres refuses to run as root/Administrator, so run your tests as a regular user (this matters in Docker containers)
Usage
Install the package from NuGet:
dotnet add package MysticMind.PostgresEmbed
Start a server
await using var server = new PgServer("17.6.0");
await server.StartAsync();
// trust authentication is used, so no password is needed
await using var conn = new NpgsqlConnection(server.ConnectionString);
await conn.OpenAsync();
await using var cmd = new NpgsqlCommand("CREATE TABLE table1(id int PRIMARY KEY, title text)", conn);
await cmd.ExecuteNonQueryAsync();
Disposing the server stops it. You can also call StopAsync() yourself.
Options
Everything else is configured with PgServerOptions:
await using var server = new PgServer(new PgServerOptions
{
PgVersion = "17.6.0",
Port = 5432, // default: a free port, see server.PgPort
DbDir = "/tmp/my-tests", // default: the current directory
BinariesDir = "/tmp/pg-cache", // share downloads across projects (see below)
ServerParams = new Dictionary<string, string>
{
["max_connections"] = "300",
["timezone"] = "UTC",
["synchronous_commit"] = "off"
},
StartupTimeout = TimeSpan.FromSeconds(60),
Logger = loggerFactory.CreateLogger("Postgres")
});
await server.StartAsync();
| Option | Default | Description |
|---|---|---|
PgVersion |
required | Postgres version, for example 17.6.0 |
PgUser |
postgres |
Superuser created by initdb |
Port |
free port | TCP port; read the actual one from server.PgPort |
DbDir |
current directory | A pg_embed folder is created under it |
BinariesDir |
<DbDir>/pg_embed/binaries |
Cache of downloads and extracted installs |
InstanceId |
new Guid | Pass a fixed Guid to reuse an instance and its data across runs |
ClearInstanceDirOnStop |
true for a new instance, false for a named one |
Delete the instance folder when the server stops |
ClearWorkingDirOnStart |
false |
Delete the pg_embed folder before starting |
ServerParams |
none | Postgres settings, written to a config file (values may contain spaces and quotes) |
Extensions |
none | Extension archives to install, see below |
Locale |
en_US.UTF-8 (system locale on Windows) |
Locale passed to initdb |
AddLocalUserAccessPermission |
false |
Windows only, see InitDb failure |
StartupTimeout / ShutdownTimeout |
30s / 10s | |
MavenRepo |
Maven Central | Use a mirror or an internal proxy |
HttpClient |
shared client | Configure proxies or custom handlers |
Logger |
none | Any Microsoft.Extensions.Logging logger |
Platform / Architecture |
detected | Override detection |
Extensions
The library downloads and extracts extension archives into the Postgres install. Then run CREATE EXTENSION yourself. Pick an extension build that matches the Postgres version and platform.
await using var server = new PgServer(new PgServerOptions
{
PgVersion = "17.6.0",
Extensions =
[
new PgExtensionConfig("https://download.osgeo.org/postgis/windows/pg17/postgis-bundle-pg17-3.6.2x64.zip")
]
});
await server.StartAsync();
await using var conn = new NpgsqlConnection(server.ConnectionString);
await conn.OpenAsync();
await using var cmd = new NpgsqlCommand("CREATE EXTENSION postgis", conn);
await cmd.ExecuteNonQueryAsync();
Using it in xUnit tests
Downloading and extracting binaries takes time on the first run, so share one server across the tests in a class (or a whole assembly) with a fixture:
public sealed class DatabaseFixture : IAsyncLifetime
{
public PgServer Server { get; } = new("17.6.0");
public async ValueTask InitializeAsync() => await Server.StartAsync();
public async ValueTask DisposeAsync() => await Server.DisposeAsync();
}
public class MyDbTests(DatabaseFixture db) : IClassFixture<DatabaseFixture>
{
[Fact]
public async Task your_test()
{
await using var conn = new NpgsqlConnection(db.Server.ConnectionString);
// ...
}
}
Good to know
postgresis the default database and the default superuser.- Trust authentication is used. Any password in the connection string is ignored. The server only listens on localhost, since the main use case is local tests.
- Each server gets an instance folder (named after
InstanceId) under<DbDir>/pg_embed, holding thedatafolder and the server log (server.LogFile). New instances are deleted when the server stops. - Pass a fixed
InstanceIdto keep the data and reuse it on the next run. The instance is set up on the first run and only started afterwards. - If the test process exits without disposing the server, the library makes a best effort to kill the server on process exit.
How it works
- The zonky binaries for the version and platform are downloaded from Maven once, and cached in
BinariesDir. Each download is retried 3 times. An unknown version fails fast with a clear error. - The binaries, plus the configured extensions, are extracted once into a shared install folder in
BinariesDir, for examplepostgres-17.6.0-linux-amd64. Every instance with the same version and extensions reuses it, so later servers start in about a second. initdbcreates the instancedatafolder.- The port and
ServerParamsare written topg_embed.conf, which is included frompostgresql.conf. pg_ctl start -wstarts the server and waits until it accepts connections.- On stop,
pg_ctl stopruns in fast mode, falling back to immediate mode and then to killing the process.
Breaking changes in v5
v5 modernizes the library. Summary of what changed for callers:
- Target frameworks: .NET 8, 9, 10 and 11. .NET 6 and 7 (out of support) are dropped.
- Async only:
Start(),Stop()andDispose()are removed. Useawait StartAsync(),await StopAsync()andawait using.PgServerimplementsIAsyncDisposableonly. - Options object: the long constructor is replaced by
new PgServer(pgVersion)andnew PgServer(PgServerOptions). The parameters map to option properties of the same name.startupWaitTime(ms) is nowStartupTimeout(TimeSpan),pgServerParamsisServerParams,pgExtensionsisExtensions, and thedeleteFolder*parameters areDeleteFolderRetryCountandDeleteFolderInitialDelay. - Instance cleanup: instances without an
InstanceIdare now deleted on stop by default. SetClearInstanceDirOnStop = falseto keep them. - Folder layout: binaries now live in a shared install folder under
BinariesDir, soPgBinDirpoints there instead of<instance>/bin. Existing named instances keep their data. - Removed:
PgExtensionConfig.CreateExtensionSqlList(it was never executed). RunCREATE EXTENSIONyourself. - Errors: failures are reported as
PostgresEmbedException, and startup errors include the end of the server log. - New:
ConnectionString,IsRunning,LogFile,InstallDir,CancellationTokensupport, logging, proxy support throughHttpClient, Linux arm64 and Alpine support, and support for paths with spaces and non-ASCII characters.
Known issues
InitDb failure while starting embedded server
initdb: could not change permissions of directory "./pg_embed/aa60c634-fa20-4fa8-b4fc-a43a3b08aa99/data": Permission denied
All Postgres processes run under the current account, and Postgres needs full access to the parent folder of the data directory. On Windows, set AddLocalUserAccessPermission = true. The library then runs the following before initdb:
icacls.exe <instance folder> /t /grant:r <user>:(OI)(CI)F
The account needs the right to change folder permissions. You usually only need this on CI agents.
Non-ASCII paths on Windows
Postgres on Windows cannot use folder paths with characters outside the system code page, such as Chinese folder names. The library works around this by handing Postgres the 8.3 short form of the path, which is ASCII only. Short names are on by default for the system drive, but are often turned off on other drives.
Without them, startup stops before downloading anything, with an error that lists the problem characters and the two ways out:
- Set
DbDir(orBinariesDir, whichever the error names) to a folder whose full path is ASCII only, such asC:\pg-embed. - Turn on short names for that drive as an administrator (
fsutil 8dot3name set D: 0), then delete and recreate the folder. Folders created while short names were off never get one.
InitDb failure with a large negative number
If initdb fails on Windows with a large negative exit code, install the latest Microsoft Visual C++ Redistributable.
Acknowledgements
- This project uses the minimal Postgres binaries published by zonkyio/embedded-postgres-binaries.
- Yandex Embedded PostgreSQL and OpenTable Embedded PostgreSQL Component were useful references while designing the implementation.
License
MysticMind.PostgresEmbed is licensed under the MIT License. Refer to the License file for more information.
Copyright © 2023 Babu Annamalai
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. net11.0 is compatible. |
-
net10.0
- CliWrap (>= 3.10.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.14)
- Polly.Core (>= 8.9.0)
- SharpCompress (>= 0.50.8)
-
net11.0
- CliWrap (>= 3.10.5)
- Polly.Core (>= 8.9.0)
- SharpCompress (>= 0.50.8)
-
net8.0
- CliWrap (>= 3.10.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.14)
- Polly.Core (>= 8.9.0)
- SharpCompress (>= 0.50.8)
-
net9.0
- CliWrap (>= 3.10.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.14)
- Polly.Core (>= 8.9.0)
- SharpCompress (>= 0.50.8)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on MysticMind.PostgresEmbed:
| Package | Downloads |
|---|---|
|
Aero.Cms.Db.Marten
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 5.2.1 | 74 | 11/6/2026 |
| 5.2.0 | 41 | 11/6/2026 |
| 5.1.0 | 41 | 11/6/2026 |
| 5.0.0 | 47 | 11/6/2026 |
| 4.0.0 | 650,465 | 12/14/2023 |
| 3.1.0 | 21,202 | 8/30/2023 |
| 3.0.1 | 546 | 8/13/2023 |
| 3.0.0 | 7,783 | 8/12/2023 |
| 2.1.0 | 5,408 | 2/24/2023 |
| 2.0.0 | 7,666 | 8/30/2022 |
| 1.5.0 | 54,419 | 8/31/2020 |
| 1.4.0 | 25,014 | 9/1/2019 |
| 1.3.0 | 5,698 | 3/15/2019 |
| 1.2.0 | 1,306 | 2/20/2019 |
| 1.1.3 | 3,182 | 12/20/2018 |
| 1.1.2 | 1,339 | 10/8/2018 |
| 1.1.1 | 1,263 | 9/10/2018 |
| 1.1.0 | 2,490 | 8/29/2018 |
| 1.0.0 | 2,291 | 6/28/2018 |
| 0.1.20 | 3,932 | 2/17/2017 |