Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Embedded Postgres in Go

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Embedded Postgres in Go

Avatar for Oleg Kovalov

Oleg Kovalov

April 20, 2026

More Decks by Oleg Kovalov

Other Decks in Technology

Transcript

  1. - Open-source addicted gopher - Fan of linters (co-author go-critic)

    - 2 labradors 💛🖤 - Nearmap engineer Me 7
  2. - Open-source addicted gopher - Fan of linters (co-author go-critic)

    - 2 labradors 💛🖤 - Nearmap engineer - ... olegk.dev Me 8
  3. - Definitely not at Nearmap - which is freaking good

    - we’re hiring btw - One of my previous employers Intro 15
  4. - Definitely not at Nearmap - which is freaking good

    - we’re hiring btw - One of my previous employers - thankfully I’m not here anymore Intro 16
  5. - Definitely not at Nearmap - which is freaking good

    - we’re hiring btw - One of my previous employers - thankfully I’m not here anymore - Never over complicate your infrastructure Intro 17
  6. - Definitely not at Nearmap - which is freaking good

    - we’re hiring btw - One of my previous employers - thankfully I’m not here anymore - Never over complicate your infrastructure - especially CI Intro 18
  7. - Definitely not at Nearmap - which is freaking good

    - we’re hiring btw - One of my previous employers - thankfully I’m not here anymore - Never over complicate your infrastructure - especially CI - Okay, let’s see how deep is rabbit hole Intro 19
  8. - Definitely not at Nearmap - which is freaking good

    - we’re hiring btw - One of my previous employers - thankfully I’m not here anymore - Never over complicate your infrastructure - especially CI - Okay, let’s see how deep is rabbit hole - or 💩hole Intro 20
  9. - Go - Postgres - Monorepo - Bazel - Engineers

    with AI spice with unlimited tokens Problem statement 26
  10. Not bad, not terrible 29 DB (Postgres) Prod Canary DB

    (Postgres) Service (Go) Service (Go)
  11. Not bad, not terrible 30 DB (Postgres) Prod Canary CI

    Local DB (Postgres) DB (Postgres) Service (Go) Service (Go) DB (Postgres) Service (Go) Service (Go)
  12. Not bad, not terrible 31 DB (Postgres) Prod Canary CI

    Local DB (Postgres) DB (Postgres) DB (Postgres) Service (Go) Service (Go) Service (Go) Service (Go) BORING BORING BORING BORING BORING
  13. Software engineering 33 DB (Postgres) Prod Canary CI Local DB

    (Postgres) DB (Postgres) DB (Postgres) Service (Go) Service (Go) Service (Go) Service (Go)
  14. Software engineering 34 DB (Postgres) Prod Canary CI Local DB

    (SQLite) DB (Postgres) DB (Postgres) Service (Go) Service (Go) Service (Go) Service (Go) SQLite
  15. Software engineering, for real 35 DB (Postgres) Prod Canary CI

    Local DB (SQLite) DB (Postgres) DB (SQLite) Service (Go) Service (Go) Service (Go) Service (Go) SQLite SQLite
  16. Software engineering happened 36 DB (Postgres) Prod Canary CI Local

    DB (SQLite) DB (Postgres) DB (SQLite) Service (Go) Service (Go) Service (Go) Service (Go) SQLite SQLite
  17. - Use local Postgres - easy but might have side-effects

    - Use remote Postgres for CI or local Common sense 41
  18. - Use local Postgres - easy but might have side-effects

    - Use remote Postgres for CI or local - more complicated but more powerful Common sense 42
  19. - Use local Postgres - easy but might have side-effects

    - Use remote Postgres for CI or local - more complicated but more powerful - but even more side-effects Common sense 43
  20. - Use local Postgres - easy but might have side-effects

    - Use remote Postgres for CI or local - more complicated but more powerful - but even more side-effects - Use Docker Common sense 44
  21. - Use local Postgres - easy but might have side-effects

    - Use remote Postgres for CI or local - more complicated but more powerful - but even more side-effects - Use Docker - a bit of routine but for 2025 is not a problem Common sense 45
  22. - Use local Postgres - easy but might have side-effects

    - Use remote Postgres for CI or local - more complicated but more powerful - but even more side-effects - Use Docker - a bit of routine but for 2025 is not a problem - no idea why not use it tbh Common sense 46
  23. - Use local Postgres - easy but might have side-effects

    - Use remote Postgres for CI or local - more complicated but more powerful - but even more side-effects - Use Docker - a bit of routine but for 2025 is not a problem - no idea why not use it tbh - WASM Common sense 47
  24. - Use local Postgres - easy but might have side-effects

    - Use remote Postgres for CI or local - more complicated but more powerful - but even more side-effects - Use Docker - a bit of routine but for 2025 is not a problem - no idea why not use it tbh - WASM - modern-ish, limited, but interesting Common sense 48
  25. - Use local Postgres - easy but might have side-effects

    - Use remote Postgres for CI or local - more complicated but more powerful - but even more side-effects - Use Docker - a bit of routine but for 2025 is not a problem - no idea why not use it tbh - WASM - modern-ish, limited, but interesting - Use…embedded Postgres Common sense 49
  26. - Use local Postgres - easy but might have side-effects

    - Use remote Postgres for CI or local - more complicated but more powerful - but even more side-effects - Use Docker - a bit of routine but for 2025 is not a problem - no idea why not use it tbh - WASM - modern-ish, limited, but interesting - Use…embedded Postgres - wat? Common sense 50
  27. - Instead of shipping a Docker just ship a Postgres

    binary - pre-Docker era, isn’t? Embedded Postgres 52
  28. - Instead of shipping a Docker just ship a Postgres

    binary - pre-Docker era, isn’t? - Nothing revolutionary - and no extremism as well Embedded Postgres 53
  29. - Instead of shipping a Docker just ship a Postgres

    binary - pre-Docker era, isn’t? - Nothing revolutionary - and no extremism as well - Postgres is stable - best software ever? Embedded Postgres 54
  30. - Instead of shipping a Docker just ship a Postgres

    binary - pre-Docker era, isn’t? - Nothing revolutionary - and no extremism as well - Postgres is stable - best software ever? - Dozens of solutions already - JS: https://www.npmjs.com/package/embedded-postgres - Rust: https://github.com/theseus-rs/postgresql-embedded - Java: https://github.com/zonkyio/embedded-postgres - Python: https://github.com/orm011/pgserver - Go: https://github.com/fergusstrange/embedded-postgres - … Embedded Postgres 55
  31. - Download - Unpack - Start - … - PROFIT

    It’s still a Postgres. How it works? 62
  32. - Download - Unpack - Start - … - PROFIT

    It’s still a Postgres. How it works? 63 LIGHTWEIGHT BABY
  33. Example 65 import embeddedpostgres "github.com/fergusstrange/embedded-postgres" logger := &bytes.Buffer{} postgres :=

    embeddedpostgres.NewDatabase(embeddedpostgres.DefaultConfig(). Username("beer"). Password("wine"). Database("gin"). Version(V12). RuntimePath("/tmp"). BinaryRepositoryURL("https://repo.local/central.proxy"). Port(9876). StartTimeout(45 * time.Second). StartParameters(map[string]string{"max_connections": "200"}). Logger(logger)) err := postgres.Start() // do magic err := postgres.Stop()
  34. Example 66 import embeddedpostgres "github.com/fergusstrange/embedded-postgres" logger := &bytes.Buffer{} postgres :=

    embeddedpostgres.NewDatabase(embeddedpostgres.DefaultConfig(). Username("beer"). Password("wine"). Database("gin"). Version(V12). RuntimePath("/tmp"). BinaryRepositoryURL("https://repo.local/central.proxy"). Port(9876). StartTimeout(45 * time.Second). StartParameters(map[string]string{"max_connections": "200"}). Logger(logger)) err := postgres.Start() // do magic err := postgres.Stop()
  35. - Package is based on Java implementation - https://github.com/zonkyio/embedded-postgres -

    kudos to them - That’s why it fetches Postgres wrapped in a JAR But… 72
  36. - Package is based on Java implementation - https://github.com/zonkyio/embedded-postgres -

    kudos to them - That’s why it fetches Postgres wrapped in a JAR - wut? But… 73
  37. - Package is based on Java implementation - https://github.com/zonkyio/embedded-postgres -

    kudos to them - That’s why it fetches Postgres wrapped in a JAR - wut? - Which requires additional xz But… 74
  38. - Package is based on Java implementation - https://github.com/zonkyio/embedded-postgres -

    kudos to them - That’s why it fetches Postgres wrapped in a JAR - wut? - Which requires additional xz - XZ Utils backdoor, anyone? But… 75
  39. - Package is based on Java implementation - https://github.com/zonkyio/embedded-postgres -

    kudos to them - That’s why it fetches Postgres wrapped in a JAR - wut? - Which requires additional xz - XZ Utils backdoor, anyone? - Builder pattern, ☕ But… 76
  40. - Package is based on Java implementation - https://github.com/zonkyio/embedded-postgres -

    kudos to them - That’s why it fetches Postgres wrapped in a JAR - wut? - Which requires additional xz - XZ Utils backdoor, anyone? - Builder pattern, ☕ - personal taste But… 77
  41. - Package is based on Java implementation - https://github.com/zonkyio/embedded-postgres -

    kudos to them - That’s why it fetches Postgres wrapped in a JAR - wut? - Which requires additional xz - XZ Utils backdoor, anyone? - Builder pattern, ☕ - personal taste - … But… 78
  42. - Of course the answer is “Yes” - Simpler API

    - Less unpacking work Can we do better? 82
  43. - Of course the answer is “Yes” - Simpler API

    - Less unpacking work - Do we need Internet at all? Can we do better? 83
  44. - Of course the answer is “Yes” - Simpler API

    - Less unpacking work - Do we need Internet at all? - Lovely NIH syndrome :) Can we do better? 84
  45. import "github.com/cristaloleg/embedpg" import "github.com/cristaloleg/embedpg/embedpg18" cfg := embedpg.Config{ Package: embedpg18.Package, Port:

    54545, RuntimePath: ".data", DataPath: ".data/pgdata", Username: "postgres", Password: "postgres", Database: "postgres", } instance := embedpg.New(cfg) err := instance.Start() // ... err := instance.Stop() Example 86
  46. import "github.com/cristaloleg/embedpg" import "github.com/cristaloleg/embedpg/embedpg18" cfg := embedpg.Config{ Package: embedpg18.Package, Port:

    54545, RuntimePath: ".data", DataPath: ".data/pgdata", Username: "postgres", Password: "postgres", Database: "postgres", } instance := embedpg.New(cfg) err := instance.Start() // ... err := instance.Stop() Example 87
  47. import "github.com/cristaloleg/embedpg" import "github.com/cristaloleg/embedpg/embedpg18" cfg := embedpg.Config{ Package: embedpg18.Package, Port:

    54545, RuntimePath: ".data", DataPath: ".data/pgdata", Username: "postgres", Password: "postgres", Database: "postgres", } instance := embedpg.New(cfg) err := instance.Start() // ... err := instance.Stop() Example 88
  48. import "github.com/cristaloleg/embedpg" import "github.com/cristaloleg/embedpg/embedpg18" cfg := embedpg.Config{ Package: embedpg18.Package, Port:

    54545, RuntimePath: ".data", DataPath: ".data/pgdata", Username: "postgres", Password: "postgres", Database: "postgres", } instance := embedpg.New(cfg) err := instance.Start() // ... err := instance.Stop() Example package embedpg18 import _ "embed" import "github.com/cristaloleg/embedpg" //go:embed postgresql-18.3.0-blablabla.tar.gz var blob []byte var Package = embedpg.Package{ Version: "18.3.0", Blob: blob, } 89
  49. - Something new is something old - Don’t download what

    is constant - Ah, don’t do crazy 💩 with your infra Conclusions 94