# Liftport: a guided exit from managed hosting, with the savings math shown

> A savings calculator that cannot return the answer that would stop you.

Source: https://saascode.ai/inside/road-to-liftport · Published: 2026-08-09 · Section: builds · Product: Liftport (https://saascode.ai/products/liftport)

---
A savings calculator that cannot return the answer that would stop you.

The monthly saving is floored at zero. A VPS that costs more than the managed host does not produce a negative result — it produces $0.00 saved, and the payback month is left blank. The figure the calculator divides by — the cost of switching, the thing no product can measure on someone else's behalf — is fixed at $200, identical for every customer and every application. That constant is written into an `assumptions` object on every savings row alongside a flag marking the report as projected. The report ships its own premise. A reader can see the number assumed on their behalf and disagree with it.

The honest sting, which the product's own research had already established: two operations hours a month at a realistic rate runs roughly $1,800 per year. Against that, $200 is under three hours of work, priced once across the life of the migration.

## Starting point

Liftport is a self-serve migration cockpit: connect a managed-host account, read the source project's configuration, generate a container bundle and ordered plan, prove parity with a post-cutover smoke check, and record what the move was worth on the numbers the team supplied. The product is not a host. Its recurring value — monitoring and further migrations — sits on top of the move; the move itself is the wedge.

Three questions had to be closed before the first table was defined. What is the business model when the core action happens once? How do you inventory a source host that will not hand over everything? And how do you reproduce the app on the target and prove it matches? The first question resolved as a freemium wedge with a durable recurring layer: one free guided migration, with ongoing VPS health monitoring and unlimited further migrations available under paid tiers. The second resolved as a declared gap — the provider APIs return environment variable names and not their values, and the inventory records this plainly rather than papering over it. The third is the scope constraint that defined the product: Liftport generates the artifacts and guides the team through running them. There is no server-side SSH in version one. The bundle and the commands are the deliverable; the execution is the user's.

## Reading the market

The category that Liftport entered had four shapes: managed migration services that move a team and immediately become the new host; cheaper managed hosts funneling teams onto their own infrastructure; open-source self-hosting tooling that gives a capable destination but leaves the move as the team's own problem; and consulting firms building bespoke plans at project rates. Nobody had productized the guided move itself — read the source, generate the config, prove parity, quantify the saving — as self-serve software independent of any host.

Coolify is where many teams are headed, not a rival. It gives teams a capable VPS control plane; it does not inventory a source project, generate a migration plan, or run a parity check after cutover. Liftport is the move, not the landing pad.

The saving the product calculates is conditional. It is real for usage-dominated bills, where bandwidth and compute spending at the margin justifies the VPS. It is real for seat-dominated bills on Vercel specifically — Netlify and Render both moved to flat and unlimited-seat pricing in April 2026, removing the seat argument for those platforms. And it is real for teams already running servers, where the operations cost is already on salary. For a five-seat workload sitting comfortably inside its allowance, the arithmetic runs the other way: two operations hours a month at $75/h costs $1,800 per year, which erases a $550 annual saving entirely. The destination VPS is not a fixed floor — Hetzner raised prices between 33% and 173% in June 2026. Leaving a managed host saves money on hosting fees and costs money on edge caching, zero-ops patching, on-call and backup infrastructure. Whether the balance is positive depends on the team.

## The decisions that shaped it

**The migration run got its own state machine.** The underlying application skeleton had a generic scheduled-task infrastructure already built. It was not used. A scheduled task is designed for jobs — a start, an end, trigger and forget. A migration is a resumable conversation with a human in the middle. Two of the cockpit's seven steps deliberately park in a waiting state — cutover and verification — because the team is the one executing the commands. The state machine that shipped has one table for runs and one for steps, one discrete queue invocation per step, and a dead-letter handler that verifies the queue's cryptographic signature before acting under elevated authority. The queue triggers and disconnects; the next step is picked up from the database via a polling cursor. Whether the full advance chain works end to end in a live environment could not be verified in the build — it requires live queue credentials, a live source host and a live target. This was recorded as blocked and unverifiable, not green.

**The inventory declares what it could not read.** Every field in the manifest carries a source annotation: API, user-attested, or missing. The Vercel adapter's own source comment states that environment variable values are not returned by design — the API hands over names only. Route tables and function inventories are sourced from the repository rather than the API, so the adapters return them empty rather than fabricating a guess. When a user attests a field, the attestation is recorded and the manifest's completeness score updates accordingly. The generated `.env` template carries the same honesty into the downloadable bundle: each unreadable variable is emitted with a comment stating the value was not available from the provider. The design's answer to the gap was to make the hole explicit and auditable rather than to paper over it.

