Bitcoin Core wallets, and what wallet.dat actually is
Core's wallet does not hand you twelve words, and the file everyone argues about on the internet is no longer the file Core writes. What is actually in it now, how to make one, and how to load a wallet.dat the Entropy Workshop produced from a seed you already have.
wallet.dat is the most discussed and least understood file in bitcoin. Most of what is written about it — the recovery services, the forum threads, the dumpprivkey incantations — describes a file format Bitcoin Core no longer opens. This page is what it is now.
Start Here says in passing that Core "does not hand you twelve words. A Core wallet is described by a descriptor containing an extended private key, and the backup is a file rather than a phrase." That sentence is the whole of this page, unpacked.
Before you start
- Bitcoin Core installed. It does not need to be synced. Every step here except the final balance check works on a node that is still catching up.
- Comfort with a terminal, or with Core's GUI menus. Both routes are given.
- No bitcoin. Everything below can be done on an empty wallet, and should be the first time.
- If you intend to import a Workshop export: a test sequence, not the rolls behind a wallet you use.
1Verify the download first
Core is the software that decides what your node believes. A substituted copy is not a wallet problem, it is a validation problem. It could accept blocks nobody else accepts, or report a balance that is not there.
Core is signed by a number of independent builders rather than one, which is a stronger arrangement than most projects manage: several people compile the release themselves and sign the same checksums. Verifying it means checking your download against those checksums and checking the checksums against builders you chose to trust.
How to verify a download, and how to read the output
A single good signature on a multi-signer project is a partial result. That guide covers the distinction, and the alarming warning a successful check prints.
2What running a node actually needs
Worth knowing before you start, because the requirement people expect is not the one that bites. Bitcoin Core's own guidance:
| Resource | What it needs |
|---|---|
| Disk | Over 750 GB for the full chain, or about 7 GB pruned. Read/write of at least 100 MB/s. |
| Memory | 2 GB of RAM. |
| Upload | At least 400 kbit/s, and around 200 GB a month if you leave it serving peers. |
| Download | Around 20 GB a month, plus roughly 740 GB the first time. |
| Time | The initial download takes at least several days, longer on slow hardware or a slow connection. |
Pruning is the setting most people want. It validates every block exactly as a full node does and then discards the old ones, taking disk from over 750 GB to around 7 GB. The trade is that a pruned node cannot serve historical blocks or rescan for a wallet with years of history, which matters if you plan to import an old wallet later.
You do not need a synced node to make a wallet
This stops more people than the disk requirement does. createwallet, restorewallet, address generation and descriptor export all work the moment Core is installed, because none of them ask the chain anything. Only balances and transaction history need a synced node, because only those are questions about what happened. Set the wallet up on day one and let the sync finish in its own time.
Two other guides cover the surrounding decisions: why run a node makes the case, and connecting wallets to your node deals with the part nobody warns you about, which is that installing Core is not enough to point most wallets at it.
3The file changed underneath the folklore
For most of bitcoin's history a Core wallet was a Berkeley DB file holding a pool of individual private keys. That is the wallet.dat of the recovery industry, of dumpwallet and importprivkey, and of every story about a hard drive in a landfill.
It is gone. Bitcoin Core 30.0 states it plainly: Berkeley DB legacy wallets can no longer be created or loaded. The RPCs that went with them — dumpprivkey, dumpwallet, importprivkey, importaddress, importmulti, sethdseed and others — were removed in the same release.
- Advice telling you to run
importprivkeyis describing software that no longer exists. The command is not deprecated; it is absent. - A genuinely old
wallet.datfrom a Berkeley DB era cannot be loaded by a current Core, but it can still be migrated by one. Core 30 keeps themigratewalletRPC for exactly this, and its release notes say so: legacy wallets "can be migrated to the new descriptor wallet format". You do not need to find an old version, and you should not go looking for one. - Recovery services advertising
wallet.datextraction are usually talking about the old format. That is a different problem from anything on this page.
If you are migrating a legacy wallet, mind the version
Bitcoin Core published a notice about a wallet-migration bug in 30.0 and 30.1: where a default unnamed wallet.dat failed to migrate or load, Core could delete every file in the wallet directory, and the project asked users not to migrate on those versions at all until 30.2. Migrate on 30.2 or later. Take an independent copy of the whole wallet directory first, somewhere Core is not writing — that is cheap, and it is the one precaution that survives being wrong about any of the rest of this.
4What a Core wallet is today
A modern Core wallet is a descriptor wallet, stored as an SQLite database. The database file is still called wallet.dat, which is most of why the confusion persists — same name, different thing entirely.
Each wallet is a folder inside Core's wallets directory, and the folder holds the database:
- Linux.
~/.bitcoin/wallets/<name>/wallet.dat - macOS.
~/Library/Application Support/Bitcoin/wallets/<name>/wallet.dat - Windows.
%LOCALAPPDATA%\Bitcoin\wallets\<name>\wallet.dat
Inside, rather than a heap of keys, it holds descriptors: a compact statement of the script type, the key, and the derivation path, from which every address is computed on demand. That is the same object a restore needs in any other wallet, written down properly.
5Creating one
Either route produces the same thing.
- Terminal.
bitcoin-cli createwallet "savings" - Terminal, encrypted from the start.
bitcoin-cli -named createwallet wallet_name="savings" passphrase="…" - GUI. File, then Create Wallet.
Encrypt at creation if you are going to encrypt at all. A Core wallet file is not encrypted by default, and an unencrypted wallet.dat containing private keys is exactly as sensitive as a written seed phrase and considerably easier to copy. But the stronger reason is what encrypting later does, which is not what most people expect.
- Encrypting an existing Core-generated wallet rotates its keys. Core does not simply put a password on what is already there. It generates fresh descriptors and marks the old ones inactive, so every address issued afterwards belongs to key material your earlier backup does not contain. Core prints a warning saying a new backup is required, and it means that literally.
- A backup taken before encryption stops covering the wallet's new key material. Be precise about what that means, because it is narrower than it sounds and worse than it sounds. The old backup still holds the keys it always held, so it recognises addresses issued before you encrypted, including payments arriving at those addresses long afterwards. What it cannot reach is anything received at an address the wallet issued after encryption, because those come from descriptors it has never seen.
- If you encrypt an existing wallet anyway, run
backupwalletimmediately afterwards, refresh any private descriptor backup you keep, and test the new one before relying on it. Section 10 has the drill. - This is about wallets Core generated for itself. A Workshop export is a different case — it arrives with its descriptors already set, and encrypting it does not replace them.
6The part that surprises people
Core does not use BIP39 recovery words. It never has.
When Core generates a wallet it produces its own entropy and stores the resulting keys in the database. There is no phrase to write down, because none exists. The backup is the file itself, or a private descriptor export from it, and it restores into descriptor-aware software rather than by typing words into a device. A public descriptor is a different object and does not stand in for either: it rebuilds your addresses and your balance, and it cannot sign.
A Core wallet's backup is a file. Losing it, with nothing holding the private keys elsewhere, loses the wallet exactly as thoroughly as losing a seed phrase. A public descriptor is not that something.
That is a perfectly good backup and it is a different discipline from the rest of this site. It also explains the gap the next section fills: if you want a wallet that Core can drive and that twelve words can rebuild, Core will not make one for you.
7Exporting a wallet.dat from the Entropy Workshop
The Entropy Workshop converts dice, coins or cards into a seed, and can write the resulting account out as a Bitcoin Core descriptor wallet. The point is not convenience. It is that the wallet Core ends up driving is derived from a BIP39 seed you hold, so the words remain the master backup even though Core has no concept of them.
It offers two files, and the difference is the entire safety question.
| File | Contains | Can it spend? |
|---|---|---|
watch-only-wallet.dat | Account public keys and checked descriptors. Flagged disable-private-keys. | No. Watching only. |
wallet.dat | The above, plus the account private key as a wallet descriptor key record. | Yes. Treat it like a seed. |
The private file is only produced while "Show private recovery material" is switched on, and both the button and the filename say which one you are downloading. That is deliberate: the two files look identical in a downloads folder otherwise.
- The exported file is not encrypted. Encrypt the wallet in Core after loading it, or keep the file somewhere you would keep a seed card.
- Never replace or overwrite an existing
wallet.dat. There is no undo, and the wallet you overwrite may be the one holding coins. - Use the Workshop for a test sequence first. It is experimental software and says so, and a wallet you intend to keep deserves a device that was built for it.
8Loading it into Core
The tidy route is restorewallet, which takes the file and builds the folder around it rather than asking you to place anything by hand:
bitcoin-cli restorewallet "workshop-test" /path/to/wallet.dat- In the GUI: File, then Restore Wallet, then pick the file and give it a name.
- The name is Core's label for the wallet, not part of the file. Choose one you will still recognise later.
Core will refuse if a wallet of that name already exists, which is the behaviour you want. If it loads, the wallet appears in the wallet list and Core begins scanning the chain for its addresses.
A freshly imported descriptor wallet has no transaction history until that scan finishes. On a fully synced node it is quick; on a node still catching up it will show nothing until it gets there, which is not a fault.
9Prove it is the wallet you think it is
Everything above is an assumption until checked, and the checks are quick.
- Compare the descriptor.
bitcoin-cli -rpcwallet="workshop-test" listdescriptorsand confirm the key origin and path match what the Workshop showed you. - Derive address zero and compare it to the first receiving address the Workshop displayed. Take the external descriptor from the output above — the one ending
/0/*, with its checksum — and runbitcoin-cli deriveaddresses "<descriptor>" "[0,0]". They must match exactly. - Confirm what it can do. A watch-only import reports private keys as disabled; a private one does not. Check you got the file you meant to.
- Then a test amount, and only then anything more. Sending a test transaction covers that loop.
- Do not use
getnewaddressfor this comparison, which is the obvious move and is wrong twice. It asks for bech32 by default, so on a wallet exported as legacy, nested SegWit or Taproot it fails outright with-12: No bech32 addresses available. And passing the right type is only half a fix, because it also advances the wallet's next index. Run it once and the address you meant to compare is no longer the one you get back. - If you reach for it anyway, the type argument has to match the export:
legacy,p2sh-segwit,bech32, orbech32mfor Taproot.deriveaddressesneeds none of that, changes nothing, and gives the same answer on a wallet that has already issued addresses.
10Backing up a Core wallet
This is the part that behaves least like the rest of this site, so it is worth doing deliberately rather than by analogy.
Do not copy the live file
Copying wallet.dat while Core is running can capture it mid-write. The database may be consistent, or it may not, and you will not find out until the day you need it. Core has a command that takes a safe copy while the wallet is in use:
bitcoin-cli -rpcwallet="savings" backupwallet /path/to/backup.dat- In the GUI: File, then Backup Wallet.
- The result is a single file. Restore it later with
restorewallet, the same command used for the Workshop export above.
If you would rather copy the file by hand, stop Core first and let it shut down fully. A clean shutdown is what makes the file safe to copy.
Back up the descriptor as well
The file is the whole wallet; the descriptor is the part that survives the file. Export it and keep it separately:
bitcoin-cli -rpcwallet="savings" listdescriptorsgives the public form — script type, extended public key, derivation path, checksum.- It cannot spend, so it is safe to store where a spending backup would not be. It does reveal your addresses and balance to anyone who reads it. And it cannot restore access to coins on its own. Keeping only this and losing the file leaves you able to watch the wallet and unable to move it.
- Any descriptor-aware wallet can rebuild your addresses from it, which is what makes it worth having when the file is gone or the software has moved on.
listdescriptors trueincludes the private keys. That output is a spending backup and should be treated exactly like a seed.
This is the same advice the restore guide gives for every wallet, and it matters more here because there is no phrase standing behind it.
Encryption is separate from the backup
A Core wallet is not encrypted unless you encrypt it, and a backup of an unencrypted wallet is an unencrypted spending key in a file. Encrypting sets a passphrase that Core requires before signing.
- The passphrase is not stored in the backup file in any recoverable form. Lose it and the backup is inert, exactly like a forgotten BIP39 passphrase.
- Record it separately from the file, in a different place, with the same durability you would give a seed card.
- Encrypting after the fact does not retroactively protect a backup you already made while it was unencrypted. Destroy those or treat them as spending material forever.
- And on a Core-generated wallet it invalidates that backup going forward as well, because encrypting rotates the active descriptors. Section 5 covers this. Take a fresh
backupwalletimmediately afterwards and prove it with the drill below.
Then prove it
An untested backup is a guess here as much as anywhere. The drill is cheap:
- Restore the backup under a different wallet name on the same node, with
restorewallet "backup-test" /path/to/backup.dat. - Compare the descriptors against the original with
listdescriptors, and derive address zero from each withderiveaddresses. They must match exactly. - Then check an address the wallet issued most recently, not only the first one.
getaddressinfoon the restored wallet should reportismine: truefor it. This is the check that catches a backup which covers the wallet's original keys and none of the ones in use, which is the exact state encrypting an existing wallet leaves you in. - Unload the test wallet afterwards so you are not running two copies of the same wallet.
- Repeat after anything that changes the wallet's structure, encrypting it above all, and periodically regardless.
Where the real backup lives
- If Core generated the wallet, the file — or a private descriptor export of it — is the only thing that can spend. There is no phrase and nothing to fall back on. Keep the public descriptor too, but keep it knowing it restores your view of the wallet and not your access to it.
- If it came from a BIP39 seed by way of the Workshop, the words are the master backup and the file is a convenience. Losing the file costs you an afternoon; losing the words costs you the wallet.
- Either way, store the backup somewhere a single event cannot reach alongside the machine running the node. The durability guide applies to a file on a drive as much as to words on a card — drives fail, and they fail silently.
The short version
The wallet.dat people argue about on the internet is a Berkeley DB file Core stopped opening at version 30. What Core writes now is an SQLite descriptor wallet in a folder of its own, holding descriptors rather than a pile of keys, with no recovery phrase anywhere because Core does not use them. The Workshop can hand Core a wallet derived from a BIP39 seed instead — in a watch-only form that cannot spend, or a private one that can and should be treated exactly like a seed card.
If you take one thing from this page
Core's backup is a file, not a phrase. That is not a flaw, but it is a different discipline from every other guide here. If you delete the file with nothing holding its private keys elsewhere, the wallet is gone as completely as if you had burned a seed card.
Paths, commands and the removal of Berkeley DB wallets are taken from Bitcoin Core's own documentation and its v30.0 release notes, which also record that legacy wallets can still be migrated; the migration bug in 30.0 and 30.1 is from the project's own notice. The key rotation on encrypting an existing wallet, and the address-type behaviour of getnewaddress against a Workshop export, were reproduced against Core 30.2 during review of this page rather than inferred. The Workshop's export follows the descriptor-wallet layout Core writes and has been loaded against a current release. Wallet behaviour changes between versions, so confirm against
Bitcoin Core's documentation
for the version you are actually running.