public inbox for [email protected]  
help / color / mirror / Atom feed
From: [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