1. Redis: volume or no volume
By this point we already have the basic wiring: the application sees Redis, the cache profile is enabled, and the containers start in a predictable way. For the first launch, there is no need for a volume. But in local development it comes up very quickly: should Redis survive container recreation, or is it better to keep it disposable?
When you first add Redis to a Compose stack, it is natural to reach for the “PostgreSQL way”: if it is a service, then it needs a volume so that “nothing gets lost.” The reflex is understandable, but for Redis in the role of a cache it can easily blur the picture. We start treating a read accelerator like a database and drag unnecessary state into the local environment where it is not required.
A local environment has two opposing desires. On the one hand, you want everything to be “as it was yesterday” after a restart: fast, warmed up, convenient. On the other hand, you want an easy reset: bring up the environment, work, tear it down, bring it up again, and get a clean, predictable state without yesterday’s leftovers. As a cache, Redis usually helps the second scenario.
So the default path here is simple: first, assume Redis is disposable. That does not prevent you from getting the first working cache path, and you can consider a persisted variant later as a deliberate local policy if you really need it.
2. Redis as a cache: losing data is not scary
The reason is simple: in Container-Ready Catalog Service, Redis stores derived data, not the source of truth. The source of truth stays in PostgreSQL. If Redis is empty, the next request simply causes a cache miss, reads data from the database, and puts the result back into the cache. Annoying? Sometimes yes. But that is a loss of the accelerator, not a loss of business data.
That is why the question of volume or no volume does not begin with “how do we not lose anything,” but with “do we need to preserve this state between container recreations at all?”
/data and persistence in the container
When people say “Redis stores data,” beginners often imagine “a database file” in their heads. In reality, Redis primarily keeps data in process memory. That means as long as the container is running and the Redis process is alive, the data exists. As soon as the process restarts, memory is cleared. But Redis has persistence mechanisms that can save state to disk so it can load it back on startup. And that is where the /data directory comes in.
In the official Docker image for Redis, the /data directory is used as the place where Redis can write its persistence files (for example, an RDB snapshot or AOF, depending on the configuration). And this is where the most important Docker idea starts: if /data remains inside the container, then it is just the writable layer of that specific container. As soon as the container is deleted and created again, that state disappears.
With a named volume, the idea is different. We give Redis a separate place to store data, one that lives independently of the container. The container can be recreated as many times as needed, and the volume will remain. If Redis is configured so that it really writes persistence files into /data, then on the next startup it can read them and restore the state.
It is useful to keep a simple scheme in mind:
flowchart TD
%% Idea: data lives in Redis memory, and /data is only a place for optional persistence file writes
A["Redis process (in memory)"] -->|optional persistence writes| B["/data inside the container"]
B -->|if there is no volume| C["container writable layer"]
B -->|if there is a named volume| D["named volume (lives separately)"]
“optional persistence writes” is written here on purpose, because Redis persistence is a whole separate universe of settings. We do not need to go deep into it, but it is important to understand: a volume by itself does not make Redis “eternal”; it only provides a place where Redis can save data if it is configured to do so.
3. Option A: disposable Redis without a volume
If we keep Redis in the role of “a cache for repeated reads,” then the most honest and simplest option is Redis without a volume. That means the cache lives exactly as long as the specific container lives, and when the environment is recreated you get a “clean cache.” In local development that is actually nice: you often want to observe cache miss behavior after startup, to understand that the cache is really working and that it is not “just that something happened to be left over yesterday and now it seems fast.”
In compose.yaml, this option looks minimal. Usually you have already added the healthcheck from the previous lecture, so I will show the fragment in this more “grown-up” form:
services:
redis:
image: redis:8.6.0
# In this variant we do NOT mount /data: the container is recreated, and the cache is "clean"
# volumes: intentionally omitted
healthcheck:
# We check that Redis really responds, not just that "the container is running"
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
Notice that there is no volumes section here. This is not a mistake and not something “forgotten.” It is a deliberate decision: Redis as a cache allows a “loss of state” because the state is derived. If you run docker compose down, the Redis container disappears. When you run docker compose up, Redis is created again. The cache will be empty and will then fill up as requests come in to your API.
This approach fits nicely with the discipline of resetting the environment that we practiced with Flyway and PostgreSQL. The database may live in a volume because it is the source of truth and you often need to preserve data between restarts. Redis, on the other hand, can be safely thrown away because it is only an accelerator that warms up naturally.
If you want to quickly check that Redis is really alive and not just “seems to be added to the YAML and forgotten,” the simplest human way is to ping it with redis-cli right inside the container:
# Execute the command inside the redis container and expect a PONG response
docker compose exec redis redis-cli ping
# PONG
This does not turn the course into a “Redis CLI course,” but it does give you one simple and clear readiness indicator: Redis has not only started, it also answers a basic command.
4. Option B: Redis with a named volume
Sometimes you do want Redis to survive container recreation. For example, you may warm up the cache for a long time or want the local environment to start already “warm” after docker compose down and up. In that case, Redis can be connected to a named volume. This is exactly the same mechanism we use for PostgreSQL, but the motivation is different: for Postgres this is almost a necessity (otherwise you constantly lose data), while for Redis it is a deliberate choice.
In Compose, it looks like this: Redis writes to /data, and we mount a named volume there. Fragment:
services:
redis:
image: redis:8.6.0
volumes:
# Mount a named volume to /data — Redis MAY write persistence files here (if enabled)
- redis-data:/data
healthcheck:
# Minimal Redis availability check
test: ["CMD", "redis-cli", "ping"]
volumes:
# The named volume lives separately from containers and survives docker compose down (without -v)
redis-data:
The point of redis-data is that Docker stores this volume separately from containers. You can delete the Redis container, and the volume will remain. Then a new Redis container will mount this volume into /data again.
Now for the most practical part: how this relates to down and down -v.
When you run docker compose down, Compose removes the containers (and the network), but by default it does not remove named volumes. That means redis-data will remain. It will be connected again on the next up.
When you run docker compose down -v, Compose removes both the containers and the associated named volumes. In other words, redis-data will disappear. On the next up, the volume will be created again, and Redis will be clean, as if you had just added it.
It is useful to see this once as a short “behavior table,” so you do not keep everything in your head as YAML magic:
| Command | Redis container | Redis named volume | What it means for the cache |
|---|---|---|---|
|
removed | remained | the state can potentially be restored (if Redis wrote to /data) |
|
removed | removed | the cache will definitely be empty after the next up |
And here it is important not to fall into the trap of expectations. If you attached a volume, that still does not guarantee that you are actually “saving the cache.” Redis may not have time to write the persistence file before shutdown, or persistence may be configured in a way that does not save what you expect. So within the course it is better to think of a volume like this: it is a tool that “gives Redis a place to save data,” not a promise that “the cache will always survive restarts.” What matters more to us is the engineering discipline around the environment and a clear model of data life cycle, rather than an absolute guarantee that the cache will be warm after a restart.
If you want to make sure that the volume really exists and lives its own separate life, you can simply look at the list of volumes:
# Look at all Docker volumes: this will show whether redis-data survived the down command
docker volume ls
# ... you will see something like:
# local docker-java-catalog-service_redis-data
The name will include the Compose project prefix (usually the directory name), and that is normal: Docker tries not to create conflicts between projects.
5. Strategy for app + postgres + redis
The choice of “volume or no volume” is not a matter of taste, but a continuation of Redis’s architectural role in the project. In today’s setup, Redis is specifically a cache. That means that if you are unsure, start with the disposable option. It is simpler, more honest, and teaches the right attitude toward caching better: the cache should improve life, but it should not become a second database that you are forced to rescue and back up “because otherwise everything will break.”
With disposable Redis, responsibility is cleanly separated. PostgreSQL stores the real state of the catalog and export jobs, and that is why PostgreSQL has a named volume. Redis stores only read accelerators, and therefore Redis can be safely recreated, especially in a local environment where resetting the environment is a normal daily operation. You find configuration errors faster because you are not thrown off by “leftovers from yesterday’s cache” that are hiding the problem today.
A Redis volume becomes justified when you have a specific reason. For example, you may want to compare behavior after environment recreation with a “warmed cache,” or your local workflow may be such that you bring the environment up once a day and want it to be fast right away. But even in that case, you should keep the boundaries in mind: Redis is still not the source of truth, and you should not write code as if Redis must remember everything “forever.” If losing Redis breaks the meaning of the service, then you have silently made it the primary storage. At that point, it is not only the Compose policy that changes, but the entire application model.
Another practical point is documentation. If you do enable a Redis volume, that decision should be readable in compose.yaml. Do not hide it in ten override files or “magic” scripts. Let the student (or your future colleague) open the YAML and immediately understand: Postgres stores data in a volume, Redis also stores something in a volume, but for a different reason. That is the value of infrastructure discipline: it not only works, it is also explainable.
6. Typical mistakes when working with Redis
Mistake #1: automatically making Redis “like PostgreSQL” without understanding the difference in roles.
A student often just copies the “service → volume” pattern, and after a couple of days starts treating Redis as a place where “something important lives.” The problem is not the volume itself, but the shift in thinking: if Redis suddenly became critical, then the cache stopped being a cache. In our project, the source of truth remains in PostgreSQL, and Redis stores only a derived read result.
Mistake #2: expecting that attaching a volume guarantees “the cache will be warm after a restart.”
Redis is an in-memory store, and the mere existence of a volume does not mean that data is constantly and immediately written there. Redis persistence is configured separately. So if after down/up you see an empty cache, that does not necessarily mean “Docker is broken.” It often simply means you expected database behavior from a cache, and it never promised you anything like that.
Mistake #3: confusing restart with “recreating” a container and then wondering why data is “sometimes there, sometimes not.”
Restarting the same container and deleting the container followed by creating a new one are different stories. In the first case, the writable layer may remain (and perhaps some files too), while in the second case you are effectively starting from scratch. In a Docker environment, it is important to be precise with terminology: “stopped/started” and “deleted/created again” are different scenarios.
Mistake #4: accidentally deleting needed volumes with down -v and then trying to “restore” the cache as if it were a database.
down -v is a powerful command, and it exists specifically so you can reliably reset the local environment. But if you enabled persisted Redis for convenience and then automatically run down -v “because that is how you always do it,” you are erasing your own state. It is important to choose consciously: are you resetting containers now, or resetting data?
Mistake #5: moving persisted Redis into a project “by default” and not leaving a simple disposable path.
For a learning project and for junior-level work, it is more useful when the default path is simple: Redis can be recreated and nothing bad will happen. The persisted variant is better left as a deliberate extension rather than the only mode. Then the environment stays light and predictable, and the cache remains a cache instead of a second hidden database.
GO TO FULL VERSION