---
id: platform/key-value
title: Key Value
description: Create a managed Valkey store, connect applications, and configure eviction, persistence, and external access.
keywords: [bex, key value, valkey, redis, cache]
last_updated: 2026-09-23
---

Bex Key Value is a managed Valkey store compatible with Redis clients. Use it
for caches, shared coordination, or a job queue supported by your application.
It is a separate resource, so several services can share it without tying its
lifecycle to a single application deployment.

Choose eviction and persistence settings according to the data's role. A cache
that can discard old entries and a queue that must retain pending work should
not automatically use the same policy.

## Create a store and connect a service

A [`render.yaml` Blueprint](./app-resource.md) can declare the store and its
consumer together:

```yaml
services:
  - name: cache
    type: keyvalue
    plan: free
    ipAllowList: []
  - name: web
    type: web
    runtime: docker
    repo: https://github.com/your-org/web
    envVars:
      - key: REDIS_URL
        fromService:
          name: cache
          type: keyvalue
          property: connectionString
```

Replace the application repository, choose appropriate plans, then validate
and apply the Blueprint. Your application reads `REDIS_URL`. The supported
reference is **`fromService` with `type: keyvalue`**, not `fromKeyValue`.
A Key Value declaration needs no language runtime or application Dockerfile.

For an explicitly private store through REST, set the API origin and an
authorized workspace token:

```bash
curl --fail-with-body -X POST "$BEX_API_URL/v1/key-value" \
  -H "Authorization: Bearer $BEX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"cache","plan":"free","public":false}'
```

In this REST endpoint, omitting `public` enables public access when a nonempty
IP allowlist is supplied; otherwise it stays private. Specify the intended
access and inspect the created store. External routing also requires the
instance's Key Value domain and gateway configuration.

## Retrieve the correct connection string

Use the dashboard's connection details, or set `KEY_VALUE_ID` to the returned
resource id:

```bash
curl --fail-with-body "$BEX_API_URL/v1/key-value/$KEY_VALUE_ID/connection-info" \
  -H "Authorization: Bearer $BEX_TOKEN"
```

The response includes `internalConnectionString`, an external string when
available, and `cliCommand`. It contains credentials and requires sensitive-value
access. Keep it out of source control and public logs.

Use the returned internal `redis://` connection string from an allowed Bex
service. Use the returned external `rediss://` URL from an external client when
public routing is ready. The external CLI command includes `--sni` with the
actual host; this matters because the gateway routes by TLS server name.
Do not reconstruct that hostname from the display name or replace it with an IP.

After securely setting `REDIS_URL` to the internal connection string, test from
an allowed peer service:

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

A healthy authenticated connection returns `PONG`. For an external client,
use the returned command's TLS/SNI settings and the appropriate certificate
trust configuration. Internal `redis://` transport is not encrypted simply
because it is on a private network.

## Choose an eviction policy

When memory reaches its configured limit, `maxmemoryPolicy` decides whether
and how Valkey discards keys. Examples include:

| REST setting | Behavior |
| --- | --- |
| `allkeys_lru` | Evicts less recently used keys across the store; useful for disposable caches. |
| `allkeys_lfu` | Evicts less frequently used keys across the store. |
| `volatile_lru` | Selects eviction candidates among keys with an expiry. |
| `noeviction` | Refuses memory-growing writes instead of discarding keys when full. |

For queues, follow the queue library's requirements and monitor write failures;
`noeviction` avoids eviction but does not create unlimited capacity. Review the
policy before changing a live store:

```bash
curl --fail-with-body -X PATCH "$BEX_API_URL/v1/key-value/$KEY_VALUE_ID" \
  -H "Authorization: Bearer $BEX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"maxmemoryPolicy":"noeviction"}'
```

The API's canonical enum spelling uses underscores; Kubernetes/Valkey settings
use hyphens. The currently supported policies are validated by Bex rather than
passed through as arbitrary Valkey configuration.

## Persistence is separate from backups

The REST `persistenceMode` values are:

| Value | Storage behavior |
| --- | --- |
| `journal_snapshot` | Append-only journaling plus snapshot behavior. |
| `snapshot` | Snapshot persistence without append-only journaling. |
| `off` | In-memory operation without AOF or scheduled RDB persistence. |

Bex maps these onto Valkey configuration. Changing mode is an operational
change that should be reviewed against your application's data-loss tolerance;
it is not an immediate backup or a guarantee that every acknowledged write
survives every failure.

Backup creation additionally depends on the plan and instance's backup-store
configuration. Confirm that backups are actually completing with your instance
operator before relying on recovery. The current public Key Value REST surface
does not provide a tenant snapshot-list/restore workflow; do not reuse service-disk
snapshot endpoints or assume Render's recovery behavior applies.

Self-hosted operators who need an isolated RDB restore drill should follow
[Recovery: Key Value](./recovery-keyvalue.md) after
[Platform recovery](./recovery.md) preparation.

## Access, storage, and lifecycle

External IP rules apply to the public endpoint. Internal access still depends
on workspace/environment network policy and valid credentials. Adding a DNS
record alone does not enable a datastore gateway.

Storage is grow-only: a compute-plan downgrade does not shrink an existing
volume. Monitor memory, evictions, connection counts, and latency alongside
storage usage. Restart interrupts connections; suspension makes the store
unavailable until resumed. Configure clients to reconnect appropriately.
Deleting the store removes its data; preserve anything you need first.

## Troubleshoot

For authentication failures, retrieve fresh authorized connection details and
check whether credential changes are still converging. For external failures,
check public access, allowlist, DNS, TLS trust, and SNI. For rejected writes,
inspect memory limits and eviction policy. For missing keys, distinguish
expiration, eviction, application deletion, and a persistence failure.

See [Private network](./private-network.md), [Secrets](./secrets.md),
[PostgreSQL](./postgres.md), and [Persistent disks](./persistent-disks.md).
