Migration paths
Time: Varies by source system
Prerequisites: Familiarity with your current routing system, a Restormel Keys account
You'll need: Access to your current gateway/proxy config, your app's codebase, and the Dashboard
This page covers migration from four starting points: custom routing code, LiteLLM, Portkey, and OpenRouter. Each variant follows the same principle: strangler pattern — run old and new in parallel, shift traffic incrementally, verify, then retire the old system.
The walkthrough phases (0–6) are framework-agnostic. This page maps each migration source to the phases, highlights what's different, and provides source-specific prompts.
Migration principle: the strangler pattern
Your app sends 100% traffic to the old routing system (LiteLLM, Portkey, or custom). A feature flag gates Restormel Resolve; when enabled, some traffic takes the new path to the AI provider. Both paths can hit the provider until you cut over.
- Install Restormel Keys alongside your existing system (Phase 1). Nothing changes yet.
- Wire the resolve call behind a feature flag (Phase 2). Old system still handles 100%.
- Shift a small percentage of traffic to Restormel (Phase 6, Step 6.2). Both systems run.
- Verify — compare outcomes, latency, and errors between old and new paths.
- Cut over — move all traffic to Restormel. Old system is idle.
- Remove — decommission the old system after a burn-in period.
At no point do you rip out the old system before the new one is proven. The feature flag (Phase 0, Step 0.5) is your safety net throughout.
Variant A — I have custom routing code
This is the default path the walkthrough is written for. Your app has bespoke if/else, config files, or a small routing module that picks providers.
What's different: Nothing. Follow Phases 0–6 as written.
| Phase | What you do |
|---|---|
| 0 | Inventory your custom files (router, fallback, model picker, BYOK settings) |
| 1 | Install packages, create project |
| 2 | Wire resolve alongside your custom router via feature flag |
| 3 | Move your fallback chain config into dashboard routes |
| 4 | Move your model allowlists into dashboard policies |
| 5 | Replace your custom model picker with ModelSelector |
| 6 | Shift traffic, verify, remove custom code |
Variant B — I'm using LiteLLM
LiteLLM is a proxy server that normalises provider APIs. You call LiteLLM's endpoint and it forwards to the configured provider. Common setup: Docker container running LiteLLM with a config file defining models, fallbacks, and provider keys.
What Restormel replaces: LiteLLM's routing and fallback logic → Restormel routes and steps. LiteLLM's model config → Restormel dashboard (routes, policies, model catalog). LiteLLM's proxy endpoint → your app calls providers directly using the resolve result.
What Restormel does NOT replace: LiteLLM's request/response normalisation if you depend on its unified schema. If you need this, keep LiteLLM as a normalisation layer and have Restormel decide which model LiteLLM should use.
| Phase | LiteLLM-specific notes |
|---|---|
| 0 | Inventory: your LiteLLM config, the Docker/process setup, and every place your app calls the LiteLLM proxy. Classify as "REMOVE" (call providers directly) or "WRAP" (keep LiteLLM as normalisation layer with Restormel doing routing). |
| 1 | Install Restormel packages. No change to LiteLLM yet. |
| 2 | Wire Restormel Resolve. If keeping LiteLLM: resolve returns the model, you pass it to LiteLLM. If removing: resolve returns the provider, you call the provider SDK directly. |
| 3 | Translate LiteLLM fallback settings into dashboard routes. Each LiteLLM fallback model becomes a step in a Restormel route. |
| 4 | Translate any LiteLLM allowed_models or budget settings into Restormel policies. |
| 5 | If LiteLLM had no UI, this is net-new. If it did, replace with Restormel ModelSelector. |
| 6 | Shift traffic. When 100% is on Restormel: stop the LiteLLM container, remove config, remove Docker/process config. |
Implementors: See “Agent prompts for this phase” below for plan-only prompts for each migration variant.
Variant C — I'm using Portkey
Portkey is a gateway with routing, fallbacks, caching, and observability. You call Portkey's endpoint with a config header that specifies routing strategy.
What Restormel replaces: Portkey's routing configs → Restormel routes and steps. Portkey's fallback strategies → Restormel fallback_chain route mode. Portkey's model restrictions → Restormel policies.
What Restormel does NOT replace: Portkey's request caching (use a separate caching layer if needed). Portkey's observability/tracing (use your existing tracing).
| Phase | Portkey-specific notes |
|---|---|
| 0 | Inventory: Portkey configs (JSON headers or API configs), the Portkey API key, every place your app calls the Portkey API or uses the Portkey SDK. |
| 1 | Install Restormel packages alongside Portkey. |
| 2 | Wire Restormel Resolve. Your app calls resolve first, then makes the provider call directly. Feature flag gates which path runs. |
| 3 | Translate Portkey routing configs (provider order, fallback strategy) into dashboard routes with steps. |
| 4 | Translate any Portkey model restrictions or budget controls into Restormel policies. |
| 5 | Embed Restormel UI. Portkey has no embeddable BYOK UI — this is net-new. |
| 6 | Shift traffic. When 100% is on Restormel: remove Portkey SDK, delete Portkey API key, cancel Portkey subscription if applicable. |
Key difference from LiteLLM: Portkey configs are typically JSON objects passed as headers or configured via their dashboard/SDK. Extract the routing logic from those configs manually.
Variant D — I'm using OpenRouter
OpenRouter is a unified API that routes requests to multiple providers with a single endpoint. You call the OpenRouter API with a model name, and OpenRouter handles provider selection.
What Restormel replaces: OpenRouter's implicit provider routing → Restormel explicit routes and steps (you control exactly which provider handles each model). OpenRouter's model selection → Restormel ModelSelector with policy constraints.
What Restormel does NOT replace: OpenRouter's access to providers you don't have direct accounts with. If you use OpenRouter for that, you may keep OpenRouter as a provider within Restormel's routing (a step in a route that calls OpenRouter).
| Phase | OpenRouter-specific notes |
|---|---|
| 0 | Inventory: every place your app calls the OpenRouter API, the OpenRouter API key, any model-specific logic. |
| 1 | Install Restormel packages. Set up direct provider accounts (OpenAI, Anthropic, Google) for the models you use — or keep OpenRouter as a "provider" in your route. |
| 2 | Wire resolve. The resolve result tells you which provider to call. If keeping OpenRouter as a provider, add it as a custom provider in your config. |
| 3 | Create routes. If you previously relied on OpenRouter to pick the cheapest provider for a given model, replicate that as explicit route steps. |
| 4 | Add policies. OpenRouter has no policy concept — this is net-new governance. |
| 5 | Embed ModelSelector. OpenRouter has no embeddable UI — this is net-new. |
| 6 | Shift traffic. When 100% is on Restormel: remove OpenRouter SDK/API calls, revoke OpenRouter API key if no longer needed. |
Key difference: OpenRouter abstracts away provider choice entirely. Moving to Restormel means you take explicit control of which provider handles each request. More work, but full visibility and policy control.
Comparison: what each source gives you that Restormel doesn't
| Feature | LiteLLM | Portkey | OpenRouter | Restormel approach |
|---|---|---|---|---|
| Request/response normalisation | Yes (proxy) | Yes (proxy) | Yes (proxy) | No — call providers directly with their SDKs. Use Restormel for routing only. |
| Request caching | Plugin | Yes | No | Not in scope — use a caching layer (Redis, CDN, etc.) |
| Observability / tracing | Plugin | Yes | Basic | Not in scope — use your existing tracing |
| Access to providers you don't have accounts with | No | No | Yes | No — you need direct provider accounts (or keep OpenRouter as a step) |
| Embeddable BYOK UI | No | No | No | Yes — KeyManager, ModelSelector |
| Policy enforcement (allowlists, budgets) | Partial | Partial | No | Yes — first-class policies in the dashboard |
| Library-first (no proxy/container) | SDK only | No | No | Yes — headless core, no infrastructure required |
The safe strangler approach in detail
Regardless of your source system, the sequence is: Week 1 — install Restormel alongside the old system. Week 1–2 — wire resolve behind a feature flag. Week 2 — configure routes and policies in the dashboard. Week 2–3 — send 5% of traffic through Restormel. If errors, fix and retry at 5%; if no errors, increase to 25%, then 50%, then 100%. Burn-in for one release cycle (1–2 weeks), then remove the old system.
Timeline: Most teams complete the migration in 2–3 weeks. The burn-in period before removing the old system is typically one release cycle.
false and 100% of traffic returns to the old system instantly. No code change, no deployment — just an env var.Checkpoint
You now have: a clear mapping from your source system (custom, LiteLLM, Portkey, or OpenRouter) to the walkthrough phases; a strangler approach that lets you migrate incrementally with instant rollback; source-specific notes on what Restormel replaces and what it doesn't; and (if LiteLLM) a build-agent prompt for producing a migration plan document.
Next: Verification strategy — ongoing checks for dashboard, CLI, and smoke tests after integration.
Prompts for this phase
These are optional and collapsed by default. Use them if you're migrating from an existing routing system with a coding agent.
Checkpoint checklist: mark each section complete as you read it.
Checklist