Migrating off WHMCS without a bad weekend
The reason people stay on billing software they dislike is the migration. It is survivable if you rehearse it instead of attempting it.
Almost nobody likes their billing platform. Almost everybody keeps it.
Not because switching is impossible, but because the failure mode is so unpleasant. Billing is where your customer records, your revenue history and your accountant’s reality all live at once. A migration that goes wrong is not a bad deploy; it is invoices that do not match the books.
So the useful property in a migration tool is not speed. It is rehearsability.
Dry run by default
Our importer changes nothing unless you tell it to. It reads your existing database, works out what it would create, and reports that.
You read the report. You find the three clients with no email address, the invoice attached to a deleted customer, the domain nobody owns. You fix those at the source, where they were already wrong, and run it again.
Idempotent
Running it twice does not create two of anything.
That sounds like a technical detail and it is the whole reason the first property works. If a second run duplicated your data, you would get exactly one attempt, under time pressure, with no way to practise. Instead you can run it as many times as it takes to stop finding surprises.
Original invoice numbers are preserved
This is the detail most migrations get wrong and the one your accountant will notice first.
If invoice INV-2024-0117 becomes INV-2026-0001 in the new system, every reconciliation from here backwards has to be done by hand. Keeping the original numbers means your books still agree with your billing.
The thing that cannot come across
Passwords.
They are hashed at the source with a scheme nobody can reverse — which is the correct behaviour and exactly what you would want from your old provider. Your customers will set a new password the first time they sign in.
This is survivable and it is the single biggest support spike of a migration if you do not warn people. One email, sent before the cutover, turns "I have been locked out of my hosting" into "yes, I was expecting that."
A running order that works
- Dry run. Read the report properly, not just the summary line.
- Fix the source data. Duplicate clients, orphaned invoices, domains with no owner.
- Dry run again, until it is boring.
- Freeze the old system. No new orders during the cut.
- Run for real, then verify three numbers: client count, open invoice value, active services. If those match, you have moved.
- Switch DNS, then send the password-reset email.
How long to allow
The import itself is usually minutes. Steps 1 to 3 are where the time goes, and they are the steps worth taking slowly, because they happen while the old system is still serving customers and nothing is at stake.
A migration you have run four times is not an event. It is the fifth run.


