Connect
This page is the Postgres wire only. S3, Bolt, Qdrant, Pinecone, GraphQL and gRPC endpoints are on Connect & wire adapters.
Connection string
dodil data connect <bucket> -o psql # prints the URL below with your key filled in
dodil data connect <bucket> -o env # export DATA_PG_URL=…postgresql://<user>:<password>@pg.uk-lon-1.dodil.io:5432/<bucket>?sslmode=requireSend the bare bucket name as the database. The gateway maps it to your organisation’s storage; never prefix it yourself. Postgres schemas do not exist: the bucket is the only namespace, a leading public. qualifier is stripped, and CREATE SCHEMA public / SET search_path are accepted with no effect.
The server identifies itself as PostgreSQL 16.0 (Dodil Tables adapterd), so drivers that gate features on the server version take the Postgres 16 path.
TLS
The door listens on a raw host port and terminates TLS with public Let’s Encrypt certificates.
| Client | Recommended | Why |
|---|---|---|
| Go (pgx), Node (node-postgres), JVM (pgJDBC) | sslmode=verify-full | They use the runtime’s system trust store — zero setup |
| libpq family (psql, psycopg, SQLAlchemy) | sslmode=verify-full sslrootcert=system | libpq only reads ~/.postgresql/root.crt unless told otherwise; sslrootcert=system needs libpq 16+ (older: point at the OS bundle, e.g. /etc/ssl/cert.pem) |
| Anything, minimum | sslmode=require | Encrypts but does not authenticate the server |
Never use prefer: it falls back to plaintext silently, and on this wire your password is your credential. Do not pin the certificate; it rotates about every 60 days.
Credentials
| Credential | user | password |
|---|---|---|
| API key (issued on the bucket’s API Keys page) | the dk_… key id | the key secret (shown once at issue time) |
| Service account (CI, headless) | client_id | client_secret |
Bearer JWT (dodil auth login) | any string | the JWT |
Dispatch is by shape: a JWT-shaped password is a token, a dk_ id is an API key, anything else is treated as a service account. A rejected credential is FATAL 28P01. One credential works on every wire; see Auth & Access.
Sessions, pooling and timeouts
A session holds an open transaction, prepared statements, cursors and a few settings — nothing else. That makes the wire friendly to connection poolers (PgBouncer in session or transaction mode, an ORM’s built-in pool):
- Prepared statements work (
$1…$n, typed from the OIDs the driver declares;col = ANY($1)with an array bind is the filter-by-list idiom). - Transactions are real:
BEGIN/COMMIT/ROLLBACK,SAVEPOINT,RELEASE,ROLLBACK TO. See Transactions & concurrency. COPY <table> [(cols)] FROM STDIN [WITH (FORMAT text|csv)]streams bulk rows;COPY TOis refused.- Server-side cursors —
DECLARE … CURSOR FOR,FETCH,CLOSE; forward-only. statement_timeoutis honoured:SET statement_timeout = 5000orTO '5s'; expiry is57014, pg’s own shape.LISTEN/UNLISTENare accepted with no effect — there is no notification channel. OtherSETparameters (TimeZone,search_path, isolation level) are accepted with no effect too;TimeZoneraises a notice because timestamps are always UTC.
SHOW answers the fixed set drivers probe at connect: server_version, client_encoding (UTF8), standard_conforming_strings (on), DateStyle (ISO, MDY), TimeZone (UTC), integer_datetimes (on), search_path, statement_timeout.
Drivers and ORMs
The ORM conformance suite runs real frameworks in four languages against the wire on every release.
| Driver / ORM | Status | Notes |
|---|---|---|
| psycopg 3 | Verified | text and binary parameters, COPY, server cursors |
| SQLAlchemy 2 (psycopg 3) | Verified | create_all, Core and ORM Session, optimistic versioning, RETURNING |
| Django ORM | Verified | connect-time set_config('TimeZone', …) answered; migrations run |
| Alembic | Verified | upgrade/downgrade; a dropped never-stored constraint name is a no-op |
| GORM (pgx driver) | Verified | AutoMigrate, CRUD, RETURNING |
| SQLx (Rust) | Verified | compile-time checked queries, native JSONB decode |
node-postgres (pg) | Verified | parameterised queries, arrays |
| Prisma | Verified | db push, migrate, model CRUD; introspection answers the describer battery |
| pgx direct (Go), pgJDBC, asyncpg, Npgsql | Expected | standard protocol paths; not exercised by the suite |
Three things every ORM should know: FOREIGN KEY is enforced on insert (23503) but not on the parent’s delete, and the catalog does not report FK constraints back, so a migration diff sees them missing; ON DELETE CASCADE and DEFERRABLE options are refused at DDL (see Constraints); and a column declared INTEGER is stored as 64-bit and introspects as BIGINT (see Data types). The framework-by-framework checklist is on Migrating from Postgres.
From the CLI
dodil data pg -b <bucket> "SELECT count(*) FROM orders" # this SQL over the Postgres wire
dodil data sql -b <bucket> "SELECT count(*) FROM orders" # the same SQL over the gRPC doorOmit the SQL to open a REPL. See CLI.