Help us shape the future of Go! Take the 2026 Go Developer Survey

pgdocker

package
v0.13.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 22, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package pgdocker starts a PostgreSQL server in a local Docker container and sets up the dbc connection to it — the connections menu's "Postgres in Docker…" in the TUI and in dbc web.

It is UI-free, like connedit: each UI asks Statuses to draw its version picker, Start to bring a container up, and Register to add (or refresh) the connection, so the two cannot disagree on a container's name, port, password or the connection it gets.

One container per major version, named and labelled so a second pick of the same version finds it again rather than starting another:

pick 18 ─► Start
            │ docker inspect dbc-pg18
            ├─ none ──► image there? ─ no ─► docker pull postgres:18
            │           free port (5518, else any) + random password
            │           docker run -d --name dbc-pg18 --label dbc.pgdocker=18
            │             -p 127.0.0.1:PORT:5432 -v dbc-pg18-data:… postgres:18
            ├─ stopped ► docker start dbc-pg18
            └─ running ► (nothing to do)
            │ port and password read back from the container
            └► wait until the server answers on 127.0.0.1:PORT
         ─► Register: connection "docker-pg18" added, or its DSN updated

The data lives in a named volume (dbc-pg18-data), so removing the container by hand keeps the databases, and a later pick reattaches them. The port is published on 127.0.0.1 only: the server is for this machine.

The password is generated once, at creation, and read back from the container's environment (POSTGRES_PASSWORD) on every later start, so the container itself is where it is kept; the connection's DSN carries a copy, as every saved connection's DSN does.

