bcdedit is the only editor Windows ships for the Boot Configuration Data store: the binary database that replaced boot.ini and decides which loader runs, what the boot menu shows, how long it waits, and which diagnostic switches the kernel starts with. The store is binary. Unlike boot.ini, it cannot be opened and corrected in a text editor.
That is exactly what makes it uncomfortable on a production server. There is no confirmation prompt, no dry run and no undo. A /set aimed at {current} takes effect on the entry you are booted from, and you find out whether it was a good idea on the next restart, when the machine is either at a logon screen or at a recovery prompt.
The switch list is already on Microsoft Learn. What follows is the working loop that makes bcdedit safe to run on a box you care about: export the store, do every experiment on a copy of the boot entry, boot that copy exactly once with a sequence that reverts itself, then delete it. Three parsing traps are included at the end, all measured rather than remembered.
Applies to: Windows 10 / 11, Windows Server 2016 / 2019 / 2022 / 2025
Quick answer
Read the store, back it up, and know the one command that puts it back. The export file is the only undo bcdedit has, and it takes about a second to produce. Everything else in this article assumes it exists.
rem all three need an elevated prompt - bcdedit refuses to read or write the store otherwise
bcdedit /enum
rem the whole system store, written to one file, before any change
md C:\bcd-backup 2>nul
bcdedit /export C:\bcd-backup\bcd-2026-09-14.bcd
rem put it back if a change goes wrong - this REPLACES the live store, it does not merge
bcdedit /import C:\bcd-backup\bcd-2026-09-14.bcd
/import deletes every existing entry in the system store before the import takes place. It is a restore, not a merge. If you added a second OS to the boot menu after the export was taken, that entry is gone once you import.
What bcdedit does
bcdedit.exe lives in %WINDIR%\System32 and is present on every supported Windows build, client and server. It reads and writes the system BCD store by default; the /store switch points it at a different store file instead, which is how you inspect a backup without touching the live one.
There is no commit step. Each command writes straight through. A normal shutdown and reboot is still needed to be sure modified settings are flushed to disk, so a hard reset immediately after a change is the one way to lose work you thought you had saved.
These thirteen cover every command this article uses. The full list, including the Emergency Management Services and kernel debugger groups, is on the Microsoft Learn page linked at the end.
| Command | Operates on | What it does |
|---|---|---|
/enum | the store | Lists entries. It is the default, so bare bcdedit equals bcdedit /enum active. |
/v | output | Verbose. Prints full GUIDs instead of the friendly names such as {current}. |
/export | the store | Writes the contents of the system store to a file. System store only. |
/import | the store | Restores the system store from an /export file, deleting existing entries first. |
/store | the store | Points any of the commands above at a different store file instead of the live system store. |
/copy | an entry | Duplicates a boot entry inside the same store and returns a new GUID. |
/set | an entry option | Creates or changes one datatype on one entry. |
/deletevalue | an entry option | Removes one datatype from an entry, leaving the entry in place. |
/delete | an entry | Removes a whole boot entry. Needs /f for well-known identifiers. |
/displayorder | boot manager | Sets the order the boot menu shows entries in. |
/default | boot manager | Sets the entry chosen when the timeout expires. |
/bootsequence | boot manager | A one-time display order, used for the next boot only. |
/timeout | boot manager | Seconds the boot manager waits before selecting the default entry. |
Reading the store: what /enum actually prints
Before changing anything, learn to read the output, because every later command is addressed by an identifier taken from it. The sample below is a plain single-boot UEFI Windows 11 machine, with a second boot entry added so the multi-value case is visible. It comes from a published bcdedit /enum capture; the GUIDs are the only thing altered.
Windows Boot Manager
--------------------
identifier {bootmgr}
device partition=\Device\HarddiskVolume1
path \EFI\Microsoft\Boot\bootmgfw.efi
description Windows Boot Manager
locale en-US
inherit {globalsettings}
default {current}
resumeobject {36596990-877d-11ee-b62c-c8f75008677e}
displayorder {current}
{b4f9a2c1-1d3e-11f0-9c2a-0050569b1a77}
toolsdisplayorder {memdiag}
timeout 30
Windows Boot Loader
-------------------
identifier {current}
device partition=C:
path \WINDOWS\system32\winload.efi
description Windows 11
locale en-US
inherit {bootloadersettings}
recoverysequence {36596993-877d-11ee-b62c-c8f75008677e}
displaymessageoverride Recovery
recoveryenabled Yes
isolatedcontext Yes
allowedinmemorysettings 0x15000075
osdevice partition=C:
systemroot \WINDOWS
resumeobject {36596990-877d-11ee-b62c-c8f75008677e}
nx OptIn
bootmenupolicy Standard
hypervisorlaunchtype Auto
Two blocks, two jobs. Windows Boot Manager is the menu itself: what it lists, what it picks, how long it waits. Windows Boot Loader is one bootable Windows installation and the switches the kernel starts with. A machine with three operating systems has one boot manager block and three boot loader blocks.
These are the eleven fields in the boot manager block above, which is the block you edit when you are changing menu behaviour rather than kernel behaviour.
| Field | Meaning |
|---|---|
identifier | The GUID or well-known name this entry is addressed by. |
device | The partition the boot manager itself was loaded from, normally the EFI system partition. |
path | The boot manager binary on that partition. |
description | The text shown on the boot menu. Changed with /set ID description. |
locale | Language of the pre-boot user interface. |
inherit | The settings collection this entry inherits from. |
default | The entry selected when the timeout expires. Changed with /default. |
resumeobject | The resume-from-hibernation entry paired with this one. |
displayorder | Every entry the menu lists, in order. Multi-value. |
toolsdisplayorder | The Tools submenu, normally just {memdiag}. |
timeout | Seconds before the default entry is chosen. Changed with /timeout. |
The layout matters more than it looks. Every label is padded so that values begin at column 25, and the single longest label in the sample, allowedinmemorysettings at 23 characters, still leaves one space before its value. A multi-value field such as displayorder continues on the next line with no label at all, aligned to the same column 25. That continuation line is where most scripted parsing of bcdedit quietly goes wrong, and it is measured in the Hidden gems section below.
Identifiers come in two forms. Most entries are addressed by a GUID in braces. Fourteen entries have a well-known short name that bcdedit prints instead, unless you add /v.
| Identifier | What it refers to |
|---|---|
{bootmgr} | The Windows boot manager entry. |
{fwbootmgr} | The firmware boot manager, on systems that implement EFI. |
{current} | Virtual: the boot entry for the operating system running right now. |
{default} | Virtual: whichever entry the boot manager currently defaults to. |
{memdiag} | The memory diagnostic application entry. |
{ntldr} | The loader for operating systems earlier than Windows Vista. |
{ramdiskoptions} | Extra options the boot manager needs for RAM disk devices. |
{dbgsettings} | Global debugger settings, inheritable by any boot application. |
{emssettings} | Global Emergency Management Services settings. |
{badmemory} | The global RAM defect list. |
{globalsettings} | Settings inherited by all boot application entries. |
{bootloadersettings} | Settings inherited by all Windows boot loader entries. |
{resumeloadersettings} | Settings inherited by all resume-from-hibernation entries. |
{hypervisorsettings} | Hypervisor settings, inheritable by any OS loader entry. |
{current} and {default} are virtual. They resolve to whatever is true at the moment the command runs, so the GUID behind {current} can differ between two boots of a multi-boot machine. When you want a stable reference in a script, run bcdedit /v once and use the real GUID.
Before the first example
Five things need to be true before any command below is worth running. Each is one line. The example host throughout is SRV-APP-04.
First, confirm the prompt is actually elevated. bcdedit needs administrative privileges even to read the store, and an unelevated prompt gives an access denied message rather than a permissions hint. The SID below is the mandatory high integrity label, present only in an elevated token.
rem S-1-16-12288 is the High Mandatory Level SID - it appears only in an elevated token
whoami /groups | findstr /c:"S-1-16-12288"
Second, confirm which bcdedit you are about to run. It should resolve to the copy in System32 and nowhere else.
where bcdedit
C:\Windows\System32\bcdedit.exe
Third, check BitLocker on the system drive. Microsoft’s guidance is that BitLocker and Secure Boot may need to be suspended before boot options are changed, because a change to the boot configuration alters the measurements the TPM seals the key against. On a protected volume that means a recovery key prompt at the next start.
rem run this before any /set - a protected volume can demand the recovery key after a boot change
manage-bde -status C:
Fourth, create the backup directory and export the store. Later examples assume this file exists, and the cleanup example restores nothing without it.
rem 2>nul hides the "already exists" error so the line is safe to re-run
md C:\bcd-backup 2>nul
bcdedit /export C:\bcd-backup\bcd-2026-09-14.bcd
Fifth, two conventions that apply to every example below rather than to any single command.
- In PowerShell, every identifier must be quoted, so
"{current}"and not{current}. Unquoted braces are not a quoting nicety there, they change what the command receives entirely, and the first hidden gem measures what actually arrives. - Every
for /floop below is written for a batch file, so the loop variable is doubled as%%A. Typing the same loop straight into an interactivecmdprompt needs a single%Ainstead, or it fails on the syntax.
Practical examples
1. Inspect the store without risking it
The problem: you have inherited SRV-APP-04 and need to know what its boot menu is configured to do before a planned change, without writing anything at all.
The solution: every read-only question is answered by /enum with a type or an identifier, and the export from the prerequisites can be read back with /store so you can compare against it later.
rem just the boot manager block - menu order, default entry and timeout
bcdedit /enum {bootmgr}
rem every operating system loader, one block each - one per bootable Windows install
bcdedit /enum osloader
rem full GUIDs instead of {current}, so a script has something stable to address
bcdedit /enum active /v
rem read the backup file instead of the live store - nothing is written
bcdedit /store C:\bcd-backup\bcd-2026-09-14.bcd /enum all
active, firmware, bootapp, bootmgr, osloader, resume, inherit or all. active is the default and is what you get from bare bcdedit, which is why a freshly imaged machine can look like it only has one entry when all would list several more.
2. Make a practice entry instead of editing {current}
The problem: you need to add a boot-time diagnostic to SRV-APP-04, but if it turns out to be wrong you do not want the only bootable entry on the machine carrying it.
The solution: /copy duplicates the current boot entry under a new GUID and a new description. Edit the copy. The original stays exactly as it was, and it stays first in the display order.
rem /d sets the description, which is the text the boot menu will show
bcdedit /copy {current} /d "ZT-Test-Entry"
The new GUID is printed, and it is the handle for everything that follows. Microsoft’s own sample of this message reads:
The entry was successfully copied to {55556666-ffff-7777-aaaa-8888bbbb9999}.
Copy that GUID somewhere before you carry on. If you would rather capture it in a batch file, this is the form that works, and the reason for the trailing substring is the third hidden gem below.
@echo off
setlocal
rem token 7 is the GUID, but bcdedit ends the sentence with a full stop
for /f "tokens=7" %%A in ('bcdedit /copy {current} /d "ZT-Test-Entry"') do set "NEWID=%%A"
rem strip that trailing full stop - without this every later command rejects the id
set "NEWID=%NEWID:~0,-1%"
echo New entry: %NEWID%
endlocal
/create is not. If you ever use /create instead, you must add it yourself with /displayorder or it will never appear.
3. Change one setting on the copy and read it back
The problem: a driver on SRV-APP-04 is suspected of failing during startup, and you want the boot-time driver load log without leaving it enabled on the production entry forever.
The solution: /set writes one datatype onto one entry. Point it at the copy’s GUID and never at {current}. The bootlog datatype enables the system initialization log in Ntbtlog.txt.
rem enables the system initialization log in Ntbtlog.txt for this entry only
bcdedit /set {55556666-ffff-7777-aaaa-8888bbbb9999} bootlog yes
rem read the entry back - the new datatype appears as its own line
bcdedit /enum {55556666-ffff-7777-aaaa-8888bbbb9999}
Read the block that comes back against the {current} block at the top of this article. Three things should differ and nothing else: the identifier is the new GUID, the description is ZT-Test-Entry, and there is now a bootlog line that the original does not have. Everything else, osdevice and systemroot included, came across with the copy, which is why the copy boots the same installation rather than a different one.
bcdedit /set bootlog yes with no identifier. When the identifier is omitted, /set modifies the current operating system boot entry. That is documented behaviour, not a bug, and it is the single easiest way to change the entry you were trying to protect.
4. Show the menu and boot the copy exactly once
The problem: the test entry exists but the machine still boots straight into the production entry, and you do not want to make the test entry the default in case it does not come back.
The solution: give yourself a visible menu with /timeout, then use /bootsequence, which is a one-time display order. It applies to the next start only, and afterwards the computer reverts to the original display order on its own.
rem put the test entry last on the menu - /addlast avoids retyping the whole order
bcdedit /displayorder {55556666-ffff-7777-aaaa-8888bbbb9999} /addlast
rem 15 seconds is long enough to choose on a remote console that redraws slowly
bcdedit /timeout 15
rem next boot only - no permanent change to the default or the display order
bcdedit /bootsequence {55556666-ffff-7777-aaaa-8888bbbb9999}
/bootsequence applied to that one start and nothing else. That self-reverting property is the reason to prefer it over /default while testing.
/default is the permanent version of the same idea, and it is the right command once a configuration is proven rather than while it is being tested.
rem permanent: this entry is chosen every time the timeout expires
bcdedit /default {55556666-ffff-7777-aaaa-8888bbbb9999}
5. Remove the practice entry and restore the settings
The problem: the diagnostic is finished and SRV-APP-04 should look exactly as it did before, with no stray menu entry for the next person to wonder about.
The solution: /deletevalue removes a single datatype and leaves the entry, and /delete removes the entry itself. Because the test entry has a plain GUID rather than a well-known identifier, no /f is needed, and /cleanup is assumed, so it is taken out of the display order in the same step.
rem drop just the datatype, keeping the entry - useful when you want to retest later
bcdedit /deletevalue {55556666-ffff-7777-aaaa-8888bbbb9999} bootlog
rem remove the entry itself - /cleanup is the default, so displayorder is tidied too
bcdedit /delete {55556666-ffff-7777-aaaa-8888bbbb9999}
rem put the menu timeout back where it started
bcdedit /timeout 30
rem confirm: the menu should list what it listed before the test entry existed
bcdedit /enum {bootmgr}
/f exists precisely so that well-known identifiers are hard to delete by accident. bcdedit /delete {bootmgr} /f is a valid command and it removes the boot manager entry. Nothing asks you to confirm. This is the moment the export file from the prerequisites earns its keep.
If anything has gone wrong at any point, the whole store goes back in one command, and it does not matter how many entries were changed in between.
bcdedit /import C:\bcd-backup\bcd-2026-09-14.bcd
Hidden gems
In PowerShell, {current} is not text at all
Microsoft’s documentation says to put quotes around identifiers in PowerShell. It does not say why, and the why is worse than a quoting convention. Braces are PowerShell’s script block syntax, so an unquoted {current} is parsed as a ScriptBlock object before bcdedit is ever launched. Measured in PowerShell 7.4.6:
unquoted {current} -> System.Management.Automation.ScriptBlock
quoted "{current}" -> System.String
What the native executable then receives is not a mangled version of the identifier. It is not the identifier at all. Passing that script block to an external command produces three unrelated arguments:
unquoted: [-encodedCommand YwB1AHIAcgBlAG4AdAA= -inputFormat xml -outputFormat xml]
quoted : [{current}]
That base64 string decodes as UTF-16LE to the single word current, with the braces gone. PowerShell has turned the identifier into an instruction to run a nested PowerShell, and bcdedit is handed the pieces. The same happens to a full GUID in braces. The fix is one pair of quotes, but the reason to remember it is that the failure looks nothing like a quoting problem when you read the error.
cmd and later pasted into a PowerShell remoting session or a scheduled task that runs powershell.exe. The text is identical; the meaning is not.
for /f loses half of displayorder and never says so
Parsing bcdedit /enum with for /f "tokens=1,2" looks obviously right: label in the first token, value in the second. Running CMD’s tokenizer against the sample output at the top of this article shows two places where it is not.
tok1 tok2
------------------------ --------------------------------------
identifier {bootmgr}
description Windows <-- value truncated at the first space
displayorder {current}
{b4f9a2c1-1d3e-11f0-...} (empty) <-- continuation line, GUID landed in tok1
timeout 30
Two separate failures. description Windows Boot Manager is a multi-word value, so tokens=2 returns Windows and silently drops the rest. And the second displayorder GUID sits on a continuation line with no label, so the tokenizer puts the GUID itself in token 1 and leaves token 2 empty. A loop that collects display order entries by reading token 2 after a displayorder label therefore returns 1 of the 2 entries actually present, raises no error and sets no error level.
Two habits avoid both. Use tokens=2,* so a multi-word value keeps its tail, and filter to the single line you want with findstr before tokenizing, so continuation lines never reach the loop.
rem ^| escapes the pipe so for /f passes it to the command, instead of cmd eating it here
rem /c: treats the argument as one literal string, which matters the moment a pattern has a space
rem tokens=2,* keeps the tail of a multi-word value in %%B instead of discarding it
for /f "tokens=2,*" %%A in ('bcdedit /enum {bootmgr} ^| findstr /c:"timeout"') do echo Timeout: %%A
If you do need every display order entry rather than one field, do not use for /f for it. PowerShell can match the continuation lines by the column they align to, and that version is in the next section.
The GUID bcdedit hands back ends with a full stop
/copy reports its result as an English sentence, and sentences end with punctuation. Tokenizing the documented message shows what a batch capture actually gets:
The entry was successfully copied to {55556666-ffff-7777-aaaa-8888bbbb9999}.
tokens=7 -> {55556666-ffff-7777-aaaa-8888bbbb9999}. 39 chars, valid id: no
%~A of that -> {55556666-ffff-7777-aaaa-8888bbbb9999}. unchanged
%NEWID:~0,-1% -> {55556666-ffff-7777-aaaa-8888bbbb9999} 38 chars, valid id: yes
The instinct is to reach for %%~A, but that modifier strips one surrounding pair of double quotes and nothing else, so it leaves the string exactly as it found it. The substring form %NEWID:~0,-1% drops the last character and produces a 38-character identifier that bcdedit accepts. That is the one line separating a working provisioning script from one that creates the entry and then fails on every command afterwards.
bcdedit from PowerShell
Windows ships no PowerShell module for the BCD store. PowerShell runs bcdedit.exe the same way cmd does, with identifiers quoted, and the value of doing so is the parsing rather than the calling. This reads the menu timeout out of the padded layout:
# note the quotes - unquoted {bootmgr} would be parsed as a script block
$enum = bcdedit /enum "{bootmgr}"
# ^timeout anchors to the start of the line so toolsdisplayorder cannot match
$timeout = ($enum | Select-String '^timeout\s+(\d+)$').Matches.Groups[1].Value
"Menu timeout: $timeout seconds"
String, not an Int32. Comparing it with -gt 10 works because PowerShell coerces the left side, but sorting a collection of these gives you a text sort. Cast with [int] if the number is going anywhere numeric.
The continuation-line problem from the second gem is solvable here, because a regular expression can match the exact column the values align to. This collects every display order entry, including the ones for /f drops:
$enum = bcdedit /enum "{bootmgr}"
$ids = @()
$inDisplayOrder = $false
foreach ($line in $enum) {
if ($line -match '^displayorder\s+(\S+)') { $ids += $Matches[1]; $inDisplayOrder = $true; continue }
# a continuation line is 24 spaces then a value, with no label of its own
if ($inDisplayOrder) {
if ($line -match '^\s{24}(\S+)$') { $ids += $Matches[1] } else { $inDisplayOrder = $false }
}
}
"Entries on the boot menu: $($ids.Count)"
$ids
Run against the two-entry sample from the top of this article, that returns both GUIDs and a count of 2, where the for /f version returned one.
For anything beyond reading, Microsoft documents a Boot Configuration Data WMI provider as the supported route for programmatic changes. It is considerably more work than shelling out to bcdedit, and it is the right answer only when you need transactional behaviour or are building a management tool rather than a maintenance script.
Where this matters
- A server that will not boot after a driver rollout. A copied entry carrying
bootloggives youNtbtlog.txtfrom a failing start without making the production entry log every boot forever. - A physical-to-virtual migration. The converted machine can carry boot loader entries pointing at hardware that no longer exists, and
bcdedit /enum allis what shows you which ones to remove. - A boot menu that appeared out of nowhere. An in-place upgrade or a failed image restore leaves a second entry in
displayorder, and users see a menu and a timeout wait on every start. - Change control on a hardened server. An
/exportbefore and after gives you two files that can be diffed by a reviewer, and a rollback that does not depend on remembering what you typed. - Provisioning scripts that create boot entries. The GUID capture is where these break, and they break quietly, leaving a half-configured entry on every machine the script touched.
Tips and limitations
- Administrative privileges are required even to read the store, so
bcdeditis not a tool you can hand to a helpdesk account for diagnostics. - There is no dry run and no confirmation prompt. The export file is the entire safety net, and it is worth taking even for a change you are certain about.
- A normal shutdown and reboot is needed to be sure modified settings are flushed to disk. Do not validate a change by pulling power.
- Microsoft’s own recommendation, repeated in the caution block on its BCDEdit documentation pages, is to use the System Configuration utility
msconfig.exefor routine boot settings. On a workstation that advice is sound;bcdeditearns its place when the change has to be scripted or applied remotely. bcdeditis limited to the standard data types and is designed for single common changes. Recovering a partition or setting up a new system partition isbcdbootterritory, not this tool.- Setting
/timeout 0makes the boot menu invisible rather than fast. On a multi-boot machine that means no way to reach the second entry without/bootsequencefrom inside the first. - The examples above create one file,
C:\bcd-backup\bcd-2026-09-14.bcd. Booting the test entry afterwards also producesNtbtlog.txt, which is the point of enablingbootlog. Nothing else on disk is touched.
Official documentation
- bcdedit | Microsoft Learn
- BCDEdit /enum | Microsoft Learn
- BCDEdit /set and the full datatype list | Microsoft Learn
- Add custom boot entries in Windows | Microsoft Learn
- Edit boot options in Windows using BCDEdit | Microsoft Learn
- manage-bde status | Microsoft Learn
Related tools
- Windows Event Log Analyzer: paste the Kernel-Boot and Kernel-General entries from a failed start to see what the boot manager actually reported before you change anything.
Related guides
- chkntfs in Windows: control the boot-time disk check: the other thing that can delay a start, and the dirty bit that schedules it.
- chkdsk in Windows: what each switch does, and when not to run it: what the boot-time check actually does once it runs.
- DISKPART: practical guide for sysadmins: the partition side of the same machine, including the EFI system partition the boot manager loads from.
- findstr and find in Windows: why
/c:is the right filter for reading one line out of enum output. - driverquery in Windows: audit installed drivers: the same fixed-width parsing trap on a different command, and where to look after a bootlog names a driver.
- systeminfo in Windows: reading every field and scripting host inventory: confirms firmware mode and OS build before a boot change is planned.
- Windows Command Line (CMD) Cheat Sheet: quick lookup for the
for /fandfindstrforms used above. - PowerShell commands cheat sheet: the cmdlet side of the parsing examples in this article.