Vendor lock-in is not a single risk. It is four separate ones, and three of them are cheap to avoid if you decide before you build rather than halfway through a migration.
Most writing on this topic stops at the definition: you become dependent on a provider, switching is expensive, therefore go multi-cloud. That advice is close to useless. Multi-cloud is itself expensive, and the teams that adopt it rarely do so because of lock-in. The useful question is narrower and answerable: for each piece of your stack, what would it actually take to move it somewhere else next month?
Table of contents
- The four kinds of lock-in
- What actually moves without a rewrite
- What does not move, and what it costs
- The exit test: can you leave in a weekend?
- When lock-in is the right trade
- Portability decisions to make on day one
- How this fits the rest of the stack
- FAQ
The four kinds of lock-in
Lumping these together is why the conversation goes nowhere. They have different costs and different fixes.
- Data lock-in. Your rows are in a store you cannot export in a usable format, or the egress fee to pull them out is a meaningful number. This is the one that genuinely traps people.
- Runtime lock-in. Your code calls provider-specific APIs, or it only runs inside a proprietary function format. Moving means rewriting, not redeploying.
- Operational lock-in. Your deploys, secrets, monitoring, and on-call runbooks are all built around one dashboard. Nothing technically blocks a move, but your team has to relearn how to ship.
- Contractual lock-in. Committed spend, reserved capacity, annual terms. Purely a finance question, and the easiest one to see coming.
Ranked by how much they actually hurt: data, then runtime, then operational, then contractual. Most articles spend all their time on the last one because it is the easiest to quantify, which is a bad reason.
What actually moves without a rewrite
A surprising amount of a normal web stack is portable, and it is worth knowing which parts so you can stop worrying about them.
- A Docker image. If your service builds to a container, it runs anywhere that runs containers. The image is the portability boundary, and it is a good one.
- A Postgres or MySQL dump.
pg_dumpandmysqldumpproduce a file that restores into any instance of the same engine. Schema, data, constraints, all of it. - Static build output. A directory of HTML, CSS, JavaScript, and assets is the most portable artifact in web development. It has no runtime dependency at all.
- Your DNS zone. Records are records. Moving the zone is a nameserver change and a wait for TTLs to expire.
- Environment variables. A list of key-value pairs. Tedious to retype, not hard to move.
If your entire production footprint is a container, a managed Postgres, a static bundle, and a DNS zone, you are not locked in to anything. You have a weekend of work and a maintenance window, which is a very different thing from a migration project.
What does not move, and what it costs
The genuinely sticky pieces share a trait: they have no standard, so there is nothing to port to.
- Managed queue and event semantics. Delivery guarantees, visibility timeouts, dead-letter behaviour, and ordering rules differ between providers. Code written against one set of semantics does not behave the same against another, and the bugs are subtle.
- Identity and access policy. Access rules are written in a provider-specific policy language against provider-specific resource identifiers. There is no export format worth having.
- Proprietary function runtimes. Handler signature, cold-start behaviour, execution limits, and bundled SDKs are all provider-shaped. This is the classic rewrite.
- Vendor-specific database extensions. A managed engine with a proprietary extension is not quite the engine any more. Your dump restores; some of your queries do not.
- Data egress at volume. Terabytes cost real money to move, and the bill lands exactly when you are already committed to leaving.
None of these are reasons to never use managed services. They are reasons to know, at the time you adopt one, roughly what leaving would cost.
The exit test: can you leave in a weekend?
Here is a drill that is more informative than any architecture review. Pick your production stack and write down, honestly, the steps to stand it up somewhere else. Not to migrate traffic. Just to have it running.
- Build the image from the repository. If this needs a proprietary build service, note it.
- Restore the database from last night’s dump into a fresh instance. Time the restore.
- Set the environment variables. Count how many are provider-specific endpoints rather than your own config.
- Point a test domain at the new deployment and issue a certificate.
- Run your smoke tests against it.
If that list is five steps and a few hours, lock-in is not your problem and you can stop thinking about it. If step two turns into a data-export project, or step one requires a rewrite, you have found the real dependency. That is worth knowing on a calm Tuesday rather than during an incident.
Teams that run this drill once tend to find one surprise. It is almost never the thing they were worried about.
When lock-in is the right trade
The honest position is that some lock-in is worth paying for. A managed database is lock-in in the operational sense, and it is still the right call for almost every small team, because the alternative is you doing backups, patching, and failover yourself at three in the morning.
The trade goes bad in a specific way: when the locked-in component is load-bearing for your product and the provider’s roadmap or pricing stops matching yours. A proprietary managed service you use for one background job is a footnote. The same service holding your primary data model is a strategic dependency, and it deserves a real decision rather than a default.
So the rule is not avoid lock-in. It is: keep the lock-in shallow where the component is deep, and let it run deep where the component is shallow.
Portability decisions to make on day one
Five choices, made early, that keep the exit test short. None of them cost you anything at build time, which is what makes them worth making by default.
- Build to a container. Even if your host does not require one, having a Dockerfile in the repository means the runtime is defined by you, not by a platform’s build detection.
- Use a standard database engine. Postgres and MySQL both have decades of tooling and a dump format that works everywhere. Reach for something exotic when you have a reason you can articulate out loud.
- Keep configuration in environment variables. Not in a provider’s config service behind an SDK call.
- Own your domain and your DNS records. The nameserver is your steering wheel. Never let it sit somewhere you cannot log into.
- Take your own backups, on your own schedule, to storage you control. Provider backups are convenient and they live inside the thing you might need to leave.
Do those five and the answer to what if we need to move becomes a scheduling question rather than a project.
How this fits the rest of the stack
The practical version of this decision is usually financial rather than architectural. You are not really asking whether you could leave; you are asking whether the bill justifies the effort of staying. That is easier to answer when the line items are visible: the service, the database, the storage, the bandwidth, each as its own number. The RunxBuild hosting calculator lays them out that way, which makes it obvious which parts of a stack are worth the portability work and which are rounding errors. On the platform side the portability answers are deliberately boring: services build from a GitHub repository as containers, databases are stock MySQL and Postgres with ordinary dumps, and static sites are just build output on a custom domain.
Useful related references:
- Cloud Migration Challenges: The Nine That Always Come Up
- Multi-Cloud Management Without Spreadsheets or Suffering
- iPaaS Vendor Comparison: The Criteria That Survive the Sales Call
- Services on RunxBuild
FAQ
What is vendor lock-in in cloud computing?
Vendor lock-in is the situation where moving your workload to a different provider costs more than it is worth. It comes in four forms: data that is hard to export, code written against provider-specific APIs, operational habits built around one dashboard, and contractual commitments. The four have very different costs, and only the first two usually require engineering work to undo.
Is multi-cloud a good way to avoid lock-in?
Rarely, on its own. Running the same workload across two providers doubles your operational surface and forces you down to the lowest common denominator of both platforms. Most teams get more portability for less money by containerising their services and using standard database engines on a single provider.
How portable is a managed database?
Very, if it is a stock engine. A Postgres or MySQL instance dumps with pg_dump or mysqldump and restores into any other instance of the same engine. It stops being portable the moment you depend on a provider-specific extension or a proprietary variant of the engine, because the dump will restore but some queries will not run.
Does using Docker eliminate lock-in?
It eliminates runtime lock-in for your own code, which is a real win. It does nothing about data lock-in, egress fees, or dependencies on managed services your container calls out to. A container that talks to three provider-specific APIs is portable in name only.
When should I actually worry about this?
Before you adopt a managed service that will hold primary data or sit on the critical path, and before you sign anything with a committed-spend clause. After that, run the exit test once a year. If it stays short, you are fine; if it grows, you have found something worth looking at.