1---
2title: Cache with Memorystore
3description: Bind a Memorystore Redis instance to a Cloud Run service over Direct VPC egress, and what to expect from create and destroy times.
4---
5
6Memorystore is managed Redis on a private IP. That private IP is the
7whole story: unlike Firestore or Pub/Sub, which a Cloud Run container
8reaches over the public `googleapis.com` endpoint, Redis is only
9reachable from inside your VPC. So a Memorystore binding needs two
10things a normal binding does not — VPC egress on the service, and
11patience while the instance is created.
12
13## The service
14
15```typescript
16export default class Cached extends GCP.Function<Cached>()(
17 "Cached",
18 {
19 main: import.meta.url,
20 location: "us-central1",
21 invokerIamDisabled: true,
22 template: {
23 vpcAccess: {
24 egress: "PRIVATE_RANGES_ONLY",
25 networkInterfaces: [{ network: "default", subnetwork: "default" }],
26 },
27 },
28 },
29 Effect.gen(function* () {
30 const cache = yield* GCP.Redis.Instance("Cache", { memorySizeGb: 1 });
31 const redis = yield* GCP.Redis.ReadWriteRedis(cache);
32
33 return {
34 fetch: Effect.gen(function* () {
35 yield* redis.set("probe", "ok").pipe(Effect.orDie);
36 const cached = yield* redis.get("probe").pipe(Effect.orDie);
37 return yield* HttpServerResponse.json({ redis: cached });
38 }),
39 };
40 }).pipe(Effect.provide(GCP.Redis.ReadWriteRedisHttp)),
41) {}
42```
43
44`template.vpcAccess` with `PRIVATE_RANGES_ONLY` is Direct VPC egress:
45the revision gets an interface on the network, and traffic to private
46ranges goes through it while everything else still leaves normally.
47Without it the container cannot open a socket to the instance and
48every command times out.
49
50## The client is `alchemy/Redis`
51
52`ReadWriteRedis` returns the same RESP client the Fly and Upstash
53providers return, so `get`, `set`, `incr`, `expire`, and the rest
54behave identically no matter who runs the server. Two narrower
55bindings exist when you want least privilege at the call site:
56
57- `GCP.Redis.ReadRedis` / `ReadRedisHttp` — read commands only.
58- `GCP.Redis.WriteRedis` / `WriteRedisHttp` — write commands only.
59- `GCP.Redis.ReadWriteRedis` / `ReadWriteRedisHttp` — both.
60
61The binding needs no IAM role: RESP authenticates with the instance's
62AUTH string. Alchemy resolves the connection URL (private IP, port,
63AUTH string) when the instance reconciles and carries it to the runtime
64through the host's runtime context, keyed per instance — so the private
65IP is never something you paste in, and two instances bound to one host
66never collide.
67
68## Expect minutes, not seconds
69
70Creating a Memorystore instance takes several minutes; so does
71deleting one. That is Google's provisioning time, not Alchemy's. Two
72consequences worth knowing before you put this in a deploy loop:
73
74- Destroy waits out a `CREATING` instance and then retries `Conflict`
75 until the instance is actually gone, rather than failing on the
76 first race.
77- The repository's live tests keep this path behind `GCP_TEST_REDIS=1`
78 so the normal suite is not held up by a ten-minute resource.
79
80For a cache that has to come up with the service, size the instance
81once and leave it alone; treat it as slower-moving than the code
82deployed in front of it.
83
84## Next
85
86- [How bindings grant IAM](/gcp/guides/bindings).
87- [Serve an API on Cloud Run](/gcp/guides/cloud-run-api).
88- [Redis.Instance](/providers/gcp/redis/instance) reference.