Skip to content

How to Resolve H2 Database Corruption When Using File Copy Backup

Problem

I tried to back up my H2 database by copying the .mv.db file directly while my application was running. It seemed to work at first, but when I tried to restore from that backup, I got corruption errors. Some queries returned wrong results, and sometimes the database wouldn’t even open.

What I Tried

At first, I thought copying the database file would be the simplest backup solution:

UnsafeBackup.java
// My original approach - copying files directly
Files.copy(
Paths.get("./data/mydb.mv.db"),
Paths.get("./backup/mydb-backup.mv.db")
);

This approach had problems. When I ran this while my app was active, the backup file sometimes wouldn’t open. Other times it opened but contained corrupted data - rows with partial values, tables that didn’t match what I expected.

I tried stopping my application before copying the files. That worked better, but I had to schedule downtime for every backup. Not ideal for a production system.

Why File Copy Is Unsafe

The issue is that file copy operations don’t know anything about the database’s internal state. When I copy a file at the filesystem level, I might catch the database in the middle of an operation:

What happens during file copy
Database is updating page 5...
File copy reads page 5 (partial update in progress)
Database finishes updating page 5
Database updates page 6
File copy reads page 6 (completed update)

The backup file ends up with inconsistent data - some pages from before an update, some from after. This creates internal mismatches that the database engine can’t fix.

There’s another problem too. H2 database file formats can change between versions. I once upgraded H2 and found my file-based backup from the previous version wouldn’t open with the new version.

The Solution

H2 provides a BACKUP TO command that handles all these issues. It coordinates with the database engine to ensure a consistent backup:

SafeBackup.java
// Safe approach - use BACKUP TO command
try (Connection con = DriverManager.getConnection(
"jdbc:h2:./data/mydb", "user", "password");
PreparedStatement ps = con.prepareStatement(
"BACKUP TO './backup/mydb-backup.zip'")) {
ps.executeUpdate();
}

The BACKUP TO command:

  • Coordinates with the database engine to ensure consistency
  • Creates a compressed backup file
  • Works while the database is online
  • Handles version compatibility

For offline scenarios where I can stop the database, I can safely copy files after a clean shutdown:

OfflineBackup.java
// Safe offline approach - shutdown then copy
try (Connection con = DriverManager.getConnection(
"jdbc:h2:./data/mydb", "user", "password")) {
con.createStatement().execute("SHUTDOWN");
}
// Now safe to copy files
Files.copy(
Paths.get("./data/mydb.mv.db"),
Paths.get("./backup/mydb-backup.mv.db")
);

Summary

Don’t copy H2 database files while the database is running. The file copy can catch pages mid-update, leading to corruption. Instead, use the BACKUP TO command for online backups, or shut down the database cleanly before copying files for offline backups.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments