6 min read
Compute-storage separation does not remove lock-in
Portability is more than speaking PostgreSQL
A PostgreSQL-compatible interface does not, by itself, make a database portable. I would ask a harder question: if the service’s compute and storage are designed as one managed system, what exactly can I take with me when I leave?
Azure HorizonDB Preview is a useful case for asking that question. Microsoft describes an architecture that separates compute from storage, with storage shared by compute nodes; its documentation also describes a database-as-a-log design (Microsoft’s 2026 Postgres overview, HorizonDB architecture documentation). That is not merely a different way to buy PostgreSQL. It changes which parts of the system the service owns and which assumptions an architect should test.
The appeal is straightforward: compute and storage need not be scaled as a single unit. But an architectural benefit inside a service can also define a boundary around that service. I would not treat “PostgreSQL” as the complete portability story. I would separate portability into at least three questions: can my application move, can my data move, and can I operate the destination with the same assumptions?
The first question is often the easiest. If application code relies on ordinary PostgreSQL behavior and avoids provider-specific features, it may be easier to point it at another PostgreSQL deployment. That is a design aim, not a guarantee. Compatibility tests, extensions, authentication, connection handling, and operational integrations can still make a move more involved than changing a connection string.
The data question is different. The service’s internal storage design does not automatically tell me what export, restore, or replication paths are available to a customer. I need to establish those separately. And even if I can extract the data, the destination may have different assumptions about restore procedures, capacity, recovery, and maintenance. I would not infer an exit path from the fact that the service is built on PostgreSQL.
Shared storage moves the boundary
With a self-operated PostgreSQL deployment, I expect the team to make explicit choices about compute, disks, backups, replication, and failure recovery. A managed service takes some of that operational work away, but it does not make the choices disappear. It relocates responsibility and changes what I can inspect or control.
That distinction matters when compute and storage are separated. If compute nodes share a storage layer, I want to understand which failures the service handles, which failures surface to my application, and what recovery behavior I should design around. The architecture documentation can explain the model, but an architect still needs answers tied to the service contract: what happens during a compute failure, a storage disruption, a regional incident, or a maintenance event? What does the application observe? Which recovery actions are available to the customer?
These are not reasons to reject the design. They are reasons to draw the operational boundary accurately. A team may be comfortable delegating storage durability and recovery to a provider, especially if it does not want to run that machinery itself. Another team may need more control over the database’s failure modes, or may already have the operational capability to run PostgreSQL on its own infrastructure. The right choice depends on which responsibilities the team is willing and able to own.
I would be careful not to compare a managed service with an imaginary self-hosted database that runs itself. Operating PostgreSQL means owning the surrounding work as well as the server: deployment, upgrades, backup verification, monitoring, access control, and recovery exercises. Conversely, choosing a managed service does not remove the need to understand application-level retries, connection behavior, data protection, and the consequences of an outage. It changes the division of labor, not the need to design for failure.
Ask what a move would require
Portability becomes concrete when I describe the exit, rather than when I label a design “portable.” I would write down a plausible move from the managed service to a PostgreSQL deployment the team operates itself, then identify the assumptions that plan depends on.
First, I would inventory the database surface the application actually uses. Which extensions, database settings, authentication mechanisms, and provider-specific capabilities are required? Which are optional conveniences? If the application depends on a feature that exists only in the managed environment, that may be a sensible choice—but it is a dependency to record, not a surprise to discover during migration.
Second, I would ask how data is extracted and kept consistent during the transition. A one-time export may be enough for some workloads; others may require a controlled period in which writes continue while the destination is prepared. The available mechanisms, their limitations, and the service’s documented guarantees need to be verified for the chosen product and workload. I would not assume that an architectural description of shared storage answers these questions.
Third, I would test the destination as a real operating environment. A successful restore is only one part of the move. The team also needs to confirm that its monitoring, backup policy, access model, capacity planning, and recovery procedures work there. A design that is portable at the SQL layer but depends on provider-specific operational tooling has only partial portability.
Finally, I would decide whether that degree of portability is worth paying for. Avoiding every provider-specific feature can make a system less useful without making an exit meaningfully easier. The goal is not to preserve a theoretical ability to leave at zero cost. It is to understand the cost, keep it within the organization’s tolerance, and avoid accidental dependence where the consequences would be unacceptable.
Put the exit path in the decision
For HorizonDB Preview, I would evaluate the promised benefits of separated compute and shared storage against the operational boundaries the service creates. Microsoft’s description establishes the architectural direction; it does not, by itself, settle how a particular team should move data, respond to failures, or operate elsewhere. Those questions belong in product evaluation, not in a migration plan written after the system depends on the service.
When comparing a managed PostgreSQL service with PostgreSQL that a team operates itself, I would make the trade explicit. A managed design may reduce the work the team must perform directly. A self-operated design may expose more of the stack to the team, while also making that team accountable for running it. Neither is inherently more portable. Portability depends on the application’s dependencies, the data’s exit path, and whether the destination’s operational model is one the team can sustain.
I would choose the managed architecture when its operating model solves a problem the team is prepared to hand over—and when the service’s failure and exit behavior are clear enough to accept. If those answers are missing, I would treat that as an unresolved architecture decision, not as a detail to defer because the database speaks PostgreSQL.
Drafted with AI assistance, reviewed and edited by Ali.