Docker is driven through its CLI rather than the Engine API: the CLI is what the user has installed and configured (contexts, colima, Docker Desktop's socket location), and it adds no dependency to dbc.

Index

Constants

View Source
const Label = "dbc.pgdocker"

Label marks the containers dbc started; its value is the major version.

Variables

View Source
var (
	ErrNoDocker   = errors.New("docker was not found — install Docker Desktop (or colima) to run Postgres in a container")
	ErrNotRunning = errors.New("docker is installed but not running — start Docker Desktop (or colima) and try again")
)

ErrNoDocker means no docker binary was found; ErrNotRunning that one was, but its daemon did not answer. Both are the user's to fix, and the UIs show their messages as they are.

View Source
var Versions = []Version{
	{"18", date(2030, 11, 14)},
	{"17", date(2029, 11, 8)},
	{"16", date(2028, 11, 9)},
	{"15", date(2027, 11, 11)},
	{"14", date(2026, 11, 12)},
}

Versions are the majors with an official postgres image, newest first. Supported drops each one at its end of life, so a version leaves the picker on its own once it is past it; a new major is added here when it is released (not its betas: a throwaway server is still one people expect to behave like production).

Functions

func ConnName

func ConnName(major string) string

func ContainerName

func ContainerName(major string) string

The names one major's container, volume and connection go by.

func DSN

func DSN(port int, password string) string

DSN is the connection string for a container's server: the image's superuser and default database, on loopback, without TLS (the image's server has no certificate). The password is hex (newPassword), so it needs no escaping in the URL.

func FindBin

func FindBin() (string, error)

FindBin finds the docker binary: on PATH, else in binDirs.

func Image

func Image(major string) string

func Known

func Known(major string) bool

Known reports whether major is one of Versions.

func MajorOf

func MajorOf(conn string) (major string, ok bool)

MajorOf is the PostgreSQL major of a connection Register made, from its name: "docker-pg18" and "docker-pg18-2" are 18's. ok is false for any other name, or a major dbc does not offer.

The name is the link, not the DSN's port: a menu asks for every row it draws, without a docker call, and a stop then checks the container itself (Docker.Stop), so a name that only looks like one costs a clear refusal.

func PreferredPort

func PreferredPort(major string) int

PreferredPort is the host port a new container asks for first: 5500 plus the major (5518 for 18). Off 5432, so a server already installed on the machine keeps its port, and one per major, so several versions run side by side; a taken one falls back to any free port (freePort).

func StopTarget

func StopTarget(ed *connedit.Editor, conn string) (major string, err error)

StopTarget is the major whose container stopping connection conn means: one Register made (MajorOf) and still a connection added in dbc — a config-file connection under such a name is the user's own, whatever it points at.

func VolumeName

func VolumeName(major string) string

Types

type Docker

type Docker struct {
	// Bin is the docker binary; "" finds it (FindBin).
	Bin string
	// Run runs docker with args and returns its stdout; nil runs Bin. Its
	// error carries docker's stderr, which says what went wrong.
	Run func(ctx context.Context, args ...string) (string, error)
	// Ping reports whether a server answers on dsn; Start polls it until it
	// does. Required: the caller passes its own (db.Probe), which keeps
	// this package free of the drivers.
	Ping func(ctx context.Context, dsn string) error
	// ReadyTimeout bounds the wait for the server to answer; 0 means 90 s
	// — a first start initialises the data directory, which on a slow disk
	// or an emulated architecture takes a while.
	ReadyTimeout time.Duration
	// Progress, when set, is told each slow step as it starts (a pull, the
	// wait for the server), for a UI to show something while it waits.
	Progress func(string)
}

Docker runs the docker CLI. The zero value finds the binary itself; tests set Run (and Ping) to stand in for Docker and the server.

func New

func New(progress func(string)) *Docker

New is a Docker that checks readiness the way the connection form's Test does — db.Probe: open, ping, close — so "ready" means dbc can connect, not merely that the port is open. progress may be nil.

func (*Docker) Check

func (d *Docker) Check(ctx context.Context) error

Check reports whether Docker can be used: a binary, and a daemon that answers. It is what a picker asks before offering to start anything.

func (*Docker) Start

func (d *Docker) Start(ctx context.Context, major string) (Result, error)

Start brings up dbc's container for major — created, started, or found running — and waits until its server answers. See the package comment for the steps.

A container under dbc's name that dbc did not label is refused rather than used: its password and port are not dbc's to guess, and starting someone's container is not what the user asked for.

func (*Docker) Statuses

func (d *Docker) Statuses(ctx context.Context) (map[string]Status, error)

Statuses is the state of dbc's container for each major that has one, by major. A version not in the map has no container.

func (*Docker) Stop

func (d *Docker) Stop(ctx context.Context, major string) (wasRunning bool, err error)

Stop stops dbc's container for major. wasRunning is false when it was already stopped, which is no error: the user asked for it to be stopped, and it is. A container that is gone, or under dbc's name but not dbc's, is an error that says so — the latter is never stopped.

type Registered

type Registered struct {
	Name     string
	Added    bool // a new connection; false: an existing one kept or updated
	Updated  bool // an existing one's DSN was replaced (its container was recreated)
	Warnings []string
}

Registered says what Register did with the connection.

func Register

func Register(ed *connedit.Editor, res Result, inUse func(string) error) (Registered, error)

Register makes the connection for a started server: ConnName(major) ("docker-pg18"), saved like any connection added in the form, so it survives restarts and is edited or removed the same way.

no connection by that name ─────────────► Add
one added in dbc, same DSN ─────────────► nothing to do (a second pick)
one added in dbc, other DSN ────────────► Edit its DSN: the container
                                          was recreated (new port and
                                          password); its ai_rows stay
one from the config file ───────────────► Add as docker-pg18-2, -3, …
                                          (the file's is the user's)

inUse is the UI's in-use rule (connedit.Editor.Edit): a DSN change under a session is refused with its words, and the user disconnects first.

type Result

type Result struct {
	Major     string
	Container string
	Port      int
	Password  string
	Created   bool // a new container; false: one already there was used
	Pulled    bool // the image was downloaded first
	DSN       string
}

Result is a started (or found running) server.

type Status

type Status struct {
	Major   string
	Exists  bool
	Running bool
	Port    int // 0 while stopped: Docker lists no ports for a stopped container
}

Status is what the picker shows beside a version: whether dbc's container for it exists, runs, and on which host port.

func (Status) Text

func (s Status) Text() string

Text is a status in a few words, for a picker row ("" when there is no container yet).

type Version

type Version struct {
	Major string
	EOL   time.Time
}

Version is a PostgreSQL major version the picker offers, with the date the PostgreSQL project stops supporting it (https://www.postgresql.org/support/versioning/).

func Supported

func Supported(now time.Time) []Version

Supported is Versions less those past their end of life at now. It is never empty: were every listed one past it (a dbc far older than its clock), the newest is still offered rather than a picker with nothing in it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL