MemoryStore guide
This is the end-to-end walkthrough for MemoryStore, the platform's managed, Redis-compatible key-value store. It assumes only an account and a project, and it takes you from zero to a working cache or session store.
What MemoryStore is
MemoryStore gives you a fully managed, Redis-protocol-compatible instance. Anything you would do with redis-cli or any Redis client library works against it unchanged. Use it for caching, session storage, rate limiting, leaderboards, work queues, and the small fast state your agent's tools need.
Use cases:
- Cache for expensive API or model calls —
allkeys-lrueviction is the right default. - Session store for web apps and agents.
- Rate limiting and counters — atomic increments with no risk of eviction if you keep the default
noevictionpolicy. - Pub/sub and queues — the wire protocol supports the standard list and channel primitives.
- Agent tool state — the side channel for shared state between a Serverless service and an agent.
Quick start: working instance in five commands
# 1. sign in once
platformctl login
# 2. create a small cache
platformctl memorystore create cache --maxmemory-policy allkeys-lru
# ^ the response contains the password. Copy it; it is shown exactly once.
# 3. wait for ready
platformctl memorystore get cache
# 4. see the connection details (host, port; the password was shown at create)
platformctl memorystore get cache -o json
# 5. clean up when done
platformctl memorystore delete cache
connection.host resolves only inside the platform. Your laptop cannot
reach it and there is no public endpoint by default, so a redis-cli against
that host from your machine hangs until its own timeout rather than failing
fast.
To use it from a workload, see
connect from workloads. To reach it
from outside, publish it first with platformctl gateway publish.
The same flow in the Console is Data services → MemoryStore → Create instance → Connect.
Core concepts
- An instance is a managed, Redis-protocol-compatible server. It is the only object you create, and the platform places it for you. There is no replica: one instance serves every connection.
- The size class (
small,medium,large) sets memory. You can change it after creation — see change the size class or the durability. A shrink to a class whose limit is at or below what the instance is holding is refused rather than applied. - The eviction policy (
noevictionby default,allkeys-lrufor caches) is set at create time only. - Persistence (on by default) writes every change to an append-only log on a disk of its own. Turn it off (
persistence.enabled: false) only if losing the data is acceptable — with no disk, any restart brings the instance back empty, including the one a size class or durability change causes. It is settable at create and cannot be changed afterwards, in either direction. - Durability (
persistence.fsync,everysecby default) says how long an acknowledged write may sit in memory before it is on disk. Unlike persistence itself, this one is changeable after create. - A new instance returns its password exactly once. Later reads return only the name of the Secret that holds it.
- The TLS endpoint is enabled at create time. (
tls.enabled: true.) Required for any connection that crosses the project boundary. sharedmode puts this instance on the project-shared backend (cheap, fast, less isolated).dedicatedmode gives the instance its own backend (louder-neighbor protection, higher cost).- The credential Secret lives in Secrets Manager under
ms-<name>-credentialonce the instance is provisioned. Use it from your code, do not paste the password. - Rename is not supported. The name is part of the hostname. Recreate if you need a different name.
- Every instance is project-scoped. Other projects cannot see it, list it, or reach it.
Change the size class or the durability after create
Two settings can be changed on an instance that already exists: the size class, and the durability setting persistence.fsync. Everything else is fixed when the instance is created, and an attempt to change it is refused in words rather than accepted and ignored — see what an update refuses.
There used to be no update path at all. The only way to give an instance more memory was to delete it and create another one, which threw the data away.
- platformctl
- curl
- Console
platformctl memorystore update cache --size-class medium
platformctl memorystore update cache --fsync always
platformctl memorystore update cache --size-class large --fsync everysec
Only the flags you pass are sent, so a resize cannot silently revert a durability setting somebody else just chose. Passing neither is an error, not a no-op.
curl -sX PATCH "$MS/v1/projects/$PROJ/memorystores/cache" \
-H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
-d '{"size_class":"medium","persistence":{"fsync":"always"}}'
Both fields are optional and an absent field means "leave it alone". The response is the instance as a GET returns it, plus a note saying what the change does to the running instance, plus persistence.fsync — the one field no read route returns today. Project admin only. ($MS, $PROJ and $TOK are set under API examples below.)
The instance page does not offer these two controls yet. Use platformctl or the API. The console's
create form still sets the size class, and the instance page still shows it, read-only. The in-place
controls land alongside the API and the CLI rather than ahead of them, and nothing else on this page
changes when they do.
What it costs. A change restarts the instance once: about 30 seconds back to ready. Sending the values the instance already has changes nothing and does not restart it.
What that restart costs your data depends on one setting, and the two answers are opposite ones:
- Persistence on (the default): the disk follows the instance to whichever machine it lands on, and it
reloads the append-only file on the way back up. A graceful restart like this one costs you nothing. It
is not a promise about every failure — if the machine dies mid-roll, the
everysecwindow below still applies. - Persistence off: there is no disk. The instance holds the dataset in memory and nothing else, so the same restart brings it back empty. That is the whole of a cache, gone, in exchange for a resize.
The note on the API response says which of the two you are getting, in those words, before you wait for
the instance to come back.
The disk does not grow with the class. persistence.size is fixed at create (2 GiB by default) and is
independent of the size class, so growing an instance to a class whose memory limit approaches or exceeds
its disk leaves the append-only file less room than the dataset it has to record. If that is where you are
heading, create a new instance with a bigger --persistence-size and migrate — which is the path an
in-place resize spares you, and still the only one there is for a disk.
Clients reconnect. Commands in flight at the moment of the restart do not survive it, so make the change when a dropped connection is cheap, or make sure your client retries.
What everysec and always actually promise
persistence.fsync sets how long an acknowledged write may sit in memory before it is on disk. It means
something only while persistence is on: with persistence off there is no disk, and a restart loses
everything. Sending it to an instance with persistence off is refused with 409 rather than accepted and
quietly ignored — see what an update refuses.
--fsync / persistence.fsync | What an acknowledgement means | What it costs |
|---|---|---|
everysec (default) | The write is handed to the machine to write out, and is on disk within about a second. A restart of the instance alone keeps it; if the machine dies, up to about one second of acknowledged writes can be lost. | Nothing extra. Every instance has run this way so far. |
always | An acknowledged write is on disk before we acknowledge it. | Every write waits for the disk before it is answered. We have not measured what that costs here, so treat it as a real, unmeasured latency cost rather than a free upgrade, and measure your own workload before you depend on a number. |
Reading the value an instance is running today. The PATCH response carries persistence.fsync. No
read route does yet — platformctl memorystore get and the console detail page do not show it — so until
they do, the durability an instance is running is whatever you last set on it.
Neither value promises that nothing can ever be lost, and always least of all, because its promise is deliberately narrow: it is about this one instance and its own disk, at the moment of the acknowledgement. It says nothing about the disk being lost, the instance being deleted, or a machine failure that strands the disk where nothing can reach it. There is no replica and no backup behind it, so each of those is still a total loss. always protects you from a crash, not from the list in Scale, limits and recovery.
What an update refuses, and what to do instead
| What you ask for | What you get | What to do instead |
|---|---|---|
A different persistence.size — a bigger or smaller disk | 400, refused | A disk cannot be grown or shrunk in place on this platform. Create a new instance at the size you want (platformctl memorystore create <new-name> --persistence-size 20Gi), copy the data across, then delete the old one. If what you wanted was more memory, that is the size class, and it is settable here. |
persistence.enabled — persistence on or off | 400, refused | The disk is attached when the instance is created; a running instance cannot have one added or taken away. Create a new instance and migrate. |
persistence.fsync on an instance with persistence off | 409, refused | There is no append-only file to flush, so the setting would change nothing and would read back as a durability level the instance does not have. Persistence cannot be turned on in place either: create a new instance with persistence enabled and migrate. |
| A size class whose limit is at or below what the instance is holding right now | 409, refused, naming both numbers | Delete keys or shrink the working set first, then retry. Read the live figure with platformctl memorystore stats <name> (used_memory_bytes). An instance sitting exactly at the new ceiling is refused too: its next write fails. |
| Neither field | An error, not a silent no-op | Pass a size class, a durability setting, or both. |
The shrink guard is the one worth understanding rather than working around, and it refuses whatever your
eviction policy is. With the default noeviction, an instance that came back over its limit would not
drop anything to fit: it would keep every key, keep answering reads, and fail every write — which looks
like your own code breaking rather than like an outage. With allkeys-lru, or any other eviction policy, it
would do the opposite and silently evict keys until the dataset fit — data you did not ask to lose. Neither
is something to do to you on the strength of a resize flag. If the live usage cannot be read at all, because the instance is provisioning, restarting or unreachable, the shrink is refused too and says which: the alternative is guessing, and guessing is how a resize becomes a silent write outage.
There is no instance count, on purpose
One instance serves every connection, and there is no setting asking for a second. Replication needs a way for clients to find the writable instance, a way to promote a survivor, and a way to keep traffic off one that is not ready to take it. None of those exist here yet, and a setting that only ever accepted 1 would read as support that is not there. Plan for a single instance with a disk, and read Scale, limits and recovery for what that means when a machine fails.
API examples
export MS="https://api.codyhill.net"
export PROJ="$(platformctl projects list -o json | jq -r '.[0].id')"
export TOK="<session token or API key>"
Create a small cache (eviction tuned for a cache):
curl -sX POST "$MS/v1/projects/$PROJ/memorystores" \
-H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
-d '{"name":"cache","size_class":"small","maxmemory_policy":"allkeys-lru","tls":{"enabled":true}}'
The response contains the only copy of the password. A later read of the same instance returns the credential name, not the value:
curl -s "$MS/v1/projects/$PROJ/memorystores/cache" -H "Authorization: Bearer $TOK"
Resize it, or change how durably it writes (admin; both fields optional, absent means unchanged):
curl -sX PATCH "$MS/v1/projects/$PROJ/memorystores/cache" \
-H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
-d '{"size_class":"medium","persistence":{"fsync":"always"}}'
Live stats:
curl -s "$MS/v1/projects/$PROJ/memorystores/cache/stats" -H "Authorization: Bearer $TOK"
Rotate the credential if it leaks:
curl -sX POST "$MS/v1/projects/$PROJ/memorystores/cache/rotate-credential" \
-H "Authorization: Bearer $TOK"
CLI examples
platformctl memorystore list # all instances
platformctl memorystore get cache # state, hostname, size_class, eviction policy
platformctl memorystore stats cache # live memory, evictions, hit ratio
platformctl memorystore create cache --size-class small --maxmemory-policy allkeys-lru
platformctl memorystore update cache --size-class medium # admin only; one restart. The data survives it with persistence on
platformctl memorystore update cache --fsync always # admin only; same restart, same condition
platformctl memorystore create big --size-class large --persistence-size 8Gi # the disk is create-time only; size it here
platformctl memorystore rotate-credential cache # admin only; issues a new password, revokes the old one
platformctl memorystore renew-credential cache # admin only; renews the existing credential lease
platformctl memorystore delete cache # admin only; drops the instance and its data
Console walkthrough
- Open the Console, pick your project.
- Go to Data services → MemoryStore.
- Click Create instance. Pick a name, size class, eviction policy, and whether to enable the TLS endpoint. For a cache, choose
allkeys-lru. For anything that matters, leave the defaultnoeviction. - The response shows the password once, with copy and Save to Secrets buttons. Save it to Secrets — that is the easiest way to never lose it.
- When the row flips to Ready, click the instance to open it. The detail page shows the private hostname, port, TLS endpoint, and live stats.
- To connect from your code, use the TLS endpoint, not the private hostname — see Connect from workloads.
- Rotate credential (admin) issues a fresh password without downtime. Delete terminates the instance.
Limits and quotas
| Limit | Default |
|---|---|
| Size classes | small (~192 MiB usable), medium, large |
| Instances per project | 10 (soft; raise via support) |
| Eviction policies | noeviction (default), allkeys-lru, volatile-lru, allkeys-random, plus the standard policy set of the wire protocol |
| Max requests/second | bounded by the chosen size class; the stats endpoint reports the live ceiling |
| TTL per key | any positive value up to the protocol maximum (~2^31 ms) |
The one-time password is returned only at create and at rotate. If you lose both copies, your only recovery is rotate-credential.
Troubleshooting snippets
| Symptom | First thing to check |
|---|---|
NOAUTH Authentication required | The password was wrong or rotated. platformctl memorystore get cache shows the current credential name; the value lives only in Secrets Manager. |
OOM command not allowed when used memory > maxmemory | The instance is full and the eviction policy is noeviction. Either raise the size class in place (platformctl memorystore update <name> --size-class medium), switch to allkeys-lru (recreate — the policy is fixed at create), or delete keys. Raising the class does not grow the disk: if the new class allows a dataset near the persistence.size you chose at create, create a new instance with a bigger disk instead. |
Connection refused from your code | You are using the private hostname. Use the TLS endpoint — see Connect from workloads. |
state stuck at provisioning | Normal for ~30 seconds on a small instance. If it stays past five minutes, check the project for a stuck resource with platformctl memorystore get cache -o json. |
| Lost password | The API will not show it again. Use rotate-credential to issue a new one. |
read-only errors | The instance went into a degraded state. stats reports master_link_status; recreate if it does not recover in a few minutes. |
Security notes
- Password shown once is a hard rule. Anything else would be a silently-leaking secret. Pair the create call with an immediate Secrets Manager write.
- TLS endpoint when crossing the project line. Traffic inside the project's services runs over mutual TLS already. The moment a laptop, CI runner, or external tool connects, use the TLS endpoint and validate the server certificate.
- Eviction policy and durability are a security choice.
noevictionplus persistence is the safest;allkeys-lruwithout persistence is the fastest.--fsync alwaysmeans an acknowledged write is on disk before we acknowledge it, at the cost of write latency, and still leaves you with one copy of the data. Do not put authentication tokens in aallkeys-lrucache — a full cache will silently log your users out. - Delete is irreversible. There is no soft-delete, snapshot, or point-in-time restore. Export anything that matters with
connectbefore calling delete. - Audit is on. Every create, update, rotate, renew, and delete lands in the project's audit log with the actor, the time, and the previous credential name where applicable. An update records the size class and the durability setting before and after, so a resize is traceable to whoever asked for it.
Where next
- Connect from workloads — TLS, service accounts, and example client code in every common language.
- Use with agents — where the agent's chat history actually lives, and when to give it its own store.
- Quickstart — the five-minute standalone version.