Most zero-trust conversations start with people: single sign-on, MFA, device posture, and conditional access for engineers and employees. That work matters. It is also only half the traffic in a modern estate.
The other half never touches a browser. A checkout service calls an inventory service. A reporting job reads from a database in another cloud. An inference endpoint receives requests from an application running somewhere else entirely. Those service-to-service calls are usually trusted because of where they come from — a VPC, a subnet, a VPN tunnel, a private range that was considered safe when it was drawn.
That assumption is the problem zero trust was meant to remove. If network location is the credential, then anything that reaches that network segment inherits the trust, including a compromised workload, a misconfigured job, and a test service that was never meant to run in production.
1. Give every workload an identity that is not an IP address
The first step is separating identity from location.
A workload identity should answer three questions on every call: which service is calling, in which environment, and with what proof that the identity was issued recently to that exact workload?
Patterns that hold up in practice:
- Short-lived credentials over static keys. A token or certificate that expires in minutes limits the damage from a leak in a way a long-lived API key copied into configuration never can. Rotation stops being a calendar event when issuance is automatic.
- Identity bound to the workload, not the host. Containers move, instances are replaced, and jobs run once and disappear. Tying trust to a machine name or an address breaks in exactly the environments where automation is working correctly.
- Separate identities per environment. Production, staging, and development should not share a service identity, even when they share code. Environment confusion is a common incident pattern; distinct identities make it a policy denial instead of a data incident.
- No shared service accounts across teams. When five services authenticate as the same generic account, logs can show what happened but not who did it. Per-service identity is what makes an authorization policy, and an audit trail, meaningful.
Cloud platforms across AWS, Azure, Google Cloud, and Oracle all offer workload identity mechanisms, but they do not automatically compose across clouds. In a multi-cloud estate, the design work is choosing one consistent identity model for cross-cloud calls instead of letting each cloud's defaults create a different trust story at every boundary.
2. Authorize the call, not just the connection
A firewall rule that allows one subnet to reach another on a port answers a connectivity question. It does not answer an authorization question.
Zero trust between services means each call is evaluated against policy: is this caller allowed to perform this action on this resource, in this environment, right now?
That evaluation does not need to be slow or centralised for every request, but it does need to be explicit:
- Default deny between services. A new service should reach nothing until its outbound and inbound relationships are declared. The declaration is documentation that enforces itself.
- Policy at the resource, not only at the perimeter. If a database accepts connections from the whole application tier, the tier is the perimeter. Narrow the rule to the named services that actually read and write that store.
- Distinguish read, write, and admin paths. Many flat designs grant a reporting job the same path as the transactional service that owns the data. Separate policies for read-only consumers make least privilege practical instead of theoretical.
- Make policy reviewable as code. A policy expressed in versioned configuration can be reviewed in a pull request, tested in staging, and rolled back. A policy that lives only as portal clicks cannot.
A useful test is to pick an internal service at random and ask: which named services are allowed to call it, and where is that written down in a form that would still be true after the team changes? If the answer is a diagram from last year, the policy does not really exist yet.
3. Encrypt and authenticate in transit, even on private paths
Private connectivity reduces exposure. It does not remove the need to authenticate the ends of a conversation.
Mutual TLS, signed tokens, or an equivalent authenticated transport should be the normal path for service-to-service traffic, including traffic that never leaves a private network. That provides two things a private route alone cannot: proof that the other end is the intended service, and protection if a route, tunnel, or segment is misconfigured later.
The operational lesson from teams that have rolled this out is to automate certificate and credential issuance from the start. Manual certificate handling is where service identity projects stall — not because the cryptography is hard, but because expiry becomes an outage pattern. Issuance, renewal, and revocation belong in the platform, with alerts on failed renewal long before expiry.
Start with the highest-value paths if a full rollout is unrealistic: calls that carry customer data, calls that cross a cloud or account boundary, and administrative interfaces. Then expand. A partial rollout with clear coverage is better than a mandate that every team quietly routes around.
4. Log the decision, not just the traffic
Traditional flow logs show that bytes moved between addresses. Zero-trust systems should also record the identity decision: which workload identity called which service, whether policy allowed it, and which policy version was in effect.
That record changes troubleshooting as much as it changes security:
- A denied call from a legitimate service usually means a missing policy after a deployment, visible immediately instead of discovered as a silent failure.
- An allowed call from an unexpected identity is a signal worth alerting on, even if the data accessed was ordinary.
- Repeated denials from the same workload often reveal a stale configuration or a retired service that was never decommissioned.
Treat denials as telemetry, review them weekly during a rollout, and tune policy from evidence. A zero-trust deployment that generates denials nobody reads trains teams to request broad permissions to make the noise stop — which recreates the flat network, one exception at a time.
5. Roll it out without rebuilding everything at once
The estates that succeed rarely start with a flag day. They start with inventory and observation:
- List the real service-to-service flows from flow logs and application dependencies, not from architecture diagrams alone.
- Issue identities to workloads in observe mode, logging which identity would have called what, before enforcing denial.
- Enforce default-deny first on new services, where there is no legacy behaviour to break.
- Move the highest-risk existing paths — cross-cloud, cross-environment, and data-store access — to authenticated, policy-checked calls.
- Retire the broad network rules those paths relied on. This last step is the actual security gain, and it is the step most often skipped, leaving the new controls layered on top of the old trust instead of replacing it.
That sequencing also clarifies where help is useful. Designing identity, policy, and private connectivity consistently across several clouds is the combined problem CloudVerg works on for US businesses — private cloud and AI connectivity with VergShield covering zero-trust security alongside the wider platform set (VergMesh, VergForge, VergMind, and VergRail): https://cloudverg.com
Whether the work is handled internally or with outside support, the measure of success is the same: a network location on its own should no longer be enough for one service to reach another.
A short checklist
For each important service-to-service path:
- [ ] Caller and callee each have a distinct workload identity, separate per environment
- [ ] Credentials are short-lived and issued automatically; no shared static keys remain on the path
- [ ] Policy names which services may call which resources, with read, write, and admin separated
- [ ] Default is deny; new services start with no access until relationships are declared
- [ ] Transport is authenticated and encrypted, including on private routes
- [ ] Allowed and denied calls are logged with identity and policy outcome, and denials are reviewed
- [ ] Broad subnet-to-subnet rules retired once identity-based policy is enforced
Zero trust for users changed how people sign in. Zero trust between services changes something more fundamental: it replaces inherited trust in a network diagram with explicit, checkable permission for every conversation that matters. In a multi-cloud estate, that is the difference between a segmentation story and segmentation that actually holds.

