Verify an od-db-backup cold backup by restoring it into a throwaway, network-isolated Hyper-V VM (revert to checkpoint after), never over live prod. Covers matching versions from MANIFEST, whole-datadir restore for InnoDB, re-pointing the image path, and a pass/fail health checklist (connects, Help>About version, Database Maintenance Check, recent data, images open, no missing tables, optional mysqlcheck). Cites Open Dental Backups / Manual Backups / Database Maintenance. Cross-linked with od-db-backup.md; README updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
11 KiB
Runbook: Open Dental — Rock-solid database + images backup (Windows)
Applies to: The Windows machine hosting the Open Dental MySQL/MariaDB database (the "server"). Goal: A fully consistent, restorable backup of both halves of an Open Dental practice — the database and the A-to-Z images folder — following Open Dental and MySQL/MariaDB best practice, including cleanly stopping the database during the copy and restarting it after.
What a complete Open Dental backup is (and isn't)
Per Open Dental's own docs, a full backup is two things — the database and the A-to-Z images folder — and both must be captured together. A database backup without the images folder (or vice-versa) is not a usable restore.
| Component | Default location | Back up? | Holds |
|---|---|---|---|
| MySQL/MariaDB data directory | C:\mysql\data\ (contains the opendental DB) |
Yes — required | All clinical/financial data |
| A-to-Z / OpenDentImages folder | C:\OpenDentImages\ |
Yes — required | Scanned docs, images, attachments |
FreeDentalConfig.xml |
Open Dental install dir | Nice-to-have | Connection / Choose-Database / AtoZ path config |
| Open Dental program files | …\Program Files\Open Dental\ |
No — reinstall | The application itself |
| Other office documents | (varies) | Per your policy | Non-Open-Dental files |
There is no separate "application backup." Open Dental does not back up the program itself — on restore you reinstall the matching Open Dental version (and matching MySQL/MariaDB) and point it at the restored data. So the two "recovery aids" worth grabbing are just: a copy of FreeDentalConfig.xml and a record of the exact versions to reinstall. The script captures both automatically into the backup folder (FreeDentalConfig.xml + MANIFEST.txt) — nothing extra to run.
Placeholders:
| Placeholder | Meaning |
|---|---|
<DB_SERVICE> |
Name of the MySQL/MariaDB Windows service (e.g. MySQL, MySQL57, MariaDB) |
<DATA_DIR> |
MySQL data directory — the folder containing the opendental subfolder (commonly C:\mysql\data) |
<IMAGES_DIR> |
A-to-Z images folder (commonly C:\OpenDentImages) |
<DEST> |
Backup destination — separate physical disk or UNC path, ideally replicated off-site |
<DB_USER> / <DB_PASSWORD> |
A MySQL account for mysqldump (supplemental method) — from the password manager, never committed |
Why "cold copy" is the rock-solid method
Open Dental databases run on MyISAM or InnoDB. For a file-level backup to be consistent:
- A hot copy (copying the data directory while the service runs) can capture InnoDB mid-write → corrupt, unrestorable backup. Open Dental's built-in Backup tool and most "online" file backups cannot even restore InnoDB.
- A cold copy — stop the service so it flushes and closes cleanly, copy, restart — is consistent for both engines. This is the gold-standard local backup.
Two details that make or break a cold copy:
- Copy the entire data directory, not just
data\opendental\. InnoDB's shared tablespace and redo logs (ibdata1,ib_logfile*) live at the root of the data directory. Copy only theopendentalsubfolder and an InnoDB restore will fail. - Verify the service actually stopped before copying. If it won't stop, do not copy — you'd capture a live datadir.
The script below does both, and always restarts the service (even if the copy fails), so the practice is never left down.
1. Pre-flight
- Run on the database server, in an elevated PowerShell, off-hours — the copy causes downtime; Open Dental is unavailable on every workstation while the service is stopped.
- Make sure no one is in Open Dental (fully closed on all workstations).
- Have a
<DEST>on a different physical disk (or UNC share) with enough free space for the data directory + images.
2. Run the cold backup (primary method)
From an elevated PowerShell on the server:
irm https://gitea.ivangodwin.com/ops/rb/raw/branch/main/scripts/od-db-backup.ps1 | iex
It will:
- Auto-detect the
<DB_SERVICE>,<DATA_DIR>, and<IMAGES_DIR>(prompting to confirm/override). - Report sizes and destination free space, then confirm before doing anything.
- Stop
<DB_SERVICE>and verify it reached Stopped — aborting the copy if it doesn't. robocopythe whole data directory to<DEST>\od-backup-<timestamp>\data, then the images to…\OpenDentImages.- Restart
<DB_SERVICE>in afinallyblock — this runs even if the copy fails or is interrupted. - Capture recovery aids (best-effort, outside the downtime window): a copy of
FreeDentalConfig.xmland the exact Open Dental + MySQL/MariaDB versions. - Write
MANIFEST.txtandbackup.loginto the backup folder.
Manual equivalent (offline / if you can't fetch the script)
Elevated PowerShell, no one in Open Dental:
net stop <DB_SERVICE>
Get-Service <DB_SERVICE> # confirm Status = Stopped BEFORE copying
robocopy "<DATA_DIR>" "<DEST>\od-backup\data" /E /COPY:DAT /R:2 /W:5
robocopy "<IMAGES_DIR>" "<DEST>\od-backup\OpenDentImages" /E /COPY:DAT /R:2 /W:5
net start <DB_SERVICE>
Get-Service <DB_SERVICE> # confirm Status = Running
Copy
<DATA_DIR>itself (the parent ofopendental), not<DATA_DIR>\opendental— you needibdata1/ib_logfile*at the datadir root for InnoDB. Ifnet startfails, start it immediately: the practice cannot work until the DB is back up.
3. Supplemental logical backup (mysqldump)
A cold copy is engine-perfect but physical — a portable logical dump is a valuable second line (survives binary corruption, restores to any server/version). It runs while the service is up, so schedule it separately (e.g. mid-day incremental in addition to the nightly cold copy). Slight slowness while it runs.
mysqldump -u <DB_USER> -p --single-transaction --quick --max-allowed-packet=1024M --default-character-set=utf8 --routines --events opendental > "<DEST>\opendental-<date>.sql"
--single-transactiongives a consistent snapshot of InnoDB without locking the practice out. (For MyISAM, that flag does not guarantee consistency — use the cold copy as the source of truth.)- Compress the
.sqlafterward; it shrinks dramatically. - The dump does not include the images folder — always pair it with an
<IMAGES_DIR>copy.
4. Schedule it (daily minimum)
Open Dental's floor is at least one backup per day; combine an automated nightly job with an off-site copy.
Register the cold backup as a nightly Task Scheduler job (runs off-hours, as SYSTEM/admin). Because irm | iex prompts interactively, schedule a local copy of the script with the paths baked in (keep that filled-in copy in the private tier, not here), e.g.:
schtasks /Create /TN "OD Nightly Cold Backup" /TR "powershell -NoProfile -ExecutionPolicy Bypass -File C:\ops\od-db-backup-local.ps1" /SC DAILY /ST 23:30 /RU SYSTEM /RL HIGHEST
5. Verify — a backup you haven't restored is a guess
- Test-restore to an isolated machine periodically (matching Open Dental + MySQL/MariaDB versions):
net stop, rename the existingopendentalfolder aside, drop in the backup'sdata\contents,net start, launch Open Dental, spot-check patients/images. Full step-by-step with a health checklist:od-backup-verify.md(isolated Hyper-V test-restore). - Never restore over a live production database — data loss is irreversible.
- Confirm the nightly job is actually producing dated folders and that they leave the building (off-site / immutable copy) — ransomware that reaches the server will reach on-line backups too.
6. Retention & off-site (3-2-1)
- 3 copies, 2 media, 1 off-site. The
<DEST>disk alone is not a backup strategy. - Keep several daily generations plus weekly/monthly rollups; prune old
od-backup-<timestamp>folders on a schedule. - These files contain PHI — encrypt at rest and in transit; restrict access. HIPAA applies.
Point off-site tools at <DEST>, not at the live database
Any off-site/cloud replication (Duplicati → Backblaze B2, Veeam, rclone, Wasabi, etc.) must use <DEST> as its source — it backs up this backup. It must not re-run the stop/copy against the live <DATA_DIR>.
- The
od-backup-<timestamp>folders under<DEST>are already a consistent, cold copy produced with the service cleanly stopped. Copying them off-site is a safe file copy — no service stop, no downtime, no consistency risk. - Never let a naive file-sync tool crawl the live
C:\mysql\datadirectly. A hot copy of a running InnoDB datadir is corrupt and unrestorable — the exact failure this runbook exists to avoid. Off-site tools have no idea they need to stop the service first; that's this script's job, done once, up front. - Sequence: this cold backup runs first (nightly, off-hours) → the off-site job runs after it completes, sourcing
<DEST>. Stagger the schedules (e.g. cold backup 23:30, off-site upload 01:00) so the upload never reads a half-writtenod-backup-<timestamp>folder. If your tool supports it, exclude any in-progress/partial folder or upload only completed timestamps. - Let the off-site tool own its own encryption + retention on top — B2/Wasabi object lock or Duplicati's immutability/versioning gives you the ransomware-resistant, off-site copy of the 3-2-1 rule.
Security note
Backup media and dumps hold the entire practice's PHI. Treat them as the crown jewels: encrypt the destination, lock down share permissions, and keep at least one copy off-line/immutable so ransomware can't encrypt your backups along with production. Don't store <DB_PASSWORD> in the scheduled command line — use a MySQL option file / limited account.
Note that the captured FreeDentalConfig.xml stores the MySQL password obfuscated but reversible on direct-connection setups. The backup already contains all PHI so this doesn't change the requirement — but it's one more reason the destination must be encrypted and access-controlled. (Middle Tier setups don't put DB credentials in that file — see od-cfg-persist.md.)
References
- Open Dental manual — Backups (overview): https://opendental.com/manual/backups.html
- Open Dental manual — Manual Backups: https://www.opendental.com/manual/backupsmanual.html
- Open Dental manual — Backup Tool: https://www.opendental.com/manual/backuptool.html
- Open Dental manual — FreeDentalConfig.xml: https://www.opendental.com/manual/freedentalconfig.html
- Open Dental — InnoDB (backup implications): https://www.opendental.com/site/mysqlinnodb.html
- Open Dental manual — MySQL Data Directory Management: https://opendental.com/manual/mysqlmanage.html
- MySQL —
mysqldump,--single-transaction: https://dev.mysql.com/doc/refman/en/mysqldump.html