---
cuppy_hub: gcp
cuppy_path: guides/networking/reach-a-private-service.md
cuppy_section: docs
cuppy_doc: ENA4-6BSK
cuppy_doc_uuid: 6b511697-32e3-43d7-9358-fabf79f7973d
cuppy_permalink: https://cuppy.io/gcp/d/ENA4-6BSK
cuppy_version: 1
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/gcp/docs/guides/networking/reach-a-private-service
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: ac844d94ce87392dd180653aec18c895a5a4e86e3ecb39ac53a5d3ce84c9b846
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# Reach a service privately

Use this instead of peering whenever the thing you need is **a service**, not a network. Peering joins whole
address spaces, is not transitive, and fails outright on overlapping ranges; Private Service Connect joins one
service to one internal address and does none of those things.

## Step 1 — endpoint or backend

| Choose | When |
|---|---|
| **endpoint** | the workload just needs to call the service. One forwarding rule, one internal IP address. This is the default answer |
| **backend** | you want the service behind **your own** load balancer — so your own policies, including a Cloud Armor security policy on a regional internal Application Load Balancer, sit in front of it |

A backend is more objects and more control. Do not reach for it unless you want the load balancer for a
reason you can name.

## Step 2 — reserve the consumer-side address

An endpoint **consumes an internal IP address from a subnet in the consumer's VPC network**. Reserve it
deliberately, in the subnet the calling workload lives in, and record it in the address plan.

At one or two connections this is invisible. At a hundred it is a real allocation, and it is the line most
often missing from a migrated address plan.

## Step 3 — the producer publishes a service attachment

On the producer side (which may be another team, or another company):

- a **service attachment** targeting the producer's load balancer;
- a **consumer accept list** — who is allowed to connect;
- **NAT subnets** for the translated traffic.

**The accept list is the authorisation boundary.** The consumer cannot connect without being on it, and the
producer does not have to trust the consumer's network to let them in.

If the producer also needs to initiate calls *into* the consumer — a managed data plane reading a customer
database — that is a producer-side **interface**, the reverse-direction object, and not a second endpoint.

## Step 4 — connect, and confirm which object you got

The consumer creates the endpoint (or the backend) against the attachment. Confirm the connection's state on
both sides before testing anything: a pending accept-list approval looks exactly like a network fault from the
consumer's side.

## Step 5 — allow the egress

Egress is implied-allow on a fresh VPC network, so this step is a no-op there. On a hardened network with
explicit egress rules it is not: add the rule to the endpoint's address. This is the step people skip because
it worked in the sandbox.

## Step 6 — verify that the path is actually private

| Check | Expected |
|---|---|
| resolve and connect to the endpoint address from the consumer workload | **success**, on an internal address from your own subnet |
| confirm the workload needs no external IP and no NAT for this call | correct — the traffic stays within Google Cloud |
| remove the consumer from the accept list and retry | **the connection fails** |

The third row is the one that proves it. A path that still works after you revoke the authorisation was never
going through the mechanism you think it was.

## For a migration

This is the counterpart of AWS PrivateLink and Azure Private Link, and Microsoft's own mapping table pairs it
with Private Link. Designs port. What does not port automatically is the address plan: every private
connection consumes a consumer-side address on **all three** clouds. Count them before you commit a subnet
size. See `guides/parity/networking.md`.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 91 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/gcp/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
