Files
wehub-resource-sync 3e779be6f3
CI / test (3.14, ubuntu-24.04-arm) (push) Waiting to run
CI / lint (push) Failing after 13m4s
CI / test (3.11, ubuntu-latest) (push) Failing after 2m4s
CI / test (3.13, ubuntu-latest) (push) Successful in 13m30s
CI / test (3.14, ubuntu-latest) (push) Successful in 17m21s
CI / test (3.12, ubuntu-latest) (push) Successful in 17m55s
CI / discover-apps-ps (push) Successful in 1m56s
CI / test (3.9, ubuntu-latest) (push) Successful in 13m17s
CI / test (3.10, ubuntu-latest) (push) Successful in 26m21s
CI / audit (push) Successful in 13m38s
Deploy site / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:37 +08:00

28 KiB
Raw Permalink Blame History

Usage

English | 한국어

CLI, GUI, configuration, and health checks. Everything you need after install.

Launch an app

winpodx app run word              # Launch Word
winpodx app run word ~/doc.docx   # Open a file
winpodx app run desktop           # Full Windows desktop

Or just click an app icon in your application menu — WinPodX registers every discovered Windows app as a .desktop entry the first time the pod boots.

CLI reference

# Apps
winpodx app list                  # List available apps
winpodx app run word              # Launch Word (auto-provisions on first run)
winpodx app run word ~/doc.docx   # Open a file in Word
winpodx app run desktop           # Full Windows desktop session
winpodx app install-all           # Register all apps in desktop menu
winpodx app sessions              # Show active sessions
winpodx app kill word             # Kill an active session
winpodx app refresh               # Re-scan the guest and rebuild the app list

# Pod lifecycle (container state only — see `guest` for in-guest ops, `install` for disk / install)
winpodx pod start --wait          # Start and wait for RDP readiness
winpodx pod stop                  # Stop (warns about active sessions)
winpodx pod status                # Status with session count
winpodx pod restart
winpodx pod recreate              # Stop + remove + start (clean container)
winpodx pod wait-ready --logs     # Wait for Windows first-boot with progress + container logs (auto-extends on slow ISO download)

# Guest-side operations (renamed from `pod <x>` in 0.6.0 — old spellings still work through 0.6.x with a deprecation notice)
winpodx guest apply-fixes         # Re-apply Windows-side runtime fixes (idempotent)
winpodx guest sync                # Push host updates (agent / urlacl / rdprrap / fixes) into the guest — no reinstall
winpodx guest sync --force        # Re-sync even when the guest version stamp already matches
winpodx guest sync-password       # Recover from password drift (cfg ↔ Windows)
winpodx guest multi-session on    # Toggle bundled rdprrap multi-session RDP
winpodx guest multi-session status
winpodx guest recover-oem         # Print noVNC PowerShell steps to download + run install.bat manually when dockur's first-boot OEM copy failed (#287)

# Install / disk operations (renamed from `pod install-* / pod grow-disk / pod disk-usage` in 0.6.0)
winpodx install status            # Install progress / pending steps (#271 agent-first installs)
winpodx install resume            # Resume a deferred install step
winpodx install disk-usage        # Show Windows C: size / free / used% + auto-grow status (#318)
winpodx install grow-disk         # Add the auto-grow increment (default 32G) to the disk + extend C: (#318)
winpodx install grow-disk 128G    # Grow to an absolute size
winpodx install grow-disk --extend-only   # Just extend C: into existing unallocated space

# Power management
winpodx power --suspend           # Pause container (free CPU, keep memory)
winpodx power --resume            # Resume paused container

# Host device passthrough (USB / PCI → Windows guest, #286)
winpodx device list               # List host USB / PCI devices + their attach state
winpodx device attach <id>        # Attach a host device to the guest (USB hot-plugs live; PCI is boot-added)
winpodx device detach <id>        # Detach a device from the guest
winpodx device attach <id> --force   # Skip the guest-restart safety confirmation for a PCI device

# Security
winpodx rotate-password           # Rotate Windows RDP password (host config + Windows-side guest account)

# Reverse-open (host listener / guest sync)
winpodx host-open status          # Show listener daemon + manifest state
winpodx host-open list            # List discovered host apps (live or --cached)
winpodx host-open refresh         # Rescan host + push manifest to guest
winpodx host-open enable          # Turn reverse-open on
winpodx host-open disable         # Turn reverse-open off
winpodx host-open add <slug>      # Add app to allowlist
winpodx host-open remove <slug>   # Remove from allowlist (or --deny)
winpodx host-open start-listener
winpodx host-open stop-listener
winpodx host-open daemon-status