**The config generator has no model in the loop.** The plan and the container configuration are produced from templates keyed on the detected application type and the build settings read from the source. The output is a Dockerfile a stranger will run against production; determinism is what makes it reviewable. The same manifest produces the same bundle, and the function is safe to call repeatedly. The bundle contains a Dockerfile, a compose file, a `.env` template and a README carrying the exact commands — copy, fill the environment file, build, run, then return to the smoke check before touching DNS. The source host stays live until the team approves the flip.

**The billing layer carries two different transaction shapes.** The free guided migration and the recurring subscription are products aimed at different intentions: a team that wants to move one application and leave, and a team that wants the move watched over time. Supporting only one shape would have forced one of those buyers out. A one-time Migration Pass required restoring a billing-type value that the underlying schema had narrowed away — recorded as a reconciliation anomaly in the build log. The one-time grant is idempotent by Stripe checkout session identifier; a redelivered webhook grants exactly once. All four plans and their prices were verified live against the deployed database before the release was tagged.

## What fought back

The database specification was written against an older version of the underlying application skeleton and then built onto a newer one. Three separate collisions surfaced at migration time: a billing type the newer schema had excluded that the Migration Pass needed, a plan-deletion step that collided with a foreign key on a pre-existing demo subscription, and an identifier comparison written for a text column that had since become a UUID. All three were reconciled with additive changes. A separate reconciliation check also reported a hard failure on the role configuration that turned out to be a false positive — the checker took the last matching constraint in the file, which belonged to an unrelated table's enumeration, not to the users table. The actual configuration was correct.

The Coolify export is registered, generated and unreachable. The generator supports three output kinds and all three are seeded: a compose bundle, a standalone Dockerfile, and a Coolify import artifact. The generator produces the Coolify artifact only when a call site asks for it by slug. Both call sites in the shipped build pass the compose slug — so the artifact is always null. The plan screen exposes an export control for it, gated on that value. Given that Coolify is where most migrations are headed, this is the gap most worth naming.

The smoke check inherits the inventory's silence. The parity proof derives its comparison URL from the manifest's domain list and its paths from the manifest's route list. Both fields are the ones the provider adapters return empty, because they are sourced from the repository rather than the API. Unless the team supplies a source URL and route paths explicitly, the check falls back to a single path against the target only, and a target response below a 500 counts as a match. The response diff is real and works; it needs the team to supply what the API would not enumerate.

The cost monitoring reads a number the user typed. The monitoring pass runs two checks: an HTTP health probe with a bounded timeout, and a cost-drift comparison against the latest savings report. There is no billing API integration in this version; the monthly VPS cost is a number entered when adding the target, and there is no endpoint to update it afterward. Uptime is measured. Cost is remembered.

## What shipped

Liftport shipped as a complete multi-tenant migration cockpit: three source adapters for Vercel, Netlify and Render, each declaring its own capability profile. A five-step guided run — inventory, plan, cutover, verify and savings — backed by a custom run-step state machine and live status streaming over Realtime channels. A public savings estimator on the marketing surface. An ongoing VPS health and cost-drift monitoring layer, feature-gated to paid tiers. A per-migration savings ledger, with the assumptions object written into every row so the basis for each figure is readable. Credentials for source connections and VPS targets held in organization-scoped tables, encrypted under an organization-derived key, with private key material encoded before encryption and the raw column excluded from every client-facing projection.

Verification confirmed routes reachable and role boundaries enforced at every tier, with zero unwaived security findings after a live authorization check across three role levels. The full migration chain — queue, source-host OAuth, payment integration — was not exercised end to end in the build environment. The build records this honestly. The savings report records its assumptions honestly too.

## See it

https://liftport.saascode.ai

## Related reading

- [Leaving a Managed Host in 2026: What You Hold When You Reach the Other Side](https://saascode.ai/inside/leaving-a-managed-host-in-2026-what-you-hold-when-you-reach.md)
- [Liftport vs. the Free Path — When a Community Guide Is Enough, and When It Isn't](https://saascode.ai/inside/liftport-vs-free-community-guides-vercel-to-coolify.md)
