# Import a Redis

Move a Redis from another host (Heroku Key-Value Store, Upstash, Render Key Value, Railway, Redis Cloud, ElastiCache or your own server) into a new Deplane [Redis](redis.md): paste its URL, check it, and start; or give Deplane an RDB file, uploaded or from a link. Your keys arrive with their expiry times.

Press **Import** on the Redis page of the dashboard, or run [`deplane redis import`](cli.md#deplane-redis-import). The command and its flags are in the [CLI reference](cli.md#deplane-redis-import), and every error has a fix in [Troubleshooting and error codes](errors.md#import_check_failed).

An import is for people: CI tokens and AI tools cannot start one, and the [assistant](assistant.md) never takes a Redis URL or password. The import always creates a **new** Redis; it never overwrites one you have.

## Three ways in

| | From the URL | Upload a file | From a link to a file |
|---|---|---|---|
| You give | The Redis's `redis://` or `rediss://` URL | An RDB file (`dump.rdb`) on your computer | An `https://` link that downloads an RDB file, such as a presigned S3 link |
| CLI | `deplane redis import` | `deplane redis import --rdb FILE` | `deplane redis import --rdb-url` |
| Checked first | Yes: version, size, keys, types and how they can be copied | The file's header, as it arrives | Yes: the file's header and size, without downloading it all |
| Limit | 150 MB of data | 256 MB file | 256 MB file |
| What is copied | The one database the URL names (`/0` when it names none), into database 0 | Every database in the file, as it is | Every database in the file, as it is |
| Works with | Any Redis or Valkey Deplane can reach | A file from Redis 7.2 or older, or Valkey 8 (RDB version 11 or older) | The same files |

Use the URL when you can: it works with every managed host, including the ones that give you no file. Use a file for a Redis Deplane cannot reach, or one you would rather not open to the internet.

## From the URL

1. **Stop writes for an exact copy.** The copy reads the keys one by one while the source keeps running, so a key written during the copy may or may not arrive. Scale your app to 0 or turn on maintenance mode before you start; a Redis that fits takes a minute or two. The CLI asks you to confirm this (`--yes` answers it), and the dashboard has a checkbox.
2. **Paste the URL.** It is asked for at a hidden prompt, or read from stdin; it is never an argument. Deplane checks the source (read-only) and shows what it found: the server and its version, the size of the data, the keys in each database, how the keys will be copied, warnings, and whether the import is possible, with the reasons if not.
3. **Name the new Redis and start.** Deplane creates the Redis as `deplane redis create` does (it counts against your plan), copies the keys into it and shows the keys copied as it goes.
4. **Check the counts.** When it is done, the import shows how many keys the source had, how many the new Redis has, and how many expired during the copy. If the new Redis has fewer than the source minus the expired ones, it warns you: something was probably still writing to the source.
5. **Attach it** to your app: `deplane redis attach NAME --app APP`, or **Attach to an app** on its page. The app gets `REDIS_URL` and rolls out a new release.

**How keys are copied.** Deplane reads each key with `DUMP` and writes it with `RESTORE`: an exact copy of every type, with its expiry time. A Redis newer than a Deplane Redis (Redis 7.4 or later, Redis 8, Valkey 9), or a host that does not allow `DUMP`, is copied **type by type** instead: strings, hashes, lists, sets, sorted sets and streams with their values and expiry times. Type by type does not keep stream consumer groups or the expiry times of single hash fields (Redis 7.4), and cannot make an empty stream; the check says when it will be used, and warns about what it loses.

**What the check refuses:** more than 150 MB of data (a Deplane Redis holds 200 MB), a Redis in cluster mode, and keys of module types (RedisJSON, RediSearch, Bloom filters, TimeSeries), which a Deplane Redis does not have. Other databases than the one the URL names are listed as not copied. A host that does not answer part of `INFO` gets "unknown" for that line and a warning, not a refusal; the copy still stops if the data does not fit.

## From an RDB file

An RDB file is the snapshot Redis writes to disk (`dump.rdb`). Deplane checks its header, stores it as the new Redis's first backup, and loads it with the same restore every Redis backup uses. A file is already a snapshot, so you do not need to stop writes to anything: the new Redis holds the data as it was when the file was written.

- **Upload:** `deplane redis import --rdb ./dump.rdb --name cache`, or the file field on the import page. The CLI checks the file before it sends anything: that it is an RDB file, at most 256 MB, and not newer than version 11.
- **Link:** `deplane redis import --rdb-url`, then paste an `https://` link that downloads the file itself (not a page about it). Deplane downloads it from your team's own sandbox, not from Deplane's servers, follows at most 3 redirects (each `https`), and stops at 256 MB.

A file loads every database in it, as it is; your app's `REDIS_URL` uses database 0. A file made by a newer Redis (RDB version 12 or later: Redis 7.4 and 8, Valkey 9) is refused at once: copy that Redis from its URL instead. Module types cannot be found before the load: a file that holds them fails to load, with a clear message. After the load, a Redis that holds more than 190 MB fails with "the data needs more room than a Deplane Redis has".

## What touches the source

The import only reads. To the source it sends `HELLO` or `AUTH`, `PING`, `SELECT`, `INFO`, `SCAN`, `TYPE`, `PTTL`, `DUMP` and `DBSIZE`, and when copying type by type `GET`, `HSCAN`, `LRANGE`, `SSCAN`, `ZRANGE` and `XRANGE`. It never writes, and never sends `KEYS`, `MONITOR`, `CONFIG`, `SAVE`, `BGSAVE`, `DEBUG`, `CLIENT` or `REPLICAOF`: the importer refuses to send any command that is not on that list. It uses one connection. `DUMP` of a very large key pauses the source for a moment (Redis runs one command at a time); the limits below keep that short.

## Your password and your link

- **The URL** is sent once, over HTTPS, in the body of a request: never in an address, a log line, an audit entry or a command's arguments. Deplane stores it encrypted for the length of the import and erases it when the import is done, cancelled, failed or expired. Audit entries keep only the host, port and database number.
- **The file link** is treated the same way: a presigned link lets anyone who has it download your data until it expires. Make it expire soon (an hour is plenty).
- **TLS:** with `rediss://`, the connection is encrypted, but the server's certificate is **not checked**, because Heroku Key-Value Store, the most common source, uses a self-signed certificate. A plain `redis://` URL works too, with a warning that the password crosses the internet unencrypted; prefer `rediss://` when the host offers it, and change the password afterwards if it matters.

## Let Deplane connect

Deplane connects to the source from a short list of addresses. If the source only accepts listed addresses, allow them: `deplane redis import status` and the import page list them, and the API's `GET /v1/redis-imports` returns them as `egress_ips`. Hosts on private ranges, and names that resolve to them, cannot be reached: the source needs an address on the internet. One of your own Deplane Redis can be copied into another.

## If something goes wrong

- **Cancel** works in every state before **Done** (`deplane redis import cancel`). The copy stops, and the new Redis is emptied, so a half copy never looks complete. The Redis stays: use it, or delete it with `deplane redis destroy NAME`.
- **A failed import** ends the same way, with the reason: for example that the data needs more room than a Deplane Redis has, that a key holds more than 64 MB, or that the source refused a read. See [import_failed](errors.md#import_failed).
- **A check** that fails or expires leaves nothing behind. A checked import that you do not start within an hour expires, and its stored URL or link is erased.
- **While an import runs**, its new Redis cannot be attached, destroyed, backed up or restored ([redis_importing](errors.md#redis_importing)), and nightly backups skip it.

## Provider notes

**Heroku Key-Value Store (Heroku Redis).** Get the URL with `heroku redis:credentials -a APP` (or `heroku config:get REDIS_URL -a APP`). Use the `rediss://` URL: Heroku's certificate is self-signed, which Deplane accepts because it does not check certificates. Heroku gives no RDB file, so the URL is the only way. Turn on maintenance mode (`heroku maintenance:on -a APP`) for the copy.

**Upstash.** Use the `rediss://` URL from the database's page (Upstash always uses TLS). If the database does not allow `DUMP`, the check says so and the copy goes type by type. Some `INFO` lines may show as unknown; that is a warning, not a refusal.

**Render Key Value.** Use the **External** URL from the Key Value's **Connect** menu, not the internal one. Render only accepts outside connections from the addresses you list under its access control: add Deplane's addresses there.

**Railway.** Use the `REDIS_PUBLIC_URL` variable. It only exists when the Redis has a public **TCP proxy** (in the service's networking settings); turn one on for the import and off again after. If the URL is `redis://`, the password crosses the internet unencrypted: change it after the move if that matters.

**Amazon ElastiCache.** An ElastiCache cache has no public address, so Deplane can copy it from its URL only if you have made it reachable from the internet yourself, with its security group allowing Deplane's addresses. The simpler way is a file: export a backup to an S3 bucket from the ElastiCache console, make a link that expires soon (`aws s3 presign s3://BUCKET/FILE.rdb --expires-in 3600`), and run `deplane redis import --rdb-url`.

**Redis Cloud.** Use the database's public endpoint with its password (`rediss://` when TLS is on for the database), and add Deplane's addresses to its allow list if it has one. Or export the database to a bucket and give Deplane a link to the file.

**Your own server.** Redis must listen on an address Deplane can reach (`bind` in `redis.conf`, for example the server's public address) and have a password (`requirepass`, or an ACL user). Open the port in your firewall for Deplane's addresses only, and close it again after the import. If you set up TLS (`tls-port`), use `rediss://`. Or skip the network: run `redis-cli BGSAVE` (or `SAVE` on a quiet server), find the file in the folder `redis-cli CONFIG GET dir` names, and upload it with `deplane redis import --rdb dump.rdb --name NAME`.

## Limits

| Limit | Value |
|---|---|
| Data copied from a URL | 150 MB (`used_memory_dataset`); larger sources are refused by the check |
| RDB file | 256 MB, RDB version 11 or older |
| One key | 64 MB |
| The copy | At most 30 minutes |
| Imports at a time | One active Redis import per team; a Postgres import can run at the same time ([redis_import_active](errors.md#redis_import_active)) |
| Checks | 20 a day per team ([too_many_import_checks](errors.md#too_many_import_checks)) |
| A checked import | Expires after 1 hour if you do not start it |
| Not supported | Cluster mode, Sentinel URLs, module types, importing into an existing Redis |
| Who can import | People signed in to the dashboard or the CLI; not CI tokens or AI tools |

See also [Limits](limits.md#redis).
