public inbox for [email protected]
help / color / mirror / Atom feedFrom: [email protected]
To: [email protected]
Subject: pg ctl start spawns visible cmd.exe console window on Windows — CreateProcessAsUser missing CREATE NO WINDOW
Date: Wed, 24 Jun 2026 06:15:40 +0000
Message-ID: <18bbefccd530e218.a6bdbcf84d30dc08.260dd5df653405e8@NBK11-64-T5J> (raw)
Dear PostgreSQL developers,
We discovered this bug while debugging a cascading failure across three
layers of software that ultimately traced back to pg_ctl.exe on Windows.
## The discovery chain
We use a tool called pg0 (vectorize-io/pg0) which bundles PostgreSQL via
the theseus-rs/postgresql-embedded Rust crate. On every PostgreSQL start,
a cmd.exe console window flashed briefly. We traced the process tree:
parent (CREATE_NO_WINDOW) → pg0.exe → postgresql_embedded → pg_ctl start
→ cmd.exe /C "postgres.exe -D ... < nul >> start.log 2>&1" ← console window!
→ postgres.exe
The theseus-rs crate already applies CREATE_NO_WINDOW (0x08000000) to its
CreateProcess call when spawning pg_ctl.exe, so the parent→pg_ctl edge
was clean. The flash came from pg_ctl.exe itself spawning cmd.exe.
## Root cause
File: src/bin/pg_ctl/pg_ctl.c
Lines 510-511 explain the design choice:
"As with the Unix case, it's easiest to use the shell (CMD.EXE) to
handle redirection etc."
The command is constructed at lines 557-561:
cmd = psprintf("\"%s\" /C \"\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1\"",
comspec, exec_path, pgdata_opt, post_opts,
DEVNULL, log_file);
It is launched via CreateRestrictedProcess() at line 1846:
r = CreateProcessAsUser(restrictedToken, NULL, cmd, NULL, NULL, TRUE,
CREATE_SUSPENDED, // <-- no CREATE_NO_WINDOW
NULL, NULL, &si, processInfo);
Since cmd.exe is a CUI binary (subsystem 3), Windows allocates a new
console for it on every CreateProcess call. The parent's own
CREATE_NO_WINDOW flag does not propagate to grandchildren.
## Proposed fix
Option A (minimal, one flag):
Add CREATE_NO_WINDOW (0x08000000) to dwCreationFlags:
r = CreateProcessAsUser(restrictedToken, NULL, cmd, NULL, NULL, TRUE,
CREATE_SUSPENDED | CREATE_NO_WINDOW,
NULL, NULL, &si, processInfo);
Option B (eliminate cmd.exe dependency):
Use native CreateProcess handle inheritance (hStdInput/hStdOutput/hStdError
in STARTUPINFO) for I/O redirection, removing the need for cmd.exe /C
entirely. This would also eliminate the dependency on COMSPEC being set.
## Impact
Any application that launches PostgreSQL via pg_ctl from a headless or
background context sees an unexpected console flash on every start.
This affects embedded databases, CI/CD pipelines, IDE-integrated
PostgreSQL instances, and tools using libraries like pg0,
theseus-rs/postgresql-embedded, and zonkyio/embedded-postgres.
## System details
- OS: Windows 11 (22H2+, ConPTY enabled)
- PostgreSQL version tested: 18 (bundled via pg0 v0.14.2)
- The code path in pg_ctl.c is substantially unchanged across versions
We are happy to test a patch if one is drafted.
Thank you for your work on this excellent project.
Best regards,
Yuebei Wang
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected]
Subject: Re: pg ctl start spawns visible cmd.exe console window on Windows — CreateProcessAsUser missing CREATE NO WINDOW
In-Reply-To: <18bbefccd530e218.a6bdbcf84d30dc08.260dd5df653405e8@NBK11-64-T5J>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox