---
title: "Redis"
description: "Snapshots of a Redis instance, local or cloud, including managed Redis."
url: "https://saved.sh/docs/backups/sources/redis"
---

Reads every key with `SCAN` and `DUMP` and stores them. Available as both a local and a
cloud backup, and it works against **managed Redis**, which a replication-based snapshot
cannot.

## What is taken [#what-is-taken]

A pass over one keyspace: every key, its value, and its TTL.

| Included                              | Not included                                     |
| ------------------------------------- | ------------------------------------------------ |
| Keys, values, expiries                | The server's config                              |
| One numbered database, or the default | ACL users                                        |
|                                       | Cluster topology, replication state              |
|                                       | Anything written after the pass reached that key |

<Callout type="warn">
  **This is not a point-in-time snapshot.** `SCAN` walks the keyspace while the server keeps
  serving, so a key written after the walk passed it is not in the artifact, and two keys
  changed together may be captured either side of the change. For a cache that is fine. For a
  primary datastore, take the backup when writes are quiet, or from a replica.
</Callout>

## The artifact [#the-artifact]

**JSON Lines**, one key per line:

```json
{"key":"user:1","ttl_ms":0,"value":"AAVhbGljZQ0AWLBU0uYB8Kw="}
```

`value` is Redis's own `DUMP` payload, base64 encoded. That is what makes the format lossless:
`RESTORE` puts each value back with its type and internal encoding intact, so a list comes
back a list and a hash a hash. `ttl_ms` is `0` for a key with no expiry, otherwise the
remaining life in milliseconds, which is exactly what `RESTORE` takes.

The format is plain text, so you can read it, grep it, and restore one key by hand if that is
all you need.

## Fields [#fields]

Local, in the worker's `config.yaml` under `source:`:

| Key            | Required | Notes                                                                      |
| -------------- | -------- | -------------------------------------------------------------------------- |
| `host`, `port` | No       | `host` also names the artifact, defaulting to `redis.redis.jsonl`          |
| `user`         | No       | For ACL-enabled servers                                                    |
| `password`     | No       | Never passed on the command line                                           |
| `db`           | No       | The numbered keyspace. Must not be negative                                |
| `tls`          | No       | `true` connects over TLS                                                   |
| `insecure`     | No       | `true` skips certificate verification. **Refused unless `tls` is set too** |

Cloud, on the backup definition. `host` is **required** here, because unlike a database dump
there is no local socket fallback worth guessing at. Every field goes to the vault together
and none is returned by any endpoint:

| Fields                                                      |
| ----------------------------------------------------------- |
| `host`, `port`, `db`, `user`, `tls`, `insecure`, `password` |

```yaml title="config.yaml"
backups:
  "<backup-uuid>":
    source_type: redis
    source:
      host: cache.internal
      port: 6379
      password: "<password>"
      tls: true
```

<Callout type="warn">
  `insecure: true` disables certificate verification, which makes the TLS connection
  interceptable. Use it to get a self-signed development instance working, not in production.
  Setting it without `tls` is refused at startup, rather than quietly doing nothing.
</Callout>

## What the server has to allow [#what-the-server-has-to-allow]

Only ordinary read commands: `SCAN`, `DUMP` and `PTTL`. No `SYNC`, no `BGSAVE`, no `SAVE`.

That is the whole reason for the format. Managed Redis blocks the replication and persistence
commands a `--rdb` snapshot needs:

```
REPLCONF -> ERR command 'replconf' is not allowed
BGSAVE   -> ERR command 'bgsave' is not allowed
```

Redis Cloud, Upstash and ElastiCache Serverless all answer that way, and there is no flag on
our side that changes it. Reading the keys back is the path they leave open, so that is the
path we take.

An ACL user still needs read access to the keys it is meant to back up.

## Sizing [#sizing]

Larger on the wire than an RDB, because base64 costs a third, though it compresses well and
compression is on by default. The bigger cost is time: one `DUMP` and one `PTTL` per key, so a
keyspace of millions of keys takes minutes rather than seconds.

Keys are read in batches and nothing is held in memory but the key in hand, so the size of the
keyspace does not decide whether the backup fits.

## Restoring [#restoring]

```bash
sctl restore redis <artifact-id> --host cache.internal --port 6379
```

Keys are replayed into a **running** server as `RESTORE` commands. Nothing has to be stopped,
no file has to be placed, and permissions on the data directory do not matter.

Each key keeps the keyspace it was dumped from; pass `--db` to collapse every key into one.
Existing keys of the same name are overwritten and everything else in the target is left
alone, so restoring into a live server is additive rather than destructive.

<Callout>
  Artifacts taken before 2026-08-19 are RDB files. `sctl restore redis` still reads them: the
  format is detected from the artifact, not configured, so an old backup restores with the
  same command as a new one.
</Callout>