# Maintenance
winpodx cleanup                   # Remove Office lock files (~$*.*)
winpodx timesync                  # Force Windows time synchronization
winpodx debloat                   # Disable telemetry, ads, bloat
winpodx uninstall                 # Remove winpodx files (keeps container)
winpodx uninstall --purge         # Remove everything including config

# System
winpodx setup                     # Full setup: config + container + wait-ready + discovery + reverse-open
winpodx setup --customize         # Wizard: backend / specs / edition / language / region / keyboard / timezone / tuning
winpodx setup-host                # Host prep wizard (kvm group, /etc/subuid, kvm module) via one pkexec prompt — AppImage users
winpodx provision                 # Post-pod-running chain (wait-ready → apply-fixes → discovery → reverse-open) — the single source of truth used by install.sh, setup, migrate, and the GUI bring-up (0.6.0 item B)
winpodx provision --retries N     # Override discovery retry count (default 2 — see 0.6.0 item M)
winpodx provision --require-agent # Hard-gate on the in-guest agent (used by fresh installs, #271)
winpodx migrate                   # Upgrade an existing guest in place (refresh agent.ps1 + scripts, re-apply fixes, re-discover, refresh reverse-open)
winpodx doctor                    # Read-only health diagnostic with per-check fix hints (deps / pod / RDP / agent / disk / config / install state)
winpodx doctor --json             # Same checks, machine-readable JSON array of findings
winpodx doctor --quick            # Skip slow probes (container-health, guest exec) — cheap local checks only (< 1 s)
winpodx doctor --fix              # Idempotent auto-remediation for warn/fail findings that carry a fixer (dead agent, stale locks, missing desktop entries, OEM-version drift)
winpodx autostart on|off|status   # Start the Windows pod on login (opt-in; off by default)
winpodx language                  # Show the current UI language
winpodx language ko               # Set UI language: auto | en | ko | zh | ja | de | fr | it (auto = host locale)
# `winpodx info` and `winpodx check` are deprecated aliases of `winpodx doctor` (work through 0.6.x with a notice; removed in 0.7.0).
winpodx gui                       # Launch Qt6 main window (Dashboard / All apps / Devices / Settings / Tools / Terminal)
winpodx tray                      # Launch Qt system tray icon
winpodx config show               # Show current config
winpodx config set rdp.scale 140  # Change a config value
winpodx config import             # Import existing winapps.conf

GUI

