Multi-cloud is usually sold as resilience. In practice, most teams end up there because of an acquisition, a data residency rule, or a single very persuasive procurement conversation.
Whatever the reason, you now have three key management services that do not talk to each other, and an auditor who expects one story.
The naive pattern, and why it fails
The obvious move is to let each cloud manage its own keys with its own service. It works, it is cheap, and it is what the quickstart guides show.
It fails at the first audit. Each provider gives you a key rotation log in its own format, with its own clock, and its own idea of what counts as an access event. Reconciling three of those into a single answer to a question like who decrypted this record on 12 March is a week of work every time somebody asks.
It fails worse during an incident, when you have hours rather than a week.
What we run instead
The pattern that has held up for us separates three concerns that the cloud providers deliberately bundle together.
Key material stays where it is. We do not try to move keys between providers. That is where most designs get expensive and fragile.
Key policy lives in one place. Rotation schedule, grant expiry, and which workloads may request a decrypt are declared once, in a repository, and pushed to each provider. When the policy changes, it changes everywhere in one commit.
Key usage is recorded in one place. Every decrypt request passes through a thin broker before it reaches the provider. The broker adds nothing cryptographically. It exists so there is a single ordered log of who asked for what, in one clock, in one format.
The broker is the whole trick
People resist the broker because it looks like a single point of failure sitting in the hot path of every read.
It is not, if you build it correctly. The broker authorises and records. It does not hold key material and it does not perform the decrypt. If it goes down, you can fail open to direct provider access with a loud alarm, and reconcile the gap afterwards. We have done this twice. Both times the reconciliation took under an hour, because the gap had known boundaries.
Compare that to the alternative, where the gap is permanent and shaped like three incompatible log formats.
Residency is a routing problem
One more thing that catches teams out. Data residency rules almost never say that data must be encrypted with a key in a given region. They say the data must not leave the region.
That means residency is enforced at the routing layer, not the key layer. Trying to enforce it with key placement produces a design where a single misrouted request is both a residency breach and a decrypt failure, and you find out about it as a 500.
Route first. Encrypt second. Record always.