How I Restored My Entire PostgreSQL Database After Reinstalling Windows
After nuking Windows and reinstalling everything from scratch, I thought I lost all my Docker containers, PostgreSQL data, and volumes.
All I had was a backup of my old Docker folder:
C:\Users\<me>\AppData\Local\Docker
When I restored Docker Desktop, my containers and images were gone — but my data wasn’t lost.
Here’s the complete journey of how I restored everything: Docker volumes, PostgreSQL, user credentials, and database compatibility.
1. Understanding Where Docker Stores Data
Docker Desktop on Windows uses WSL2 internally.
All your images, volumes, and containers live inside a single virtual disk file:
C:\Users\<me>\AppData\Local\Docker\wsl\disk\docker_data.vhdx
This .vhdx
file is everything.
If you’ve backed it up, you have all your Docker volumes, including PostgreSQL data.
2. Restoring Docker Data After Reinstalling Windows
Step 1 — Reinstall Docker Desktop
- Download the same or newer Docker Desktop version.
- Launch it once so it initializes the WSL distros (
docker-desktop
anddocker-desktop-data
).
Step 2 — Replace the VHDX
- Quit Docker Desktop completely.
- Open PowerShell and stop WSL:
wsl --shutdown
- Go to:
C:\Users\<me>\AppData\Local\Docker\wsl\disk\
- Rename the new
docker_data.vhdx
→docker_data.vhdx.bak
. - Paste your old backed-up VHDX here.
Step 3 — Restart Docker
Start-Process "C:\Program Files\Docker\Docker\Docker Desktop.exe"
Now, all your volumes are back!
You can confirm with:
docker volume ls
3. Restoring PostgreSQL From a Docker Volume
After restoring, I saw all my volumes:
local computer-use-agent-backend_data
local 33d42ed1502e9388e...5df5790b25c
local 67c1d2f03871edded...7383e77c83f
local telegram-bot_bot-data
...
But docker images
showed nothing.
That’s normal — images are not auto-restored, but volumes do come back.
So, we just need to run a new Postgres container and point it at the existing volume. But how to know which volume belongs to PostgreSQL?
Step 1 - Locating the PostgreSQL volume
We can look for volume sizes and guess which volume is related to PostgreSQL, in order to see the volume sizes, we need to execute this command:
docker system df -v
From the output, search for volume information, it will look something like this:
Local Volumes space usage: VOLUME NAME LINKS SIZE 67c1d2f03871eddeda...383e77c83f 0 9240B 33d42ed1502e9388e3...df5790b25c 0 802.5MB db4de6a5346e99bfc7...efe05b0f23 0 40.39MB telegram-bot_bot-data 0 78.44MB ...
We can see that volume starting with 33d42
is bigger in size, so it can be our PostgreSQL volume. By listing volumes with their sizes, we can rule out the small volumes…
Next, we will inspect our volumes one by one and look for PostgreSQL related files in them:
docker run --rm -it -v <volume_name>:/data alpine sh -c "ls -lh /data | head"
We stop when we see this kind of output:
-rw------- 1 999 ping 3 Jan 9 2025 PG_VERSION
drwx------ 11 999 ping 4.0K May 1 05:48 base
drwx------ 2 999 ping 4.0K Aug 21 08:54 global
drwx------ 2 999 ping 4.0K Jan 9 2025 pg_commit_ts
drwx------ 2 999 ping 4.0K Jan 9 2025 pg_dynshmem
-rw------- 1 999 ping 5.6K Jan 9 2025 pg_hba.conf
-rw------- 1 999 ping 2.6K Jan 9 2025 pg_ident.conf
drwx------ 4 999 ping 4.0K Aug 21 08:59 pg_logical
drwx------ 4 999 ping 4.0K Jan 9 2025 pg_multixact
We save the volume name to session variable $VOLUME
for reuse:
$VOLUME = "33d42ed1502e9388e335005311308097787d8595f54c8784ba7c75df5790b25c"
Step 2 — Check Your PostgreSQL Version
First, find out which major version of Postgres your volume used:
docker run --rm -v ${VOLUME}:/var/lib/postgresql/data alpine sh -c "cat /var/lib/postgresql/data/PG_VERSION"
For me, it returned 16
, so I needed the postgres:16
image.
Step 3 — Pull the Matching Postgres Image
docker pull postgres:16
Step 4 — Run Postgres Using the Existing Volume
docker run -d ` --name "pg-restored" ` -e POSTGRES_PASSWORD=changeme ` -v ${VOLUME}:/var/lib/postgresql/data ` -p 5432:5432 ` postgres:16
Step 5 — Verify
docker logs -f pg-restored
Look for:
database system is ready to accept connections
Your Postgres DBs are now back ✅.
5. Extra Tips & Troubleshooting
Fixing Collation Version Mismatch
After starting Postgres, I saw this warning:
WARNING: database "postgres" has a collation version mismatch
DETAIL: The database was created using collation version 2.36,
but the operating system provides version 2.41.
This happens because the glibc version changed between systems.
In order to fix it we need to execute these commands on affected DBs:
REINDEX (VERBOSE) DATABASE <db-name>; ALTER DATABASE <db-name> REFRESH COLLATION VERSION;
I had multiple DBs affected, so I wrote PowerShell loop to fix them all:
# Set container name $C = 'pg-restored' # Set the target version to ignore DBs in that version $target = '2.41'; # List DBs whose datcollversion differs (and isn’t NULL) $DBS = (docker exec -i $C psql -U postgres -t -A -c "SELECT datname FROM pg_database WHERE datcollversion IS NOT NULL AND datcollversion <> '$target';") -split "`n" | ForEach-Object { $_.Trim() } | Where-Object { $_ } foreach ($db in $DBS) { Write-Host "== Reindexing and refreshing $db ==" docker exec -it $C psql -U postgres -d $db -c "REINDEX (VERBOSE) DATABASE $db;" docker exec -it $C psql -U postgres -c "ALTER DATABASE $db REFRESH COLLATION VERSION;" } # (Optionally you can try refreshing template0, although it may fail) # Write-Host "== Refreshing template0 ==" # docker exec -it $C psql -U postgres -c "ALTER DATABASE template0 REFRESH COLLATION VERSION;" # Verify the refresh docker exec -it $C psql -U postgres -c "SELECT datname, datcollate, datctype, datcollversion FROM pg_database ORDER BY 1;"
Resetting Forgotten PostgreSQL Passwords
I couldn’t remember my DB passwords. Postgres doesn’t store plaintext passwords, so the only solution is to reset them.
Step 1 — List Users
docker exec -it pg-restored psql -U postgres -c "\du+"
Step 2 — Reset a User’s Password
docker exec -it pg-restored psql -U postgres -c "ALTER ROLE <user-name> WITH PASSWORD 'NewStrongPassword!';"
If You’re Locked Out
If postgres
requires a password you don’t know, log in as the container’s OS-level postgres
user:
docker exec -it -u postgres pg-restored psql
From there, you can reset any password.
Changing Database Owners
To change the owner of a single DB:
docker exec -it pg-restored psql -U postgres -c "ALTER DATABASE <db-name> OWNER TO <user-name>;"
Generating PostgreSQL Connection URLs
Standard format:
postgresql://<user>:<password>@<host>:<port>/<database>
Example:
postgresql://postgres%27:1234@localhost:5432/test
Here, the username was
postgres'
— we URL-encoded'
→%27
.
Final Lessons Learned
- Back up
docker_data.vhdx
— it contains all your Docker volumes. - Postgres data lives entirely inside the volume — images are replaceable.
- Always match the original Postgres version (
PG_VERSION
). - If you get collation mismatch warnings, reindex + refresh.
- If you forget passwords, reset them — they’re not recoverable.
- Recreate connection URLs properly — encode special characters.
Final Words
This whole process taught me one thing: Docker volumes are king.
Even if your images, containers, and Windows installation are gone, as long as you have your docker_data.vhdx
backup, your PostgreSQL databases are safe.