# Redis

A Redis 7 instance per project for caching, queues, sessions and rate limiting.

## Deploy

Click Deploy on the Redis card. You get one value, `REDIS_URL`. When TLS is on it starts with `rediss://` (two s), otherwise `redis://`.

- Your app signs in as the `default` user, which can run every command except admin ones such as `CONFIG`, `ACL`, `SHUTDOWN`, `MONITOR` and `REPLICAOF`.
- Data is persisted with an append-only file, so keys survive restarts.
- Memory is capped at 32 MB on Free and at the chosen size on Pro (256 MB by default), with the `noeviction` policy. A full Redis refuses writes instead of silently dropping data, which keeps queued jobs safe.

## Sizes and resizing

On Pro, each Redis has a memory size and is charged for that size per hour while it runs. New Redis starts at 256 MB, the same price as before sizes existed.

| Size | Memory (maxmemory) | CPU | Per hour | About per month |
| --- | --- | --- | --- | --- |
| 256 MB | 256 MB | 0.5 | $0.012 | $8.76 |
| 512 MB | 512 MB | 0.5 | $0.024 | $17.52 |
| 1 GB | 1 GB | 1 | $0.049 | $35.77 |
| 2 GB | 2 GB | 1 | $0.097 | $70.81 |
| 4 GB | 4 GB | 1 | $0.193 | $140.89 |

To change it, open Resize on the Redis card, pick a size and click Resize. The change is live: Redis does not restart, apps keep their connections, and your keys and `REDIS_URL` stay the same.

- A resize needs a day of the new size in credits.
- Shrinking is refused while Redis holds more than 90% of the smaller size, while its process still holds more memory than the smaller size (memory freed by deleted keys is not always handed back), or while it is saving to disk. Delete keys, try a larger size, or try again in a minute.
- The hour of a resize is charged at the larger of the two sizes.
- Free Redis has 32 MB and no sizes. Upgrade to Pro to resize it. After a downgrade to Free, a Redis larger than 256 MB keeps its size and price, and can be shrunk but not grown.

## Connect

*ioredis*

```ts
import Redis from "ioredis";

export const redis = new Redis(process.env.REDIS_URL!);

await redis.set("greeting", "hello", "EX", 60);
console.log(await redis.get("greeting"));
```

*Terminal*

```bash
redis-cli -u "$REDIS_URL" ping
```

*redis-py*

```python
import os, redis

r = redis.from_url(os.environ["REDIS_URL"])
r.set("greeting", "hello", ex=60)
```

## Common patterns

*Cache with expiry*

```ts
async function cached<T>(key: string, ttlSec: number, load: () => Promise<T>): Promise<T> {
  const hit = await redis.get(key);
  if (hit) return JSON.parse(hit);
  const value = await load();
  await redis.set(key, JSON.stringify(value), "EX", ttlSec);
  return value;
}
```

*Simple rate limit*

```ts
async function allow(userId: string, limit = 20, windowSec = 60) {
  const key = `rate:${userId}`;
  const count = await redis.incr(key);
  if (count === 1) await redis.expire(key, windowSec);
  return count <= limit;
}
```

Because memory is capped and nothing is evicted, always give cache keys an expiry (`EX`). BullMQ and other queue libraries work too.

> **Note:** For BullMQ with ioredis, create the connection with `maxRetriesPerRequest: null`.

## Manage

- Reset password changes it live. Apps still using the old one are disconnected until you update `REDIS_URL`.
- Resize changes a Pro Redis's memory size live (see Sizes and resizing).
- The nightly check records memory use for the Usage page.
- Delete removes the instance and its data.

## Use with AI

Ask an agent to add caching, queues or rate limiting backed by your project's Redis.

```text
Read https://cloudhike.dev/docs/md/redis, then add Redis to this app using my CloudHike.dev Redis service.

- REDIS_URL is in my env file (if not, follow https://cloudhike.dev/prompt.md to download it).
- Use ioredis (or this language's standard client) from server code only, with one shared connection.
- Add [caching / a job queue / rate limiting] for [describe the feature].
- Memory is small and nothing is evicted, so give every cache key an expiry.
```