Launch with winpodx gui. The Qt6 main window is a Start-menu-style shell (#460-#471): a left vertical navigation sidebar with one row per page, a hero search bar that doubles as a command bar, an in-house SVG icon set, and responsive layouts that reflow on narrow / fractionally-scaled windows and fit themselves to the screen. The pages:

Page What it does
Dashboard Home screen — live Pod / RAM / CPU ring gauges + disk usage, an auto-recovery status card, pinned / recent workspace tiles, a "Running sessions" strip that lists live RDP app sessions with a per-session terminate button, and a reverse-open toggle
All apps Grid / list view of installed app profiles (formerly "Apps"), search + category filter, per-app launch with 3 s cooldown, Add / Edit / Delete app profile dialogs
Devices Two-column host ↔ guest mover for USB / PCI device passthrough (#286) — pick a host device on the left, attach it to the Windows guest on the right (USB hot-plugs live; PCI needs a guest restart with a safety confirmation)
Settings RDP (user / IP / port / scale / DPI / password rotation / multi-monitor), Container (backend / CPU / RAM / idle timeout), and the reverse-open panel (enable toggle, allowlist + denylist, live daemon status, refresh / start / stop buttons) all in one screen
Tools Suspend / Resume / Full Desktop buttons, Clean Locks / Sync Time / Debloat, Grow Disk / Sync Guest, and a one-click Windows Update enable / disable toggle
Terminal Embedded shell limited to a command allowlist (podman, docker, winpodx, xfreerdp, systemctl, journalctl, ss, ip, ping, ...) with quick buttons (Status / Logs / Inspect / RDP Test / Clear)
Info Live Health card (pod / RDP / agent / OEM / disk / password age / app count) + System / Display / Dependencies / Pod / Config snapshot

The system tray (winpodx tray) is a lighter-weight alternative — pod controls, app launcher submenu (top 20 + Full Desktop), a USB device switcher (#300, attach / detach host USB devices to the guest), maintenance submenu (Clean Locks / Sync Time / Suspend), a running-sessions submenu that can terminate live RDP app sessions, and an optional idle-monitor thread.

Tray auto-spawn + UNRESPONSIVE recovery (v0.5.5)

Since v0.5.5 the tray spawns itself automatically from the GUI window and from every CLI subcommand that touches the pod (everything except setup / gui / tray), so a user who only ever runs winpodx app run still gets the system-tray indicator + the UNRESPONSIVE auto-recovery driver. A flock under $XDG_RUNTIME_DIR/winpodx/tray.lock prevents stacked instances when the user manually re-launches the tray.

The tray context menu now starts with Open Dashboard (one-click to the main GUI window). Quit confirms via a dialog and on confirmation runs stop_pod + pkill -f 'winpodx gui' + app.quit so a stray click can't cycle the pod's ~30 s restart.

To launch the tray at every login, open the GUI → Settings → tick "Launch WinPodX tray at login (system tray icon + idle-stall auto-recovery)". The toggle writes / removes ~/.config/autostart/winpodx-tray.desktop via the XDG autostart spec; portable across KDE / GNOME / XFCE / Cinnamon. The file is the source of truth — you can also drop it by hand to opt out without launching the GUI. Toggle applies immediately; no Save Settings click needed.

The tray watches the pod state every 30 s. On a RUNNING → UNRESPONSIVE transition (container alive long enough that an RDP-port miss can't be confused with a fresh boot) it fires a desktop notification and spawns a background worker that asks the agent to cycle Windows TermService. On recovery a "Pod recovered" notification fires; on failure a "needs manual restart" notification points at winpodx pod restart. While install.sh is running its [3/4] / [4/4] Sysprep + OEM-reboot phases, the marker file ~/.config/winpodx/.install_in_progress suppresses the recovery path so genuine install-time RDP gaps don't fire spurious notifications.

Host device passthrough

Pass a host USB or (non-GPU) PCI device through to the Windows guest (#286). Three surfaces drive the same backend:

  • CLIwinpodx device list shows each host device + its attach state; winpodx device attach <id> / winpodx device detach <id> move one in or out.
  • GUI Devices page — a two-column host ↔ guest mover (pick on the left, attach on the right).
  • System tray — a USB switcher submenu (#300) for one-click attach / detach of host USB devices.

USB devices hot-plug live (cfg.pod.usb_live, default on) — no restart needed. A PCI device is boot-added and only becomes visible after a guest restart, so the attach is guarded by a safety confirmation; pass --force on the CLI (or confirm the dialog in the GUI) to proceed.

Bare-metal compatibility mode (hide the hypervisor)

Some software refuses to run under a detected hypervisor — most notably Nvidia's consumer GPU drivers (they fault with code 43 when they see KVM, the common GPU-passthrough blocker) and apps with launch-gate VM checks. cfg.pod.disguise_level (#246) hides the KVM/QEMU signature from the guest so it presents as a physical PC. It has three levels, default balanced:

Level What it does Performance
off No disguise — an honest VM. Best (and most compatible)
balanced (default) Clears the CPUID hypervisor bit + KVM signature, mirrors the host's SMBIOS/DMI, adds synthetic sensor descriptors, and advertises a bare-metal-looking disk size. No measurable cost
max Everything in balanced plus emulated virtual hardware — disk → SATA (AHCI), network → e1000 (MTU=1500), GPU → std VGA, no virtio-rng — and HV=N. This removes the virtio (VEN_1AF4) / QXL (VEN_1B36) PCI IDs and the vioscsi/viostor/netkvm drivers that give a KVM guest away. (MTU=1500 is required so dockur omits the host_mtu= flag that an e1000 NIC rejects.) Requires a wipe + reinstall (changing the boot-disk controller makes the existing install unbootable), so switching into/out of max prompts a strong confirmation and reinstalls Windows from scratch. Significantly slower — emulated disk + NIC have much lower throughput than virtio, and Hyper-V enlightenments are off
winpodx config set pod.disguise_level off        # honest VM
winpodx config set pod.disguise_level balanced   # default — free hiding, applied automatically
winpodx config set pod.disguise_level max        # maximum hiding (emulated HW, slower)
# off <-> balanced apply automatically. Switching into/out of `max` changes the
# virtual hardware, so it needs a destructive reinstall — apply it with:
winpodx pod recreate --wipe-storage              # WIPES Windows, reinstalls on the new hardware

You can also pick the level in the GUI: Settings → Bare-metal compatibility. Either way it takes effect after a winpodx pod recreate (it edits the QEMU -cpu line, the HV env, and the disk size; recreate keeps your Windows disk).

Advanced — patched-QEMU image (winpodx disguise build-image): a couple of VM markers live in QEMU's compiled-in strings (ACPI OEM BOCHS, disk model QEMU HARDDISK) that command-line args can't reach. Run winpodx disguise build-image — it builds a custom dockur image whose QEMU has those strings patched to your host's real vendor + disk model (read from /sys, no root, nothing committed; ~2040 min compile, local image only), then sets cfg.pod.disguise_image so max uses it. winpodx ships only the patch recipe (packaging/qemu-disguise/), never a patched binary. PCI vendor IDs are deliberately left alone (spoofing them breaks dockur's virtio-serial). See that directory's README.

Not an anti-cheat bypass. This is signature-level VM hiding for casual detectors and VM-hostile apps (code 43, DRM/launch gates). It does not defeat kernel-mode anti-cheat (EAC / BattlEye / Vanguard) — those anchor on hardware attestation (TPM + Secure Boot) and VM-exit timing the guest can't spoof — and bypassing online-game anti-cheat violates the game's ToS.

The disk bump is gated: the dockur disk is sparse, so a larger advertised size costs ~0 host space up front, but winpodx only raises it when the host has enough free space (keeping a 10 GiB / 10 % reserve). On a small host it leaves the disk as-is and logs a warning. The pre-0.6.x disguise_hypervisor = false key still works — it maps to off.

This is not an anti-cheat bypass. It is signature-level only and does not defeat kernel-mode anti-cheat (EAC / BattlEye / Vanguard). Bypassing anti-cheat in online games violates their terms of service and risks a ban — winpodx does not support that use.

Multi-monitor

Multi-monitor RAIL is on by default (cfg.rdp.multimon, default "span"): a remote-app window keeps working input when you drag it onto a second monitor. Values:

cfg.rdp.multimon Effect
span (default) Span the RDP session across all monitors so a remote-app window stays interactive on any of them
multimon Use FreeRDP's discrete /multimon mode (per-monitor geometry)
off Single-monitor only

Change it with winpodx config set rdp.multimon off or via the GUI Settings page.

Health checks

winpodx doctor runs every probe used by the GUI Health card and prints a one-line verdict for each:

=== WinPodX doctor ===

  [OK  ] pod_running        running (ip=127.0.0.1)  (58ms)
  [OK  ] rdp_port           127.0.0.1:3390 reachable  (0ms)
  [OK  ] agent_health       version=0.2.2-rev4  (63ms)
  [OK  ] agent_auth_ready   bearer token available  (1ms)
  [OK  ] oem_version        bundle=24  (3ms)
  [OK  ] password_age       7d remaining (max_age=7d)  (0ms)
  [OK  ] apps_discovered    41 app(s) in /home/.../discovered  (3ms)
  [OK  ] disk_free          401.0/3725 GiB free  (0ms)

Overall: OK

Status legend: OK (green) / WARN (yellow — informational, exit 0) / FAIL (red — exit 1) / SKIP (grey — disabled by config). Use --json for machine-readable output.

Changing the Windows password

Use winpodx rotate-password — never reuse winpodx setup for this. The two have very different effects on an already-running install:

Command Host config (winpodx.toml) Windows guest account
winpodx rotate-password Updated atomically (with rollback on failure) Updated via Windows-side change mechanism
winpodx setup (rerun) Preserved as-is (since v0.5.5) Not touched
winpodx setup (fresh install, no prior config) Generated / prompted Applied on first boot via dockur USERNAME/PASSWORD env vars

Re-running winpodx setup to bump cores / RAM / win_version is safe and will not touch your credentials. On pre-v0.5.5 releases the wizard reprompted for the password every run and silently overwrote winpodx.toml — but dockur honors the password env var only on first boot, so the host config desynced from the Windows guest account and the next RDP launch failed with LOGON_FAILED_BAD_PASSWORD.

Recovering from a desynced password (pre-v0.5.5 lockout)

If you ran winpodx setup on an older release and can no longer log in:

  1. Restore the old password if you still have it (in a winpodx.toml backup, your password manager, or your shell history):
    winpodx config set rdp.password '<old-password>'
    winpodx pod start
    winpodx rotate-password
    
  2. Otherwise, the only path is winpodx uninstall --purge + reinstall, which loses any in-Windows state (installed apps, documents, settings). Make a fresh winpodx setup your first step after reinstall, then never touch the password through setup again — use rotate-password.

Performance tuning profile

cfg.pod.tuning_profile controls how aggressively WinPodX tunes the dockur compose for the underlying host. It defaults to "auto" — WinPodX probes the host once at compose time and turns on the matching subset of safe Windows-on-KVM tweaks. Look at the [Tuning] block in winpodx doctor to see what was detected and applied:

[Tuning]
  invtsc:        yes   (intel)
  io_uring:      yes   (kernel 6.18, need >= 5.6)
  hugepages:     no    (sysctl vm.nr_hugepages)
  dedicated:     yes
  nested_kvm:    yes   (/sys/module/kvm_*/parameters/nested)

  Profile: auto
    +invtsc:        yes
    io_uring aio:   yes
    hugepages:      no
    CPU pinning:    yes
    platform_tick:  yes
    no balloon:     yes
    hv-* + no-hpet: yes
    virtio-rng:     yes
    nested virt:    yes
    hv-evmcs:       yes

Profiles:

tuning_profile What it does
auto (default) Detect host capability + apply every safe tuning the host can support, including the Hyper-V enlightenments, virtio-rng, and nested-virt pass-through when /sys/module/kvm_*/parameters/nested is set. CPU pinning + no-balloon gated on dedicated_host (idle CPU + free RAM ≥ 2× VM allocation) so we don't starve other host workloads. Recommended for most users.
performance Same as auto but bypasses the dedicated_host gate: CPU pinning + no-balloon flip on regardless of current host load. Use when the box is mostly dedicated to WinPodX and you want minimum guest latency at the cost of other host workloads. Hard-gated knobs (+invtsc, io_uring) still respect capability detection -- performance can't force a CPU flag QEMU would reject or a kernel feature that crashes.
safe Apply the Windows-guest-only subset that requires no host configuration: +invtsc (when supported), platform_tick BCD tweak, Hyper-V enlightenments (hv-relaxed, hv-vapic, hv-vpindex, hv-runtime, hv-synic, hv-reset, hv-frequencies, hv-reenlightenment, hv-tlbflush, hv-ipi, hv-spinlocks=0x1fff, hv-stimer, hv-stimer-direct, -no-hpet), and virtio-rng. Excludes nested-virt + hv-evmcs which need explicit host-side opt-in.
off Apply nothing; the dockur defaults stand. Use when troubleshooting suspected tuning interaction.
manual Same shape as safe; reserved for future per-knob overrides.

What each tuning does

  • +invtsc — exposes invariant TSC so Windows uses TSC as the clock source instead of HPET (lower IRQ overhead).
  • hv-* enlightenments + -no-hpet (#245) — tells Windows it's running under a paravirtualised hypervisor. Cuts spinlock / VM-exit overhead on every workload; doubly noticeable on multi-vCPU guests. hv-spinlocks=0x1fff is the upstream-recommended retry budget.
  • virtio-rng-pci backed by /dev/urandom (#245) — fills the Windows entropy pool quickly on first boot so CryptoAPI / TLS handshakes don't stall waiting for kernel randomness.
  • +vmx / +svm nested virt (#245) — auto-enabled when /sys/module/kvm_intel/parameters/nested or kvm_amd reads Y. Required for Hyper-V / WSL2 / Docker Desktop inside the Windows guest. No effect when the host kernel hasn't opted in.
  • hv-evmcs (#245) — Intel-only nested-VMCS optimisation, paired with +vmx. Zero overhead when the guest doesn't run nested VMs.
  • io_uring AIO — kernel ≥ 5.6 disk I/O backend; lower latency than legacy threads.
  • Hugepages — backs the QEMU memory with 2 MB pages. Requires vm.nr_hugepages reserved on the host (WinPodX does not auto-reserve).
  • CPU pinning — WinPodX flags the host as dedicated and applies QEMU vCPU pinning when host idle CPU + RAM ≥ 2× VM allocation.

One-shot override

winpodx pod start --tuning {auto,safe,off,manual} overrides cfg.pod.tuning_profile for the lifetime of that container run only. The user's persisted preference in winpodx.toml is left untouched. Useful for A/B testing — flip back and forth without winpodx config set round-trips.

Items that require host-side setup (not auto-applied)

These are standard Windows-on-KVM tweaks that need operator action on the Linux host before WinPodX can take advantage of them. The [Tuning] block in winpodx doctor will show them as no until the host is set up; flipping to yes happens automatically the next time cfg.pod.tuning_profile = auto runs.

  • Transparent hugepages / explicit hugepages. Set vm.nr_hugepages via sysctl (or use madvise THP) so the QEMU process can back its memory with hugepages. WinPodX detects HugePages_Total > 0 in /proc/meminfo and skips the auto-apply if hugepages aren't reserved.
  • CPU pinning. WinPodX flags the host as dedicated when the current idle CPU + RAM is at least twice the VM's allocation. Pinning the QEMU thread to specific cores via taskset (or systemd CPUAffinity=) is then up to the operator; WinPodX will not modify host scheduling.
  • VFIO GPU passthrough. Out of scope for the RDP-based WinPodX architecture. (Non-GPU USB / PCI device passthrough is supported — see "Host device passthrough" below.) If you need bare-metal GPU performance, run your own GPU-passthrough Windows VM (for example with libvirt / virt-manager) and point WinPodX at its RDP endpoint using the manual backend.

Configuration

Config file: ~/.config/winpodx/winpodx.toml (auto-created, 0600 permissions)

[rdp]
user = "User"
password = ""                # Auto-generated random password
password_updated = ""        # ISO 8601 timestamp
password_max_age = 7         # Days before auto-rotation (0 = disable)
ip = "127.0.0.1"
port = 3390
scale = 100                  # Auto-detected from your DE
dpi = 0                      # Windows DPI % (0 = auto)
multimon = "span"            # Multi-monitor RAIL: span | multimon | off
extra_flags = ""             # Additional FreeRDP flags (allowlisted); e.g.
                             #   "+multitouch" — touchscreen / stylus / pen
                             #   passthrough into Windows apps (#623)
                             #   "-gfx" — legacy GDI path (RAIL render workaround)

[pod]
backend = "podman"
win_version = "11"                               # 11 | 10 | ltsc11 | ltsc10 | iot11 | tiny11 | tiny10 | 2025 | 2022 | 2019 | 2016 — see ARCHITECTURE.md for custom ISOs
keyboard = "en-US"                               # Windows install locale; also mapped to the FreeRDP session layout (/kbd:layout) so non-US keyboards work in RemoteApp windows (#660)
cpu_cores = 4
ram_gb = 4
vnc_port = 8007
auto_start = false                               # Opt-in login auto-start: tray starts the pod on login (toggle via `winpodx autostart on|off|status`)
idle_timeout = 0                                 # Seconds before auto-suspend (0 = disabled)
boot_timeout = 300                               # Seconds to wait for first-boot unattended install
image = "docker.io/dockurr/windows:latest"       # Container image (override for air-gapped mirror)
usb_live = true                                  # Hot-plug attached USB devices into the running guest (no restart) — see `winpodx device`
# disguise_level = "balanced"                    # Bare-metal mode: off | balanced (default, free hiding) | max (Hyper-V off, slower) — Nvidia code-43 / VM-hostile apps; not an anti-cheat bypass (#246)
disk_size = "64G"                                # Virtual disk size passed to dockur (grows via `install grow-disk`)
disk_autogrow = true                             # Auto-grow C: when it fills past the threshold (idle only)
disk_autogrow_threshold_pct = 80                 # Used-% that triggers an auto-grow (50-99)
disk_autogrow_target_free_pct = 30               # Grow is sized to restore this much free (not a flat step)
disk_autogrow_increment = "32G"                  # Grow granularity / minimum step
disk_max_size = ""                               # Optional hard ceiling; empty = bounded only by host free space
guest_autosync = true                            # After a host upgrade, push updated guest artifacts in (no reinstall)

[ui]
language = "auto"                                # UI language: auto | en | ko | zh | ja | de | fr | it (auto = host locale, falls back to English; change via `winpodx language` or GUI Settings)

[desktop]
mime_associations = true                         # Discovered apps offer their real file types in the file manager's "Open with" (#545); never set as the default handler
full_app_scan = false                            # false = Start-Menu-only discovery (clean menu, folder-grouped); true = also scan registry App Paths / Chocolatey / Scoop / all UWP (#581) — for portable apps with no Start Menu entry

[reverse_open]
enabled = true                                   # Default since v0.5.0
allow = []                                       # Empty = all discovered apps
deny = []                                        # Apps to exclude from the manifest

[logging]
level = "INFO"                                   # DEBUG | INFO | WARNING | ERROR | CRITICAL | RAW — RAW = DEBUG + pod logs (podman logs -f) interleaved in GUI Terminal

Edit via winpodx config set <key> <value> or directly with your editor — TOML is parsed via the stdlib on Python 3.11+ (tomli on 3.9/3.10).