JuliusBrussee/caveman · error
postgres: production DATABASE_URL must be a Postgres URL
Error message
postgres: production DATABASE_URL must be a Postgres URL
What it means
ParsePoolConfig refuses to build a pgxpool.Config from a DATABASE_URL that is not a parseable postgres:// or postgresql:// URL with a hostname when the process runs in production. The library enforces this because the production TLS posture (verify-full, pinned CA) is only expressible through a URL it can inspect; anything else would silently bypass the security checks.
Solutions
- Rewrite DATABASE_URL as postgres://user:pass@host:5432/dbname with a real hostname
- Confirm the deployment actually intends production (RUNTIME_ENV/GO_ENV) — if it is local dev, unset the production flag
- URL-encode the connection string (special chars in password break url.Parse)
- Add the sslmode=verify-full query parameter too, since this check is followed by the verify-full requirement
Example fix
// before DATABASE_URL="host=db.internal user=app dbname=prod sslmode=verify-full" // after DATABASE_URL="postgres://app:secret@db.internal:5432/prod?sslmode=verify-full"
Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(os.Getenv("DATABASE_URL"))
if err != nil || (u.Scheme != "postgres" && u.Scheme != "postgresql") || u.Hostname() == "" {
return fmt.Errorf("DATABASE_URL must be postgres://user:pass@host:5432/db")
} Type guard
func isPostgresURL(raw string) bool {
u, err := url.Parse(raw)
return err == nil && (u.Scheme == "postgres" || u.Scheme == "postgresql") && u.Hostname() != ""
} Try / catch
cfg, err := postgresconfig.ParsePoolConfig(databaseURL)
if err != nil {
if strings.Contains(err.Error(), "must be a Postgres URL") {
return nil, fmt.Errorf("DATABASE_URL malformed for production: %w", err)
}
return nil, err
} Prevention
- Always use postgres:// URLs, never keyword/value DSNs, in every environment
- Validate the connection string shape at config-load/startup time with a startup check
- URL-encode passwords containing special characters
- Keep local and production DATABASE_URL formats identical to catch drift early
When it happens
Trigger: Calling NewPool/ParsePoolConfig in production (runtimeenv.IsProduction() true) with a URL whose scheme is not postgres/postgresql (e.g. a bare DSN string like 'host=db user=app', or http://), a URL that fails url.Parse, or a URL without a hostname.
Common situations: Deploying with a keyword/value DSN copied from local dev, leaving DATABASE_URL set to a docker-compose placeholder, or a staging DATABASE_URL promoted to production unchanged.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- postgres: parse DATABASE_URL
- production requires CAVE_PUBLIC_URL to be an HTTPS origin…
- invalid CAVEMAN_LISTEN
- invalid listener
- must be an absolute http(s) URL
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/2e6c089b10abf178.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/postgresconfig/postgresconfig.go:34
"github.com/JuliusBrussee/caveman/shared/platform/runtimeenv"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
const (
caEnvironment = "CAVE_POSTGRES_CA_CERT"
caFileEnvironment = "CAVE_POSTGRES_CA_CERT_FILE"
)
// ParsePoolConfig validates the connection string and applies the managed
// database CA to every TLS path. Production rejects sslmode=require because it
// encrypts without authenticating the server; verify-full is mandatory.
func ParsePoolConfig(databaseURL string) (*pgxpool.Config, error) {
production := runtimeenv.IsProduction()
if production {
parsed, err := url.Parse(databaseURL)
if err != nil || (parsed.Scheme != "postgres" && parsed.Scheme != "postgresql") || parsed.Hostname() == "" {
return nil, errors.New("postgres: production DATABASE_URL must be a Postgres URL")
}
if parsed.Query().Get("sslmode") != "verify-full" {
return nil, errors.New("postgres: production DATABASE_URL requires sslmode=verify-full")
}
}
config, err := pgxpool.ParseConfig(databaseURL)
if err != nil {
return nil, fmt.Errorf("postgres: parse DATABASE_URL: %w", err)
}
caPEM, err := caPEMFromEnvironment()
if err != nil {
return nil, err
}
if production && caPEM == "" {
return nil, fmt.Errorf("postgres: %s or %s is required in production", caEnvironment, caFileEnvironment)
}
if caPEM == "" {View on GitHub (pinned to 3ee70a1026)