Files
rb/od-db-backup.md
T
igodwin 25a5127d53 od-db-backup: clarify full backup = DB + images (app is reinstalled), capture config + versions
Per Open Dental docs a complete backup is the database + A-to-Z images; the
program itself is not backed up but reinstalled at the matching version on
restore. Extend the script to capture two recovery aids best-effort (outside
the downtime window): a copy of FreeDentalConfig.xml and the exact Open Dental
+ MySQL/MariaDB versions, recorded in MANIFEST.txt. Runbook now spells out
what is/isn't backed up and notes the config file holds the obfuscated DB
password on direct-connect setups.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 23:21:17 -07:00

9.4 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:

  1. 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 the opendental subfolder and an InnoDB restore will fail.
  2. 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:

  1. Auto-detect the <DB_SERVICE>, <DATA_DIR>, and <IMAGES_DIR> (prompting to confirm/override).
  2. Report sizes and destination free space, then confirm before doing anything.
  3. Stop <DB_SERVICE> and verify it reached Stopped — aborting the copy if it doesn't.
  4. robocopy the whole data directory to <DEST>\od-backup-<timestamp>\data, then the images to …\OpenDentImages.
  5. Restart <DB_SERVICE> in a finally block — this runs even if the copy fails or is interrupted.
  6. Capture recovery aids (best-effort, outside the downtime window): a copy of FreeDentalConfig.xml and the exact Open Dental + MySQL/MariaDB versions.
  7. Write MANIFEST.txt and backup.log into 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 of opendental), not <DATA_DIR>\opendental — you need ibdata1 / ib_logfile* at the datadir root for InnoDB. If net start fails, 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-transaction gives 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 .sql afterward; 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 existing opendental folder aside, drop in the backup's data\ contents, net start, launch Open Dental, spot-check patients/images.
  • 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.

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