Last week we moved our production chatbot platform — an AI assistant that answers visitors’ questions using each client’s own website content (retrieval-augmented generation, or RAG) — from Microsoft Azure to a single IONOS Cloud Cube, IONOS’s vCPU-based virtual server line. Six services, two databases, a vector store and an API gateway; from the decision to “fully off Azure, teardown included” took ten days, with production cutover on day eight. We watched the chat logs through the cutover window and saw uninterrupted sessions — no client-side errors surfaced. This is the story of how, and of the decisions that made it small.

The original prompt

The chatbot began life as an innovation project: a multi-tenant RAG chatbot grounded in each client site’s own knowledge base. It grew up on Azure — App Services, Azure Functions, API Management, Azure SQL, Cosmos DB with vector search — largely because that’s where the credits were.

Three things prompted the move. Our Azure sponsorship credits had an expiry date. The platform’s Azure bill was heavier than its workload justified — the API gateway product alone cost more than all the compute underneath it. And strategically, we’d already had a good experience consolidating our WordPress estate onto a single IONOS Cube, so we wanted the chatbot on infrastructure we understood and controlled.

IONOS or Cloudflare?

We seriously evaluated two destinations. Cloudflare was tempting — Workers would have mapped neatly onto our Azure Functions API layer, Vectorize onto the vector store, Workers AI onto the LLM. But the evaluation kept coming back to one inconvenient truth: Workers only fit half the system. Our backend services and (especially) our Rails admin interface have no comfortable home on Cloudflare today, so “migrate to Cloudflare” really meant “migrate to Cloudflare and somewhere else, while rewriting the API layer” — under a deadline, with a two-person team.

IONOS took the whole system as-is: every service in one Docker Compose file on one Cube, sized to match the single Azure instance everything already ran on. The decision wasn’t anti-Cloudflare — it was pro-sequencing. A platform migration and an architecture rewrite are each manageable; coupling them is how migrations go wrong. Cloudflare stays on our roadmap for a future, leaner version of the platform.

The architecture we ended up with

On Azure: five Node App Services, one Azure Functions app, a Rails admin container, API Management in front, two Azure SQL databases, and Cosmos DB providing vector search. On IONOS:

There’s a data-jurisdiction dividend too: application, databases, vectors and backups now all sit in a UK data centre with an EU-headquartered provider — a materially easier conversation with clients than walking through a hyperscaler’s region and sub-processor map. The AI layer itself still calls OpenAI today; moving that to European processing is on the roadmap (see below), and we’d rather say that plainly than imply otherwise.

One choice worth highlighting: the Azure Functions app kept its runtime. Microsoft publishes the Functions host as a public container image, and with storage bindings emptied it runs happily on any Docker host. Keeping it meant zero changes to eight working HTTP handlers. Refactoring to plain Node can happen later, on our schedule — the boring path strikes again.

The other choice that saved real work: we kept our existing embedding model — the component that turns documents into the numerical vectors the AI searches — so every stored vector remained valid. Swapping embedding providers during a migration means re-embedding every tenant’s knowledge base and resizing vector columns — a whole project hiding inside a config change. We declined to smuggle it into this one.

And the bill? The new platform is a flat, predictable monthly cost instead of a dozen metered line items: roughly £23 a month all-in — the Cube, a static IP, and backup object storage. For scale: on Azure, the API gateway product alone cost several times that, before counting any compute or databases. Forecasting the bill now takes seconds.

The migration itself

Audit and the decision register

First, a complete inventory of the production environment — every resource, identity, setting and workflow — cross-checked by an LLM review pass that caught a handful of gaps before they could surprise us. Then a plan built around a decision register: sixteen decisions (database engine, gateway, region, DNS, LLM path, cutover strategy…), each written down with options and trade-offs, then closed one by one. Nobody re-litigated anything mid-migration.

Rehearsing locally: SQL Server to PostgreSQL, Cosmos DB to pgvector

The full data transformation — from SQL Server and Cosmos DB exports to PostgreSQL and pgvector — ran end-to-end against a copy of production data on a laptop before any cloud resource was provisioned. Rehearsal is where the work actually bit: the chat logger’s data layer had to be rewritten off the Microsoft SQL driver, and the Rails admin app had Azure-AD authentication baked into its production configuration that had to come out along with its SQL Server adapter. Better to find that on a laptop than during a cutover window. By provisioning day the scripts already worked; the day was execution, not discovery.

Cutover and teardown

Cutover was a WordPress plugin release pointing at the new endpoint, shipped to both production sites after fresh data dumps. The old platform stayed up as a rollback path while the new one soaked. Teardown followed within days: restores re-verified, the production resource group deleted in one command, and a final sweep confirmed nothing chatbot-related remained — including the forgotten dev-environment stragglers that every teardown seems to surface. Temporary credentials used during the migration window were revoked the same day. Net result: ten days, a rollback path live throughout, tested restores at every gate, and no data loss.

Tools that earned their keep

Lessons we’re carrying forward

What’s next

CI/CD is being repointed from Azure pipelines to the Cube, the last Azure-specific logging configuration is being stripped as branches merge, and we’re planning an evaluation of open-weight models on IONOS AI Model Hub — Llama-class chat with European data processing — against our current OpenAI baseline. That would complete the residency story: the whole platform, AI layer included, running under European jurisdiction. And when the platform’s planned consolidation into a leaner monolith lands, we’ll revisit Cloudflare with an architecture that actually fits it.

If you’re weighing a similar move — off a hyperscaler, onto infrastructure you can reason about, or adding a grounded AI assistant to your own product — we offer exactly what you’ve just read about: an audit and a decision register for your setup, before anything moves. Get in touch.