
Accelerating Bitcoin Initial Sync with the UTXO Set
We’ve all experienced situations where our Bitcoin node stops working—maybe the Raspberry Pi crashes, the disk fails, or we simply need the disk for other purposes and end up without a functioning Bitcoin node. Later, when we want to set up the node again, we find ourselves going through the initial block download (IBD) process all over again to sync the blockchain. It’s a heavy process that can take days and prevents us from using the node or connecting our wallets until the sync is fully complete.
In this post, we’ll look at how we can anticipate this situation and what measures we can take to sync our nodes faster in the future.
⚠️ CLICKBAIT ALERT
The node doesn’t actually sync faster — but you can start using it almost instantly by syncing in the background!
First Ideas
There are a few ways to speed up the IBD process by sacrificing security, i.e., skipping full verification of the blockchain. Some of these are not recommended, but we’ll cover them here first.
🛠️ assumevalid
The assumevalid
parameter in Bitcoin Core allows you to specify a block height up to which you don’t want transactions to be verified — assuming they are valid. This simplifies the process to only verifying that the blocks are constructed correctly. Using this parameter during initial sync greatly speeds up the process, since the main bottleneck is script verification.
However, with assumevalid
you still need to download the entire blockchain to use the node. Yes, it’ll be faster, but you still need the full download to reach the current state.
💡 Info:
Bitcoin Core has assumevalid
enabled by default, pointing to a block prior to the latest release.
📦 Copying the Blockchain
Another way to speed up IBD is to copy files from one node to another. Specifically, you’d copy the contents of the blocks/
and chainstate/
directories.
However, this method has downsides. First, downloading files from an untrusted source is unsafe. If you do this, make sure it’s from a node you control. There could also be incompatibilities between node versions if the file formats differ.
We’ll discard this option because maintaining redundant full copies of the blockchain isn’t an optimal solution.
So, how do we do it? 🤔
Let’s imagine we could store a checkpoint that represents the state of the chain at a given block height. What information would it need?
To verify and spend bitcoins, we need to know which transactions remain unspent. Who keeps track of this? The UTXO set.
The UTXO set is a database representing the current state of all unspent transaction outputs (UTXOs). With the UTXO set, we can verify whether a transaction is valid without scanning the blockchain, because transaction inputs must consume UTXOs and create new ones. In simpler terms, the UTXO set tells us where all the bitcoins are at a given moment.
How to Use the UTXO Set to Sync a Node
If we have the current state of the chain — meaning the coins (UTXOs) available to be spent — we can send and verify transactions without issue. This works because ALL spendable bitcoins are represented in the UTXO set.
So, if we only need the UTXO set to operate and verify transactions, could we simply load a recent UTXO set snapshot, start using Bitcoin immediately, and download the previous blocks in the background?
Yes!
The older blocks can be downloaded in the background without affecting the ability of wallets to show balances or send funds.
💡 Info:
This is why “pruned nodes” can still verify all relevant information — they only need the UTXO set!
🔄 Loading a Copy of the UTXO Set
Let’s say we have a backup of the UTXO set in a file called utxo.dat
. This snapshot was taken at block 840,000, so when we import it, our node will immediately jump to that height.
To do this, we can use the loadtxoutset
command introduced in Bitcoin Core v26.0.
Let’s start from scratch. We launch the Bitcoin node by running the usual bitcoind
binary. Initially, it will perform a pre-synchronization of headers, followed by full header synchronization.
We must wait for the header sync to complete.
2025-02-18T10:53:47Z Pre-synchronizing blockheaders, height: 10000 (~1.18%)
2025-02-18T10:53:48Z Pre-synchronizing blockheaders, height: 12000 (~1.42%)
2025-02-18T10:53:49Z Pre-synchronizing blockheaders, height: 14000 (~1.66%)
2025-02-18T10:53:50Z Pre-synchronizing blockheaders, height: 16000 (~1.90%)
2025-02-18T10:53:50Z Pre-synchronizing blockheaders, height: 18000 (~2.14%)
2025-02-18T10:53:51Z Pre-synchronizing blockheaders, height: 20000 (~2.38%)
2025-02-18T10:53:52Z Pre-synchronizing blockheaders, height: 22000 (~2.63%)
2025-02-18T10:53:53Z Pre-synchronizing blockheaders, height: 24000 (~2.88%)
2025-02-18T10:53:53Z Pre-synchronizing blockheaders, height: 26000 (~3.13%)
2025-02-18T10:53:54Z Pre-synchronizing blockheaders, height: 28000 (~3.38%)
2025-02-18T10:53:55Z Pre-synchronizing blockheaders, height: 30000 (~3.62%)
2025-02-18T10:53:56Z Pre-synchronizing blockheaders, height: 32000 (~3.86%)
Synchronizing:
2025-02-18T11:07:39Z Synchronizing blockheaders, height: 59379 (~7.13%)
2025-02-18T11:07:40Z Synchronizing blockheaders, height: 61379 (~7.37%)
2025-02-18T11:07:42Z Synchronizing blockheaders, height: 63379 (~7.60%)
2025-02-18T11:07:43Z Synchronizing blockheaders, height: 65379 (~7.84%)
2025-02-18T11:07:46Z Synchronizing blockheaders, height: 67379 (~8.07%)
2025-02-18T11:07:48Z Synchronizing blockheaders, height: 69379 (~8.30%)
2025-02-18T11:07:49Z Synchronizing blockheaders, height: 71379 (~8.53%)
2025-02-18T11:07:54Z Synchronizing blockheaders, height: 73379 (~8.76%)
2025-02-18T11:07:56Z Synchronizing blockheaders, height: 75379 (~9.00%)
2025-02-18T11:07:57Z Synchronizing blockheaders, height: 77379 (~9.23%)
2025-02-18T11:07:59Z Synchronizing blockheaders, height: 79379 (~9.47%)
2025-02-18T11:08:01Z Synchronizing blockheaders, height: 81379 (~9.70%)
Once the headers are synchronized, if we run bitcoin-cli getblockchaininfo
, we’ll see that the node starts syncing the blocks as usual:
$ bitcoin-cli getblockchaininfo
{
"chain": "main",
"blocks": 24503,
"headers": 884325,
...
"verificationprogress": 2.110628750476074e-05,
"initialblockdownload": true,
"chainwork": "00000000000000000000000000000000000000000000000000005fb85fb85fb8",
"size_on_disk": 6748282,
"pruned": false,
}
Once the node is up and running and syncing, we can import the UTXO set using the command:
bitcoin-cli -rpcclienttimeout=0 loadtxoutset utxo.dat
Where utxo.dat
is our backup of the UTXO set.
(💡 It’s recommended to place the utxo.dat
file inside the directory where the node stores its data — by default, this is ~/.bitcoin
.)
This command improves performance by pausing peer-to-peer network communication.
You’ll need to re-enable it after the UTXO set has been loaded.
(Note: This step is optional — if you skip it, the process will still work, but with lower performance.)
⚠️ Warning
It’s important to specify-rpcclienttimeout=0
to prevent the connection from timing out, as the loading process can take several minutes.
🕓 Is It Normal for the Terminal to Hang Like This?
Yes, it’s completely normal for the terminal to appear frozen or “stuck” with no output when running:
While the command appears to hang, you can check the
bitcoind
logs to confirm that the node is still syncing normally in the background. Eventually, you’ll see log entries indicating that it is loading the coins from the UTXO set.
Once the process is complete, the RPC command will confirm that the data has been successfully imported. You’ll see an output similar to:
bitcoin-cli -rpcclienttimeout=0 loadtxoutset utxo.dat
{
"coins_loaded": 176948713,
"tip_hash": "0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5",
"base_height": 840000,
"path": "/home/sliv3r/Escritorio/blockchain/utxo.dat"
}
ChatGPT Plus
If we check the bitcoind
logs, we’ll see that it has updated the chainstate (the current state of the chain) to block 840,000.
2025-02-18T14:55:18Z UpdateTip: new best=000000000000085b8326215f4334716986036cd7f22b474b4bb1d30e44abc7c6 height=190942 version=0x00000001 log2_work=68.448132 tx=5332360 date='2012-07-27T01:22:01Z' progress=0.004564 cache=264.9MiB(2106915txo)
2025-02-18T14:55:18Z [snapshot] validated snapshot (0 MB)
2025-02-18T14:55:18Z [snapshot] successfully activated snapshot 0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5
2025-02-18T14:55:18Z [snapshot] (0 MB)
2025-02-18T14:55:18Z Opening LevelDB in /home/sliv3r/Escritorio/blockchain/chainstate
2025-02-18T14:55:18Z Opened LevelDB successfully
2025-02-18T14:55:18Z Using obfuscation key for /home/sliv3r/Escritorio/blockchain/chainstate: d06729b36c1332a2
2025-02-18T14:55:18Z [Chainstate [ibd] @ height 190942 (000000000000085b8326215f4334716986036cd7f22b474b4bb1d30e44abc7c6)] resized coinsdb cache to 0.4 MiB
2025-02-18T14:55:18Z [Chainstate [ibd] @ height 190942 (000000000000085b8326215f4334716986036cd7f22b474b4bb1d30e44abc7c6)] resized coinstip cache to 22.0 MiB
2025-02-18T14:55:18Z Cache size (277731616) exceeds total space (23068672)
2025-02-18T14:55:22Z Opening LevelDB in /home/sliv3r/Escritorio/blockchain/chainstate_snapshot
2025-02-18T14:55:22Z Opened LevelDB successfully
2025-02-18T14:55:22Z Using obfuscation key for /home/sliv3r/Escritorio/blockchain/chainstate_snapshot: ffaa87a9790b88bb
2025-02-18T14:55:22Z [Chainstate [snapshot] @ height 840000 (0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5)] resized coinsdb cache to 7.6 MiB
2025-02-18T14:55:22Z [Chainstate [snapshot] @ height 840000 (0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5)] resized coinstip cache to 418.0 MiB
And if we run the command, bitcoin-cli getblockchaininfo
we’ll see that the node is indeed synchronized up to block 840,000, confirming that the UTXO set has been successfully imported and the node is ready for use:
bitcoin-cli getblockchaininfo
{
"chain": "main",
"blocks": 840077,
"headers": 884327,
"bestblockhash": "000000000000000000018bd4948d21337c98ea27cd5586393a31c644ce9ab1ce",
"bits": "17034219",
"target": "0000000000000000000342190000000000000000000000000000000000000000",
"difficulty": 86388558925171.02,
"time": 1713621243,
"mediantime": 1713616671,
"verificationprogress": 0.8484856035034852,
"initialblockdownload": true,
"chainwork": "000000000000000000000000000000000000000075537cab1a1b8a216234e13a",
"size_on_disk": 3395515205,
"pruned": false,
"warnings": [
"This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"
]
}
If you previously disabled the P2P network, re-enable it with bitcoin-cli setnetworkactive true
. Then, by checking the bitcoind
logs again, you’ll see that the node is now syncing the blocks between block 840,000 and the tip of the chain, completing the background synchronization process:
2025-02-18T14:58:36Z UpdateTip: new best=00000000000000000001caffc952e7de3b1c8b2f2d044e8e69955000ca6fb9e4 height=840436 version=0x20e00000 log2_work=94.879663 tx=993054314 date='2024-04-23T01:53:38Z' progress=0.849873 cache=458.5MiB(2943813txo)
2025-02-18T14:58:36Z UpdateTip: new best=000000000000000000014797e1aa0867732005d6cb11fad392e35fbc81f552bd height=840437 version=0x20000000 log2_work=94.879678 tx=993060398 date='2024-04-23T02:05:34Z' progress=0.849878 cache=459.1MiB(2947040txo)
2025-02-18T14:58:36Z UpdateTip: new best=00000000000000000002a0658cf7d5af3fadb3469df23e97de849a32991c0545 height=840438 version=0x3fffe000 log2_work=94.879693 tx=993066120 date='2024-04-23T02:13:23Z' progress=0.849883 cache=459.8MiB(2951620txo)
2025-02-18T14:58:36Z UpdateTip: new best=00000000000000000002349473de1b8f66f12b65cdeb870c211927865c856e41 height=840439 version=0x3fff0000 log2_work=94.879707 tx=993071846 date='2024-04-23T02:16:31Z' progress=0.849888 cache=460.5MiB(2956234txo)
2025-02-18T14:58:36Z UpdateTip: new best=00000000000000000000c6b9fd50913cf6d3d34536c06c1a029ba6c844cc8033 height=840440 version=0x20a00000 log2_work=94.879722 tx=993077310 date='2024-04-23T02:17:04Z' progress=0.849893 cache=461.2MiB(2961405txo)
2025-02-18T14:58:36Z UpdateTip: new best=000000000000000000033c2f66f551e44aea785f91a902b321c38703a158ed3a height=840441 version=0x20a00000 log2_work=94.879737 tx=993083167 date='2024-04-23T02:20:03Z' progress=0.849898 cache=461.9MiB(2965605txo)
2025-02-18T14
💡 The term “tip” refers to the current block at the end of the blockchain — that is, the latest block with the most accumulated proof of work.
Once the node finishes syncing from block 840,000 to the current tip — for example, block 884,365 — you’ll see that it begins to re-sync the entire chain from scratch in the background.
This process is easy to identify in the logs, as it’s labeled as background validation.
2025-02-18T20:18:41Z [background validation] UpdateTip: new best=000000000000001db5a1515a5f8534c941b1628f60466e6b709b3b320254afff height=208000 version=0x00000001 log2_work=69.023342 tx=8883319 date='2012-11-15T05:17:32Z' progress=0.007602 cache=58.2MiB(460376txo)
2025-02-18T20:19:04Z [background validation] UpdateTip: new best=000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e height=210000 version=0x00000002 log2_work=69.091539 tx=9344662 date='2012-11-28T15:24:38Z' progress=0.007996 cache=94.6MiB(728202txo)
2025-02-18T20:19:40Z [background validation] UpdateTip: new best=00000000000003d906e4131c39f7655b72df40146d2967f5d75113a09610de61 height=212000 version=0x00000002 log2_work=69.157547 tx=9819223 date='2012-12-13T01:33:36Z' progress=0.008403 cache=117.6MiB(927414txo)
2025-02-18T20:19:41Z Saw new header hash=000000000000000000019940b918fed6453c7afa80e03f1d837d1fdd8b31a8b9 height=884366
2025-02-18T20:19:45Z UpdateTip: new best=000000000000000000019940b918fed6453c7afa80e03f1d837d1fdd8b31a8b9 height=884366 version=0x2002a000 log2_work=95.452620 tx=1156361979 date='2025-02-18T20:19:40Z' progress=1.000000 cache=2.7MiB(19061txo)
Looking at the previous logs, we can see that even though the node is verifying very old blocks, such as block 210,000, when a new block appears — block 884,366 in this case — it immediately updates the tip of its chain and then continues verifying the older blocks.
How to create or obtain a backup of the UTXO set?
The easiest way to get a backup of the UTXO set is with the magnet link: magnet:?xt=urn:btih:559bd78170502971e15e97d7572e4c824f033492&dn=utxo-880000.dat&tr=udp%3A%2F%2Ftracker.bitcoin.sprovoost.nl%3A6969.
⚠️ Alerta. Aquest magnet link és pel bloc 800.000
As I said previously, you should avoid downloading the UTXO set from unknown sources (you can’t trust me!), but well, there are different mechanisms to verify that the copy provided by this magnet link is correct:
- To start with, here is the link provided in the PR Bitcoin Core that introduces the parameters to the code.
- You can validate the hash of the copy with: shasum -a 256 utxo-880000.dat the result should be 43b3b1ad6e1005ffc0ff49514d0ffcc3e3ce671cc8d02da7fa7bac5405f89de4.
How do we generate our own backup?
To do this we need a node already synchronized to a block height higher than that of the backup. In this case higher than 880.000.
⚠️ Warning
The node cannot be pruned to a height lower than that of the block on which the backup is made!
Using the command bitcoin-cli -rpcclienttimeout=0 dumptxoutset utxo.dat rollback. By default this command will make a backup of the UTXO set to the ~.bitcoin directory. This process may take a while, as the node “desynchronizes” until it reaches the last valid checkpoint block.
Here we can watch a video of how a node is desynchronized when doing this process.
Again, you can validate that the backup is correct with the file hash:
shasum -a 256 utxo-880000.dat
43b3b1ad6e1005ffc0ff49514d0ffcc3e3ce671cc8d02da7fa7bac5405f89de4
Conclusions and precautions
With this process the node reaches a state where it is usable to check the balance and create transactions much faster than synchronizing the entire chain. On a normal laptop, this whole process has not taken me more than 2 hours which is already a big improvement compared to the days that a normal synchronization can take.
⚠️ Warning
It is very important not to download the UTXO set from an unknown and untrusted source. The danger is very high, as a false representation of the UTXO set can make you believe that you have been paid when it is not true. You can be deceived and robbed.
Bitcoin Core attempts to prevent this by limiting the import of the UTXO set to a block defined in the latest release (block 880,000 as of this post) and checking that the UTXO set is the correct one and has not been tampered with. Skipping this check is not difficult and allows more experienced users to speed up this process even more. This post will not explain how to do this in order not to expose readers to a possible attack.
⚠️ Warning
When the first part of the post was written, the block of the last release was 840,000 and not 880,000, which is why the guide is based on 840,000.
So, enjoy synchronizing nodes at the speed of light!!

AND REMEMBER —> DON’T TRUST, VERIFY. IF YOU DON’T RUN YOUR OWN NODE YOU ARE NOT USING BITCOIN WITHOUT A “TRUSTED” MIDLEMAN
© 2025 Pol Espinasa · Powered by Hugo & PaperMod