EHR Data Migration: A Step-by-Step Guide to Moving Patient Data (2026)
Founder & CEO
15+ years building healthcare technology. Founder & CEO of Nirmitee.io, whose teams deliver EHR integrations, FHIR and HL7 platforms, ABDM and NHCX connectivity, and clinical AI for health systems and healthtech companies.

EHR data migration is the work of moving patient records from an old electronic health record (EHR) into a new one so clinicians can treat patients on day one without opening the old system. Done well, it is a short list of decisions (what moves, in what form, and who signs it off) followed by repeated test loads that are checked until the numbers and the charts match.
Updated October 2026. This is a full guide on health data migration for US providers and digital health companies. It covers the full data migration process from EHR to EHR: the phases, what to migrate and what to archive, how mapping works, the checks that prove the data arrived, cutover and rollback, the special case of Cerner (Oracle Health), the drivers of cost, and a checklist you can use as acceptance criteria. If you would rather hand the work to a team that does it, see our healthcare data migration services. It is part of our EHR software development guide.
What is EHR data migration?
EHR data migration, also called EMR data migration, health data migration or healthcare data migration, is the transfer of clinical, administrative and billing data from a legacy system to a new one, converted into the structure and codes the new system expects. It is different from an interface. An interface keeps two live systems in step every day; a migration is a one-time move (usually in several test rounds and one final load) after which the old system stops being the source of truth.
Most EHR data transfer projects include three kinds of data:
- Discrete data: structured values the new EHR can calculate with, such as allergies, medications, problems, immunizations, lab results and vitals.
- Documents: notes, discharge summaries, scanned PDFs and images that clinicians need to read but the system does not need to compute.
- Administrative and financial data: demographics, insurance, appointments, balances and open claims.
Each kind moves differently, and most of the cost and risk sits in the first one.
The EHR data migration process in six phases
A reliable electronic health record data migration follows six phases: inventory, scope decisions, extraction and mapping, test loads, reconciliation, and cutover. The loop between test loads and reconciliation is where the real work happens; plan for several rounds, not one.
| Phase | What happens | Output you should hold |
|---|---|---|
| 1. Inventory | List every source system (EHR, practice management, lab, imaging, scanned document store), the tables or exports each offers, record counts and the years covered. | Source inventory with volumes |
| 2. Scope decisions | For each data type decide: migrate as discrete data, migrate as documents, archive read-only, or leave. Clinicians and compliance sign each line. | Signed scope matrix |
| 3. Extract and map | Pull data out (FHIR Bulk export, EHI export, database extract or vendor tool) and map fields, codes, units, providers and locations to the target. | Mapping specification and code crosswalks |
| 4. Test loads | Load into a copy (test environment) of the new EHR, never into production first. | Load logs with rejected rows and reasons |
| 5. Reconciliation | Counts, values, codes and clinician chart review. Fix and reload until it passes. | Reconciliation report per round |
| 6. Cutover | Freeze the old system, run the final delta load, go live, keep a rollback path until sign-off, then archive. | Go-live sign-off and archive access |
Before phase 1, it helps to size the job. Our legacy EHR data migration sizing guide shows how to estimate volumes and effort from the source system before anyone quotes a price.
What should you migrate, convert to documents, or archive?
Migrate as discrete data only what a clinician needs to treat the patient in the coming weeks; convert older clinical history into readable documents; archive the rest read-only for the legal retention period. Trying to convert every historical value into discrete data is the most common reason migrations overrun.
| Data type | Usual decision | Why |
|---|---|---|
| Demographics, identifiers, insurance | Migrate, discrete | Every downstream record hangs off the patient |
| Allergies, active medications, active problems | Migrate, discrete | Drive safety checks such as drug-allergy alerts |
| Immunizations | Migrate, discrete (full history) | Needed for schedules and registry reporting |
| Lab results and vitals | Recent period discrete, older as a summary document | Trends need discrete values; old values are read, not graphed |
| Clinical notes, discharge summaries | Migrate as documents | Read by clinicians, rarely computed |
| Scanned documents and images | Migrate as documents, or link to the image archive | Large files; keep links valid |
| Future appointments, open orders, referrals | Migrate, discrete | Missed ones become missed care |
| Closed claims and old balances | Archive, read only | Needed for audits and appeals, not daily work |
| Audit trails and old user accounts | Archive, read only | Legal record; rarely needed in the new EHR |
How long you must keep archived records is set by state law and by payer contracts, not by HIPAA alone, so the archive period is a decision for your compliance lead. The cutoff for "recent" discrete data (for example, the last two years of results) is a clinical decision; there is no single correct number.
How do you get data out of the old EHR?
There are four practical extraction routes, and most projects use more than one.
| Route | What you get | Good for | Watch for |
|---|---|---|---|
FHIR Bulk Data export ($export) | NDJSON files of FHIR resources for a group of patients | Structured clinical data in a standard shape | Covers the US Core data set, not everything in the chart |
| EHI export | The vendor's full electronic health information export, often as tables | Completeness; certified EHRs must offer it | Vendor-specific table layouts that still need mapping |
| Database extract or reporting copy (an EHR database migration) | Raw tables | Volume and history the APIs do not expose | Needs access rights and deep knowledge of the schema |
| Vendor or third-party conversion tool | Pre-built extract and load for known source and target pairs | Common pairs where a tool exists | Coverage gaps for custom fields |
The EHI export route exists because of the ONC certification criterion for electronic health information export (45 CFR 170.315(b)(10)), which requires certified EHRs to export a single patient's EHI and a whole population's EHI. The information blocking rule (45 CFR Part 171) limits how a vendor can refuse or delay access to that data, though it allows reasonable, cost-based fees under defined exceptions. Read your contract's data return clause before you give notice to your current vendor.
How does data portability with FHIR help an EHR migration?
FHIR data portability means patient data can leave an EHR in a published, standard format (FHIR R4 resources, as profiled by US Core) instead of a proprietary dump. For an EHR migration, that gives you a known starting shape for the most important clinical data and a way to pull it in bulk.
- Bulk export: the HL7 FHIR Bulk Data Access specification defines
$exportat system, patient and group level. The server works asynchronously: you start the export, poll a status URL, then download NDJSON files. - Group export for phased moves: exporting by group lets you migrate one clinic or one cohort at a time.
- Certification: ONC-certified EHRs must support the standardized API for patient and population services (170.315(g)(10)), which includes bulk export for a group of patients.
FHIR portability does not remove mapping. The target system may use different code systems, local value sets, provider IDs and location IDs, and notes may arrive as references to documents that still need to be fetched. Treat FHIR as the best available extraction format for core clinical data, not as a migration that runs itself. Our bulk FHIR export guide for Epic, Oracle Health and athenahealth shows how each vendor implements it.
# Start a group-level bulk export (FHIR Bulk Data Access)
GET [fhir-base]/Group/[group-id]/$export?_type=Patient,AllergyIntolerance,MedicationRequest,Condition,Observation,Immunization
Accept: application/fhir+json
Prefer: respond-async
Authorization: Bearer [token]
# 202 Accepted, Content-Location: [status-url]
# Poll the status URL until 200, then download each NDJSON file listed in "output" Questions to ask your current vendor before you sign the new contract:
- Which FHIR resources does your bulk export include, and for which patient groups?
- What does your EHI export contain beyond FHIR, and in what file format?
- Are notes, scanned documents and images included, or exported separately?
- What fees apply to exports, and on what basis?
- How long will you keep read access to the old system after the contract ends?
- Will you provide the data dictionary for custom fields and local codes?
- Who on your side supports the export, and with what response time?
How does mapping work? A worked example
Mapping translates each source field and code into the target's field and code, and records what happens to anything that does not fit. Below is a small, invented example for an allergy record moving from a legacy system into a FHIR-based target.
| Source field (legacy) | Example value | Target (FHIR AllergyIntolerance) | Rule |
|---|---|---|---|
| PAT_ID | LEG-004211 | patient (reference) | Look up the new patient ID from the identity crosswalk |
| ALG_CODE | PCN01 (local) | code: SNOMED CT 764146007 "Penicillin" | Local code crosswalk, reviewed by a pharmacist |
| ALG_FREE_TEXT | "pcn - hives" | code.text, reaction.manifestation | Keep the original text; map "hives" to urticaria |
| SEVERITY | 3 | reaction.severity: severe | 1-2 = mild, 3 = severe; agreed with clinicians |
| STATUS | A | clinicalStatus: active | A = active, I = inactive, E = entered in error (do not migrate) |
| ENTERED_DT | 03/07/2019 | recordedDate: 2019-03-07 | Source is MM/DD/YYYY; confirm with a sample |
| (no code match) | "tree nut mix" | Not loaded as coded data | Sent to a review queue; never silently dropped |
Three rules keep mapping honest. Keep the original text alongside every converted code. Send anything that does not map to a human review queue with a reason. Version the mapping specification, because it will change after every test load.
How do you prove the data arrived? Reconciliation checks
You prove a migration with four kinds of checks run on every test load: counts, values, meaning and clinician review. Passing counts alone is not proof; a load can have the right number of rows with the wrong units or the wrong patient.
| Check | How | Pass rule (set before the first load) |
|---|---|---|
| Patient counts | Source patients in scope vs target patients created plus rejected | Every patient accounted for |
| Per-type counts | Allergies, medications, problems, results, immunizations per patient | Differences explained row by row |
| Values and units | Sample of lab values compared after unit conversion | No unexplained difference |
| Dates and time zones | Birth dates and result times compared | No shifted dates |
| Financial totals | Open balances and open claims | Match to the cent |
| Code mapping | Share of codes mapped; review queue size | Every unmapped code reviewed |
| Duplicates | Patient matching on name, birth date and identifiers | Possible duplicates reviewed by registration staff |
| Clinician chart review | Clinicians compare sample charts side by side, high-risk patients first | Signed off by a named clinician |
Cutover and rollback
Cutover is the short window in which the old system stops taking new data, the final changes move across, and the new EHR becomes the source of truth. Plan it as a rehearsed script, not an event.
- Rehearse the full load at least once in the test environment with production-sized data, and time it.
- Freeze the old system for new entries (or switch it to read-only) at an agreed time.
- Delta load only the records created or changed since the last full load.
- Run the reconciliation checks again on the delta, with a smaller clinician review.
- Go live, with go-live support staff watching for missing data reports.
- Keep rollback possible until clinical sign-off: the old system stays readable, and the decision to roll back has a named owner and a deadline.
- Archive the old system read-only once sign-off is complete.
Cerner (Oracle Health) EMR migration: what is different
A Cerner EMR migration, now an Oracle Health Millennium migration, follows the same phases, with a few specifics worth planning for.
- Two ways out: Millennium supports the FHIR R4 APIs and bulk export required for ONC certification, and Oracle Health offers an EHI export. Data outside those, such as custom forms, usually needs a database or reporting extract written in CCL (Cerner Command Language) or through Oracle Health's reporting tools.
- Code sets: Millennium uses its own code values for many concepts; plan crosswalks from those code values to SNOMED CT, LOINC and RxNorm.
- Moving into Oracle Health: when Oracle Health is the target, loads usually go through Oracle Health's conversion services or HL7 interfaces; agree the method and the test domain early.
- Existing interfaces: list every HL7 feed into and out of Millennium; each one needs a replacement in the new EHR. Our Oracle Health integration team handles both directions.
What drives the cost of an EHR data migration?
Cost depends far more on scope decisions and data quality than on record count. These are the main drivers, roughly in order of impact.
| Cost driver | Pushes cost up | Keeps cost down |
|---|---|---|
| Scope of discrete history | Every year of every value converted | Short discrete window, older data as documents |
| Number of source systems | Several systems with overlapping patients | One main source |
| Data quality | Free text, local codes, duplicates | Clean coded data |
| Custom fields and forms | Many specialty templates | Mostly standard content |
| Extraction access | Vendor-only extracts, slow support | Bulk FHIR and EHI export available |
| Number of test loads | No clear pass rules, so loads repeat | Pass rules agreed before the first load |
| Archive approach | Keeping the old EHR licensed for years | A read-only archive with search |
For how migration sits next to licenses, training and interfaces in an EHR budget, see our EHR implementation cost guide.
Patient data migration best practices
- Agree written pass rules for reconciliation before the first test load.
- Give each data type a named clinical owner who signs it off.
- Never load into production first; use a copy of the new EHR.
- Keep source text next to every converted code.
- Route unmapped values to a review queue; never drop them silently.
- Resolve patient identity (duplicates and merges) before loading clinical data.
- Rehearse cutover with production-sized data and time it.
- Keep the old system readable until sign-off, then archive it.
- Log every load, every rejected row and every decision; the log is your audit trail.
- Treat all extracts as protected health information: encrypted storage, access limited to the project, deletion when done.
EHR data migration acceptance checklist
Use this as the acceptance criteria in your statement of work.
| # | Item | Evidence |
|---|---|---|
| 1 | Source inventory complete with volumes | Inventory document |
| 2 | Scope matrix signed by clinical and compliance owners | Signed matrix |
| 3 | Mapping specification and code crosswalks versioned | Spec in version control |
| 4 | Identity crosswalk and duplicate review complete | Duplicate report signed by registration lead |
| 5 | Reconciliation pass rules agreed | Signed pass rules |
| 6 | Final test load passed all checks | Reconciliation report |
| 7 | Clinician chart review signed | Named reviewer sign-off |
| 8 | Cutover rehearsed and timed | Rehearsal log |
| 9 | Rollback owner and deadline named | Cutover plan |
| 10 | Archive access tested by end users | Test record |
| 11 | Extracts deleted or secured after go-live | Deletion record |
Who should do your EHR data migration?
You have three options: the new EHR vendor's conversion team, a specialist data migration firm, or your own team with outside help for the hard parts. Vendor teams know their target well but often limit what they will load. Specialist firms that do data migration for EHR projects bring tools and experience across source systems. Doing it yourself works when you have strong analysts and a single, well-understood source.
Nirmitee handles extraction, mapping, FHIR and HL7 conversion, reconciliation and archive design as one team, and works alongside the vendor's conversion team where the vendor owns the load. We are ISO 27001:2022 certified and sign BAAs. Talk to us about your EHR data migration.
Planning an EHR data migration?
Define what moves, what stays in an archive and how you will reconcile the result. Bring your source EHR, target system, available exports and proposed cutover window.
Frequently Asked Questions
What is EHR data migration?
What are the steps in the data migration process from EHR to EHR?
How much data should be migrated to a new EHR?
How does FHIR data portability help EHR migration?
What is the difference between EHR data migration and EHR integration?
How do you migrate data from Cerner (Oracle Health)?
What are patient data migration best practices?
What drives the cost of healthcare data migration?


