# Do you still need Redis on Cloud Run? Each job it does, what replaces it, and what it costs if it stays

> A PaaS Redis collects sessions, cache, queue, rate limits and locks; on Cloud Run it is Memorystore billed every hour. How to move each job to Postgres or Cloud Tasks, and when Redis stays.

Published 2026-10-12 · Updated 2026-10-13 · [By Exodus team](https://exodus.elim.io/about) · Patterns, Data
<https://exodus.elim.io/blog/do-you-still-need-redis-on-cloud-run>

Most small apps on Cloud Run do not need Redis. Each of its jobs has a home that costs nothing extra: sessions in a signed cookie, the cache, rate limits and locks in the Postgres you already run, and queues in Cloud Tasks. Redis stays for libraries that speak only Redis, its data structures and hot keys, and Memorystore bills every hour, from $25.55 a month in Belgium.

## In short

- A PaaS Redis usually does 6 jobs: sessions, a cache, a job queue, rate limits, pub/sub and locks.
- Memorystore bills every hour, used or not: $25.55 a month for the Valkey nano node, $37.23 for Redis Basic with 1 GiB, $62.78 for Standard.
- With one client, Valkey answered a GET in about 0.02 ms, and Postgres read a cache row in 0.06 ms and ran the rate-limit upsert in 0.08 ms.
- A per-command Redis outside Google costs $0.20 per 100,000 commands and passes the Valkey nano at about 12.8 million commands a month.
- If Redis stays, Direct VPC egress needs a subnet of /26 or larger, and a Basic instance is unavailable for about five minutes during maintenance.

## What a Redis does in a typical app

On Heroku, Render or Railway a Redis is one click, in a [Docker Compose stack](https://exodus.elim.io/blog/docker-compose-to-cloud-run) one more service, and over the years it collects jobs: each library finds the `REDIS_URL` and uses it. Six, in rough order of how often they show up:

1. **Sessions.** The login lives in Redis because the app runs on more than one process.
1. **A cache.** Rendered fragments, API responses, expensive queries, with a time to live.
1. **A job queue.** Sidekiq, BullMQ, Celery, RQ or Laravel's `redis` queue keep their jobs in Redis lists and sorted sets.
1. **Rate limits.** A counter per IP or per user, incremented on each request and expired after a minute.
1. **Pub/sub.** Action Cable, Django Channels, Socket.IO and Laravel broadcasting fan messages out between processes through Redis.
1. **Locks.** `SET key value NX EX 60`, so that only one process runs the nightly report.

Each job needs state shared by every instance. On Cloud Run that need gets stronger, because instances start and stop with traffic and each request goes to whichever one is free. What changes is the price of the place where the state lives.

![Six rows: sessions go to a signed cookie or a sessions table; a cache to instance memory plus an UNLOGGED table; a job queue to Cloud Tasks or a jobs table; rate limits to one counter row per key and minute; pub/sub to Postgres LISTEN/NOTIFY; locks to pg_try_advisory_xact_lock or one Cloud Scheduler job. Below: Redis stays for a library that speaks only Redis, sorted sets or a hot key](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-d7917048789f1a947adde13fd80e3a26201e9ca7742328ce1eb84d8956628e4b-680x470-svg/bytes "Where each of Redis's usual jobs goes on Cloud Run, and when Redis stays.")

## What a Redis costs on Google Cloud

Prices are Google's list prices of October 12, 2026 for europe-west1 (Belgium), before VAT, read from the Cloud Billing Catalog API; a month is 730 hours. Google's pricing pages show a cheaper US region by default, so their figures are lower than these.

- **Memorystore for Valkey, shared-core-nano (1.4 GB): $0.035 an hour, $25.55 a month.** Valkey is an open source, Redis-compatible engine (Sidekiq, for one, lists it as a supported alternative), and Memorystore runs it as a separate product. Google says this node has no SLA and is unsuitable for production. The smallest node Google recommends for production, standard-small (6.5 GB), is $0.1569 an hour: $114.54 a month, or $229.07 with one replica.
- **Memorystore for Redis, Basic, 1 GiB: $0.051 per GiB-hour, $37.23 a month.** One node and no replica; Google calls Basic suitable for a cache that can withstand a cold restart and a full flush, and maintenance makes it unavailable for about five minutes.
- **Memorystore for Redis, Standard, 1 GiB: $0.086 per GiB-hour, $62.78 a month.** A replica and automatic failover; maintenance is a failover of about 15 seconds.
- **A per-command Redis outside Google:** Upstash's pay-as-you-go plan is $0.20 per 100,000 commands. A million commands a month is $2.00, ten million $20.00, and at about 12.8 million it passes the Valkey nano. An app that makes 10 requests a second around the clock with two Redis calls each sends 52.6 million commands a month: $105.12. Traffic to it leaves Cloud Run as internet egress, see [egress on Google Cloud](https://exodus.elim.io/blog/egress-on-google-cloud-explained).
- **The Postgres you already run: $0.00 extra.** A cache table and a counter table on a database that is already billed.

Memorystore bills for provisioned capacity per second, whether anyone uses it or not, and it does not scale to zero. For an app whose Cloud Run bill is a few dollars, the Redis is often the largest line.

![Bar chart of a month's cost: the Postgres you already run $0.00, a per-command Redis at 1M commands $2.00, Memorystore for Valkey shared-core-nano $25.55, Memorystore for Redis Basic 1 GiB $37.23, Memorystore for Redis Standard 1 GiB $62.78](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-116f1aafb0879bb9b9b04541294fcaad89b2023252e1ba154dec6412139b6349-680x350-svg/bytes "A month of a Redis by where it lives, europe-west1 list prices of 2026-10-12, before VAT.")

## Step 1: find out what your Redis does

Start with the code. From the repository root:

```shell
grep -rniE "redis|valkey|sidekiq|bullmq|celery" --exclude-dir=node_modules --exclude-dir=.git .
```

On a test folder with one example per stack it found the BullMQ import, an ioredis client, the Celery broker URL, the Sidekiq gem, Django Channels' `RedisChannelLayer`, Laravel's `'limiter' => 'redis'` and the `REDIS_URL` in `.env`, and skipped `node_modules`. Each hit is a job to move or a reason to keep Redis.

Then ask the Redis itself, because libraries use it without saying so in your code. `redis-cli` reads the password from the `REDISCLI_AUTH` environment variable, so it never goes on the command line; `read -rs` takes it without echoing it:

```shell
read -rs REDISCLI_AUTH && export REDISCLI_AUTH
redis-cli -h HOST -p PORT --tls --scan | cut -d: -f1 | sort | uniq -c | sort -rn
redis-cli -h HOST -p PORT --tls info commandstats | cut -d, -f1
```

Drop `--tls` if the provider's URL starts with `redis://` rather than `rediss://`. The first command counts keys by their first segment; `--scan` walks the keyspace with SCAN, a cursor that returns a few keys per call. The second lists how many times each command ran since the server started: GET and SET are a cache or sessions, LPUSH and BRPOP a queue, INCR with EXPIRE a rate limiter, PUBLISH pub/sub. Pub/sub leaves no keys, which is why the command counts matter.

![Terminal: redis-cli --scan grouped by prefix prints 300 cache, 40 sess, 30 rl, 26 bull and 1 lock; info commandstats prints set 343, get 1200, lpush 25, rpop 20, auth 3, hset 25, expire 500, publish 50 and incr 500 calls, with notes pointing each to the step of this guide that moves it](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-868d2bcea53e62af479dff041d19483662fff78419dfe34e5981d36c0d3b2759-680x400-svg/bytes "Step 1's two commands on the sample's Redis, filled the way a typical app fills it.")

## Step 2: sessions

Sessions have their own guide, [sessions without Redis](https://exodus.elim.io/blog/sessions-without-redis): a signed cookie with keys from Secret Manager, or a sessions table in Postgres, plus a `session_version` column so "log out everywhere" still works. Both cost nothing extra.

## Step 3: the cache

A cache has two layers worth separating.

**Instance memory.** Google's own tip for Cloud Run is to keep expensive objects in global variables, because an instance serves many requests and reuses them; it also says you cannot know which request benefits. A small in-process cache with a time to live is free and the fastest option there is, and it fits values that are the same for every user: a config document, a price list, a parsed template. Its limits: each instance has its own copy, a new instance starts empty, and deleting a key on one instance does not delete it on the others. Django's default cache, `LocMemCache`, is exactly this: per-process.

**A shared table.** For what every instance must see the same way, a table in Postgres does the job. Make it `UNLOGGED`: Postgres then skips its write-ahead log for that table, which makes writes considerably faster, and empties it after a crash or an unclean shutdown. On Cloud SQL, Google adds that unlogged tables do not survive a failover on an HA instance, are not replicated to read replicas and are wiped when a backup is restored. For a cache that is the right trade.

```
create unlogged table if not exists cache (
  key        text primary key,
  value      jsonb not null,
  expires_at timestamptz not null
);

-- read: a miss is no row
select value from cache where key = $1 and expires_at > now();

-- write
insert into cache (key, value, expires_at) values ($1, $2, now() + interval '10 minutes')
on conflict (key) do update set value = excluded.value, expires_at = excluded.expires_at;

-- clean up, on a schedule
delete from cache where expires_at < now();
```

Run the cleanup from Cloud Scheduler, see [cron and timers](https://exodus.elim.io/blog/cron-and-timers-with-cloud-scheduler). The frameworks have this built in:

- **Rails:** Solid Cache is the default cache store for new apps since Rails 8.0. It is database-backed and evicts first in, first out.
- **Django:** `DatabaseCache`, after `python manage.py createcachetable`.
- **Laravel:** the `database` store is already the default.
- **Express:** no built-in shared cache; the three statements above are the whole implementation.

## Step 4: the job queue

A queue in Redis comes with a worker process that runs all the time and pulls jobs. On Cloud Run that worker has nowhere natural to live: with the default, request-based billing, a service gets CPU only while it handles requests (see [work after the response](https://exodus.elim.io/blog/work-after-the-response)).

The usual move is to let Cloud Tasks hold the queue and call your service over HTTP for each job, with retries and rate limits. [Background workers with Cloud Tasks](https://exodus.elim.io/blog/background-workers-with-cloud-tasks) sets it up end to end.

If the framework already has a database queue, that works too, with a process that polls the table:

- **Rails:** Solid Queue keeps jobs in Postgres and takes them with `FOR UPDATE SKIP LOCKED`, so several workers never pick the same job.
- **Laravel:** in new applications the `database` connection is already the default queue.

Some libraries need Redis and nothing else. Sidekiq requires Redis 7.0, Valkey 7.2 or Dragonfly; BullMQ is built on Redis; Celery's stable brokers are RabbitMQ, Redis and Amazon SQS, with Google Pub/Sub listed as experimental. Keeping one of them means keeping both the Redis and an always-on worker. On Cloud Run that worker is a worker pool, which Google built for continuous background work: no URL, and a number of instances you set rather than autoscaling. See [services, jobs and worker pools](https://exodus.elim.io/blog/cloud-run-services-jobs-and-worker-pools).

## Step 5: rate limits

A limiter that counts in memory counts per instance. express-rate-limit, for example, ships with a memory store and needs an external store to share hit counts across nodes; with five instances, a limit of 5 a minute quietly becomes up to 25.

One upsert in Postgres replaces INCR and EXPIRE. It counts hits per key in the current 60-second window and returns the count, so the app compares it with the limit:

```
create unlogged table if not exists rate_limits (
  key          text primary key,
  window_start timestamptz not null,
  hits         integer not null
);

insert into rate_limits as r (key, window_start, hits)
values ($1, date_bin('60 seconds', now(), timestamptz 'epoch'), 1)
on conflict (key) do update
  set hits = case when r.window_start = excluded.window_start then r.hits + 1 else 1 end,
      window_start = excluded.window_start
returning hits;
```

`date_bin` rounds the time down to the start of the minute. Postgres guarantees `ON CONFLICT DO UPDATE` an atomic insert or update even under high concurrency, so two instances hitting the same key at once both count. Delete old rows on the same schedule as the cache (`delete from rate_limits where window_start < now() - interval '1 hour'`).

Laravel's rate limiter already uses the default cache, and the `database` store increments atomically. For limits that should stop traffic before it reaches your container, Cloud Armor has throttle and rate-based ban rules; they live in a security policy on a load balancer, which is a separate setup and bill.

## Step 6: pub/sub and locks

**Pub/sub.** A live page needs every instance to hear about a new message. Postgres has this built in as LISTEN/NOTIFY; [WebSockets and SSE on Cloud Run](https://exodus.elim.io/blog/websockets-and-sse-on-cloud-run) wires it up, with the replay table that makes it reliable. Rails' Solid Cable does the same for Action Cable from the database. Django Channels is the exception: its docs name `channels_redis` as the only official layer for production, and the in-memory layer is not for production.

**Locks.** A transaction-level advisory lock is held until the transaction ends and cannot be leaked by a crashed process:

```
begin;
select pg_try_advisory_xact_lock(hashtext('nightly-report'));  -- true: run it; false: another instance is
-- ... the work ...
commit;  -- the lock goes with the transaction
```

Often the lock is there because every server ran the same cron. On Cloud Run the better fix is one Cloud Scheduler job that calls the service once, and the lock stays only as a guard against retries. Laravel's `WithoutOverlapping` and unique jobs need a cache driver with locks, and the `database` driver has them.

## Try it before the deploy

The sample's script fills a local Valkey the way a typical app does, runs Step 1's commands on it (above), then runs the SQL from this guide from separate Postgres sessions standing in for two instances, and times the same operations on both stores:

![Terminal output of try.sh: rate limit hits 1 to 5 allowed alternating between instances A and B, hit 6 refused with 429; instance B reads the cache row A wrote, a miss after expiry; the cleanup deletes 1 expired cache row; instance A takes the advisory lock, B gets false while A holds it and true after A commits. Timings with one client: Valkey GET 50,403 a second at p50 0.023 ms, INCR 48,192 at 0.023 ms; Postgres cache read 16,441 a second at 0.061 ms average, rate limit upsert 12,603 at 0.079 ms. With 16 clients Valkey does about 155,000 to 159,000 a second and Postgres 140,000 to 170,000](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-3365bada377a3fa6864358f940cb87b032450bad27151ac75d8ab494f4f94949-680x536-svg/bytes "The sample run: the Postgres replacements from two sessions, then the same operations timed on Valkey 8.1 and Postgres 17.")

With one client on this box, Valkey answers a GET in about 0.02 ms and Postgres reads a cache row in 0.06 ms and runs the rate-limit upsert in 0.08 ms. Redis is faster, and both are far below the time of a web request. On Google Cloud both sit across a network hop, which adds the same delay to each. The numbers come from one machine over loopback, Valkey's tool reports the median and pgbench the average, so read them as an order of magnitude, not a benchmark of Cloud SQL.

The real cost is the extra load: a rate-limit check is one more write per request on your database. If the database is already near its limits, measure before you move the counters.

## When Redis stays

Keep the Redis when:

- **A library speaks only Redis** and you are not replacing it: Sidekiq, BullMQ, Celery on Redis, Django Channels.
- **You use its data structures:** sorted sets for leaderboards, streams, HyperLogLog, or Lua scripts that do several steps in one round trip.
- **One key is hot:** thousands of writes a second to the same counter, where a row lock in Postgres would queue them.
- **The cache is large and busy enough** that it would crowd the database's memory.

Then pick the size by the job. A cache that can be lost fits Basic or the Valkey nano; a queue whose jobs must not vanish during maintenance needs Standard or a Valkey node Google recommends for production.

## If it stays: Memorystore on Cloud Run

Memorystore for Redis uses internal IP addresses on your VPC network, so the service needs a way into that network. Google recommends Direct VPC egress: the service's instances get addresses in one of your subnets, and you pay only for network traffic, with no VMs. The alternative, a Serverless VPC Access connector, runs at least two VMs that bill even when there is no traffic.

```shell
gcloud services enable redis.googleapis.com
gcloud redis instances create cache --region=europe-west1 --tier=basic --size=1 --enable-auth
gcloud redis instances describe cache --region=europe-west1 --format='value(host,port)'
```

The instance joins the network called `default` unless `--network` says otherwise, and new instances use direct peering. With AUTH on, every connection must send a 36-character AUTH string first. Move it into Secret Manager through a pipe, so it is never printed:

```shell
gcloud redis instances get-auth-string cache --region=europe-west1 --format='value(authString)' \
  | gcloud secrets create redis-auth --data-file=-
```

Grant the service's account `roles/secretmanager.secretAccessor` on that secret, as [sessions without Redis](https://exodus.elim.io/blog/sessions-without-redis) shows, then deploy with Direct VPC egress:

```shell
gcloud run deploy shop --image IMAGE --region=europe-west1 \
  --network=default --vpc-egress=private-ranges-only \
  --set-env-vars=REDISHOST=10.0.0.3,REDISPORT=6379 --set-secrets=REDISAUTH=redis-auth:1
```

Use the host and port that `describe` printed. With only `--network`, the subnet takes the network's name; it must be a /26 or larger, and Cloud Run uses about two of its addresses per instance. `private-ranges-only` sends only traffic for internal addresses through the VPC, so calls to the internet do not change path.

![Three lanes: your Cloud Run service with REDISHOST, REDISPORT and REDISAUTH, the AUTH string coming from Secret Manager; Direct VPC egress into your VPC network default through a /26 or larger subnet, or a connector of 2 or more VMs billed when idle; Memorystore for Redis Basic 1 GiB on an internal IP with AUTH, reached on port 6379, at $37.23 a month with or without traffic](https://exodus.elim.io/media/v2026-09-16/t/exodus/assets/image-5e24adf2807277f6aeb25c1d7d801ca4bdb491c8c38d7eaf40a98a71a964c7f3-680x380-svg/bytes "How a Cloud Run service reaches Memorystore for Redis.")

Two more decisions:

- **Encryption in transit.** AUTH alone is not encrypted on the wire. `--transit-encryption-mode=server-authentication` at creation turns on TLS: the client connects on port 6378 and must trust the instance's certificate authority, which you download and ship with the app.
- **Valkey instead of Redis.** Memorystore for Valkey connects only through Private Service Connect, which needs a service connection policy on the network before the first instance; it is more setup than direct peering, for a cheaper smallest node that Google does not recommend for production.

Write the client to reconnect. Both tiers reset connections during maintenance, and a Basic instance is gone for about five minutes of it.

## What breaks

- **Rate limits stop working after the move.** The limiter still counts in instance memory. Point it at a shared store.
- **Counters reset at random.** The `UNLOGGED` table was emptied by a crash, a Cloud SQL failover or a restore. Acceptable for counters and a cache, never for anything you cannot recompute.
- **The database is slower after the move.** Rate-limit writes on every request add up. Check only the routes that need limits, or keep a Redis for counters.
- **Jobs pile up in a queue nobody reads.** The worker was a separate process on the old platform and was never deployed. Move to Cloud Tasks or deploy a worker pool.
- **The service cannot reach Memorystore.** It has no VPC egress, egress points at another network, or the instance is in another region. `gcloud redis instances describe cache --region=europe-west1 --format='value(authorizedNetwork)'` names the network to use.

## Checklist

1. Grep the code and run `--scan` and `info commandstats` on the current Redis; write down each job.
1. Move sessions to a signed cookie or a table.
1. Move the cache to instance memory plus an `UNLOGGED` table, or the framework's database store.
1. Move queues to Cloud Tasks or a database queue; for Sidekiq, BullMQ or Celery, plan a worker pool.
1. Move rate limits and locks to Postgres, and crons to Cloud Scheduler.
1. Schedule the cleanup of expired cache rows and old counters.
1. Run `info commandstats` again after the deploy: the counts should stop growing.
1. If Redis stays: Basic or Valkey nano for a cache, Standard or an SLA node for a queue; Direct VPC egress; the AUTH string in Secret Manager.
1. Delete the old Redis only after a week with no new commands.

## Where Exodus comes in

Exodus's CLI reads the repository with your coding agent and lists what the Redis is used for: the session store, the cache backend, the queue library, the limiter and the channel layer. Each becomes a finding in the report with the replacement from this guide, and the cost comparison shows the month with and without Memorystore. Your agent can read this guide from the terminal with `exodus kb`.

## FAQ

### How much does Memorystore cost?

The smallest Memorystore for Valkey node, shared-core-nano with 1.4 GB, is $25.55 a month in europe-west1, but Google says it has no SLA and is unsuitable for production. Memorystore for Redis Basic with 1 GiB is $37.23 and Standard $62.78. It bills for provisioned capacity per second and does not scale to zero.

### Can Postgres replace Redis as a cache?

For most small apps, yes. An UNLOGGED table skips the write-ahead log and is emptied after a crash; on Cloud SQL it also does not survive a failover or a restore, which a cache can accept. Rails' Solid Cache, Django's DatabaseCache and Laravel's database store do this for you.

### How do I run Sidekiq or BullMQ on Cloud Run?

Both need Redis: Sidekiq requires Redis 7.0, Valkey 7.2 or Dragonfly, and BullMQ is built on Redis. Keep a Memorystore instance and run the always-on worker as a Cloud Run worker pool, which has no URL and a number of instances you set, or move the jobs to Cloud Tasks.

### How does Cloud Run connect to Memorystore?

Through your VPC network. Google recommends Direct VPC egress, on a subnet of /26 or larger, with traffic to private ranges only, and you pay only for network traffic. A Serverless VPC Access connector also works, but it runs at least two VMs that bill even with no traffic.

### Why do rate limits stop working after moving to Cloud Run?

A limiter that counts in memory counts per instance, so with five instances a limit of 5 a minute becomes up to 25. Point it at a shared store: one upsert into an UNLOGGED Postgres table per request, or Laravel's database cache store.

## Sources

1. [Connect to a Redis instance from a Cloud Run service, Google (Direct VPC egress recommended over connectors, a /26 subnet or larger, REDISHOST and REDISPORT; read Oct 12, 2026)](https://docs.cloud.google.com/memorystore/docs/redis/connect-redis-instance-cloud-run)
2. [Memorystore for Redis pricing, Google (billed per second for provisioned GiB; M1 Basic $0.049 and Standard $0.064 per GiB-hour in the region shown; read Oct 12, 2026)](https://cloud.google.com/memorystore/docs/redis/pricing)
3. [Redis tier capabilities, Google (Basic for a cache that can withstand a cold restart and full flush, Standard replicates and fails over; read Oct 12, 2026)](https://docs.cloud.google.com/memorystore/docs/redis/redis-tiers)
4. [Memorystore for Redis: about maintenance, Google (Basic unavailable for about 5 minutes, Standard fails over in about 15 seconds; read Oct 12, 2026)](https://docs.cloud.google.com/memorystore/docs/redis/about-maintenance)
5. [Create and manage Redis instances, Google (gcloud redis instances create, Basic tier and 1 GiB by default; read Oct 12, 2026)](https://docs.cloud.google.com/memorystore/docs/redis/create-manage-instances)
6. [Memorystore for Redis networking, Google (internal IP addresses, direct peering by default; read Oct 12, 2026)](https://docs.cloud.google.com/memorystore/docs/redis/networking)
7. [About Redis AUTH, Google (a 36-character string, not encrypted in transit unless in-transit encryption is on; read Oct 12, 2026)](https://docs.cloud.google.com/memorystore/docs/redis/about-redis-auth)
8. [Manage in-transit encryption, Google (TLS on port 6378 with the instance's CA; read Oct 12, 2026)](https://docs.cloud.google.com/memorystore/docs/redis/manage-in-transit-encryption)
9. [Method instances.getAuthString, Memorystore for Redis API, Google (returns authString; read Oct 12, 2026)](https://docs.cloud.google.com/memorystore/docs/redis/reference/rest/v1/projects.locations.instances/getAuthString)
10. [Memorystore for Valkey pricing, Google (per node-hour; shared-core-nano $0.0318 and standard-small $0.1425 in the region shown; read Oct 12, 2026)](https://cloud.google.com/memorystore/valkey/pricing)
11. [Memorystore for Valkey: instance and node specification, Google (shared-core-nano 1.4 GB, no SLA, unsuitable for production; standard-small 6.5 GB; read Oct 12, 2026)](https://docs.cloud.google.com/memorystore/docs/valkey/instance-node-specification)
12. [Memorystore for Valkey networking, Google (Private Service Connect through a service connection policy is the only method; read Oct 12, 2026)](https://docs.cloud.google.com/memorystore/docs/valkey/networking)
13. [Compare Direct VPC egress and VPC connectors, Google (Direct VPC egress: network charges only; connectors bill VMs; read Oct 12, 2026)](https://docs.cloud.google.com/run/docs/configuring/connecting-vpc)
14. [VPC with connectors, Google (2 instances minimum, charges continue with no traffic; read Oct 12, 2026)](https://docs.cloud.google.com/run/docs/configuring/vpc-connectors)
15. [Direct VPC egress with a VPC network, Google (--network, --subnet, --vpc-egress private-ranges-only, about 2 IP addresses per instance; read Oct 12, 2026)](https://docs.cloud.google.com/run/docs/configuring/vpc-direct-vpc)
16. [General development tips, Cloud Run, Google (global variables are reused across requests on one instance, no guarantee; read Oct 12, 2026)](https://docs.cloud.google.com/run/docs/tips/general)
17. [Cloud SQL backups overview, Google (unlogged tables do not survive failover or crashes, are not replicated, are wiped on restore; read Oct 12, 2026)](https://docs.cloud.google.com/sql/docs/postgres/backup-recovery/backups)
18. [Deploy worker pools to Cloud Run, Google (continuous background work, no endpoint, no autoscaling; read Oct 12, 2026)](https://docs.cloud.google.com/run/docs/deploy-worker-pools)
19. [Billing settings for services, Google (with request-based billing, CPU is only allocated during request processing; read Oct 12, 2026)](https://docs.cloud.google.com/run/docs/configuring/billing-settings)
20. [Memorystore for Valkey: about scaling instance capacity, Google (standard-small, highmem-medium or highmem-xlarge recommended for production; read Oct 12, 2026)](https://docs.cloud.google.com/memorystore/docs/valkey/about-scaling-instance-capacity)
21. [Cloud Armor security policy overview, Google (security policies attach to load balancers; read Oct 12, 2026)](https://docs.cloud.google.com/armor/docs/security-policy-overview)
22. [Cloud Armor rate limiting overview, Google (throttle and rate-based ban rules in security policies; read Oct 12, 2026)](https://docs.cloud.google.com/armor/docs/rate-limiting-overview)
23. [Caching with Rails, Rails guides (Solid Cache is the default store since Rails 8.0, FIFO eviction; read Oct 12, 2026)](https://guides.rubyonrails.org/caching_with_rails.html)
24. [Rails 8.0: No PaaS Required, Rails blog (Solid Cable, Solid Cache and Solid Queue replace Redis; read Oct 12, 2026)](https://rubyonrails.org/2024/11/7/rails-8-no-paas-required)
25. [rails/solid_queue README (FOR UPDATE SKIP LOCKED, PostgreSQL 9.5+; read Oct 12, 2026)](https://github.com/rails/solid_queue)
26. [Django's cache framework (LocMemCache is the default and per-process; DatabaseCache with createcachetable; read Oct 12, 2026)](https://docs.djangoproject.com/en/6.1/topics/cache/)
27. [Django Channels: channel layers (channels_redis is the only official production layer; in-memory is not for production; read Oct 12, 2026)](https://channels.readthedocs.io/en/stable/topics/channel_layers.html)
28. [Laravel cache (database is the default store; read Oct 12, 2026)](https://laravel.com/framework/docs/12.x/cache)
29. [Laravel queues (database is the default connection in new applications; read Oct 12, 2026)](https://laravel.com/framework/docs/queues)
30. [Laravel rate limiting (uses the default cache; redis, memcached and database increment atomically; read Oct 12, 2026)](https://laravel.com/framework/docs/rate-limiting)
31. [Sidekiq README (requires Redis 7.0+, Valkey 7.2+ or Dragonfly; read Oct 12, 2026)](https://github.com/sidekiq/sidekiq)
32. [What is BullMQ (a queue built on top of Redis; read Oct 12, 2026)](https://docs.bullmq.io/)
33. [Celery: backends and brokers (RabbitMQ and Redis stable, Google Pub/Sub experimental; read Oct 12, 2026)](https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/index.html)
34. [express-rate-limit (a built-in memory store; a store shares hit counts across nodes; read Oct 12, 2026)](https://www.npmjs.com/package/express-rate-limit)
35. [Redis CLI and INFO, redis.io (--scan, commandstats; read Oct 12, 2026)](https://redis.io/docs/latest/develop/tools/cli/)
36. [SCAN, redis.io (iterates the keyspace with a cursor; read Oct 12, 2026)](https://redis.io/docs/latest/commands/scan/)
37. [CREATE TABLE, PostgreSQL (unlogged tables skip the WAL and are truncated after a crash; read Oct 12, 2026)](https://www.postgresql.org/docs/current/sql-createtable.html)
38. [Advisory lock functions, PostgreSQL (transaction-level locks are held until the transaction ends; read Oct 12, 2026)](https://www.postgresql.org/docs/current/functions-admin.html)
39. [INSERT, PostgreSQL (ON CONFLICT DO UPDATE guarantees an atomic insert or update even under high concurrency; read Oct 12, 2026)](https://www.postgresql.org/docs/current/sql-insert.html)
40. [date_bin, PostgreSQL (bins a timestamp into an interval; read Oct 12, 2026)](https://www.postgresql.org/docs/current/functions-datetime.html)
41. [Upstash Redis pricing (pay as you go $0.20 per 100K commands; read Oct 12, 2026)](https://upstash.com/pricing/redis)
42. Exodus team: Cloud Billing Catalog API read for Memorystore SKUs in europe-west1, gcloud 587.0.0 help for every command shown, and a local run of the sample (Valkey 8.1 and Postgres 17.11, two sessions, pgbench and redis-benchmark), Oct 12, 2026
