---
title: "Kafka tiered storage: a consumer reads from the remote tier"
description: "A lagging consumer polls from offset 0, below localStartOffset 6, so offsets 0-5 come from remote storage and 6-8 from local disk, as one Kafka log."
url: https://monedula.dev/kafka-simulator/scenarios/reading-from-remote-tier/
---

# Kafka tiered storage: a consumer reads from the remote tier

Scenario 05.3.2 · Reading from the remote tier. Storage · Advanced · about 3 min · Apache Kafka 4.3.

An offloaded prefix raises localStartOffset; a lagging consumer reading below it is served from remote (remote_read), then crosses back into the local log.

Run it: https://monedula.dev/kafka-simulator/playground/?scenario=05.3.2

## What you start with

Once a prefix is offloaded to the remote tier, the partition localStartOffset rises above logStartOffset. A consumer reading below localStartOffset is served from remote storage (a remote_read) until it catches up and crosses back into the locally retained log (KIP-405 tiered storage).

## Step by step

1. **Remote storage on · consumer offline** (t+0.000s): The topic has remote.storage.enable=true. The consumer c1 is taken offline (kill_consumer) so it falls behind while the old prefix is offloaded - its committed position stays at 0. [Open at this step](https://monedula.dev/kafka-simulator/playground/?scenario=05.3.2&t=0)
2. **9 records produced - all in the local log** (t+0.600s): Producer p1 writes 9 records (offsets 0-8). The high watermark is 9 and every record still lives on the broker's LOCAL log: localStartOffset is unset (= logStartOffset = 0), so nothing has been tiered yet. [Open at this step](https://monedula.dev/kafka-simulator/playground/?scenario=05.3.2&t=1)
3. **Old prefix offloaded to remote** (t+1.000s): tier_offload copies the [0,6) prefix to the remote tier and raises localStartOffset to 6. The records aren't deleted - they're just no longer on local disk. The local log now begins at offset 6; offsets 0-5 live only in remote storage but stay fully readable. [Open at this step](https://monedula.dev/kafka-simulator/playground/?scenario=05.3.2&t=2)
4. **Poll from offset 0 - served from remote** (t+2.200s): The consumer comes back (restart_consumer) and polls from offset 0. Because 0 is below localStartOffset (6), the broker serves those records from the REMOTE tier - a remote_read at offset 0. max.poll.records=3 returns offsets 0-2; the position advances to 3, still below localStartOffset. [Open at this step](https://monedula.dev/kafka-simulator/playground/?scenario=05.3.2&t=3)
5. **Still below localStartOffset - remote again** (t+3.000s): The next poll starts at offset 3 - still below localStartOffset (6) - so it is another remote_read (at offset 3), delivering offsets 3-5. The position reaches 6: the consumer has drained the entire offloaded prefix from remote storage. [Open at this step](https://monedula.dev/kafka-simulator/playground/?scenario=05.3.2&t=4)
6. **Crossed into the local log - caught up** (t+3.800s): The final poll starts at offset 6 = localStartOffset, so these records come straight from the LOCAL log - no remote_read. Offsets 6-8 are delivered and the position catches up to the high watermark (9). Tiered storage is transparent to the consumer: it read all 9 records, remote then local, with nothing lost. [Open at this step](https://monedula.dev/kafka-simulator/playground/?scenario=05.3.2&t=5)

## The takeaway

Reads below localStartOffset transparently fetch from the remote tier - the consumer sees one continuous log and pays only extra latency for the offloaded segments.

## Configs in this scenario

- [`remote.storage.enable`](https://kafka.apache.org/43/configuration/topic-configs/#topicconfigs_remote.storage.enable): Turns on tiered storage (KIP-405) for the topic: older log segments are offloaded from local disk to a remote tier once they pass local.retention.ms, and a consumer reading past them hits a remote read. Total data is still bounded by retention.ms - tiering changes WHERE the data lives, not how long it is kept.
- [`max.poll.records`](https://kafka.apache.org/43/configuration/consumer-configs/#consumerconfigs_max.poll.records): The most records one poll() returns (default 500); a smaller value shortens the processing between two polls, which keeps a group member inside max.poll.interval.ms.

## Further reading

- [Apache Kafka documentation](https://kafka.apache.org/43/operations/tiered-storage/)
