# Grafana Loki 文档

Grafana Loki 日志系统的说明。

# Community

# Contributing to Loki

For the full contribution guide, see [CONTRIBUTING.md](https://github.com/grafana/loki/blob/main/CONTRIBUTING.md) in the root of the repository.

## Building

Clone the repository and use `make` to build:

```bash
git clone https://github.com/grafana/loki.git
cd loki
```

| Command | Output |
|---|---|
| `make loki` | `./cmd/loki/loki` |
| `make logcli` | `./cmd/logcli/logcli` |
| `make loki-canary` | `./cmd/loki-canary/loki-canary` |
| `make all` | all of the above |
| `make loki-image` | Docker image for Loki |

## Running tests

```bash
make test              # unit tests
make test-integration  # integration tests (requires Docker, ~15 min)
make lint              # run linters
```

## Contribute to the Helm Chart

Refer to [production/helm/loki/CONTRIBUTING.md](https://github.com/grafana/loki/blob/main/production/helm/loki/CONTRIBUTING.md) for chart-specific guidelines. The official Loki Helm chart is maintained in the [grafana/helm-charts](https://github.com/grafana/helm-charts) repository.

# Loki community frequently asked questions

### Is Loki open source? What is the license?

Grafana Loki is open source, licensed under the GNU Affero General Public License v3 (AGPLv3). The license change from Apache 2.0 to AGPLv3 took effect in 2021.

The practical implication of AGPLv3 is that if you modify Loki and make it available over a network, you must make your modifications available under the same license. For most teams running Loki internally for log aggregation, this has no effect. If you are embedding Loki in a commercial product or offering it as a service, review the [license terms](https://github.com/grafana/loki/blob/main/LICENSE) or contact Grafana Labs about a commercial license.

Other components in the Grafana observability stack (Grafana, Tempo, Mimir, Alloy) each have their own license terms, detailed in their respective repositories.

### How is the documentation organized, and where do I find docs for a specific product?

The Loki documentation lives at [grafana.com/docs/loki/](https://grafana.com/docs/loki/latest/) and is organized into the following top-level sections:

- **[Get started](https://grafana.com/docs/loki/latest/get-started/)** — Architecture overview, deployment modes, and label concepts.
- **[Set up](https://grafana.com/docs/loki/latest/setup/)** — Installation instructions (Helm, Docker, local binary), migration guides, and upgrade steps.
- **[Configure](https://grafana.com/docs/loki/latest/configure/)** — Configuration reference and example configurations for common object storage backends.
- **[Send data](https://grafana.com/docs/loki/latest/send-data/)** — Guides for shipping logs to Loki using Grafana Alloy, Promtail, the Docker driver, and the HTTP API.
- **[Query](https://grafana.com/docs/loki/latest/query/)** — LogQL language reference, query examples, and the query API.
- **[Manage](https://grafana.com/docs/loki/latest/operations/)** — Day-two operations including storage, retention, caching, and multi-tenancy.
- **[Alert](https://grafana.com/docs/loki/latest/alert/)** — Recording rules and alerting rules powered by LogQL.

Documentation for other Grafana Labs products (Grafana, Mimir, Tempo, Alloy, and others) is available from the [Grafana documentation home page](https://grafana.com/docs/).

### Am I reading the right version of the docs?

The Loki documentation is versioned to match each release. By default, links from grafana.com and search results take you to the latest version, but if you arrived via an older link — for example, from a blog post, a forum thread, or a search result — you may be reading documentation for an older release.

To check which version you are reading, look at the version selector in the left-hand navigation panel. If it does not say **latest**, you can switch to the current version from that selector, or navigate directly using `/latest/` in the URL — for example, `grafana.com/docs/loki/latest/`.

If the version selector shows **next**, you are reading unpublished documentation for the upcoming release. This content may describe features or configuration options that are not yet available in any released version of Loki. The **next** docs are useful for previewing what is coming, but should not be used as a reference for your current deployment.

If a configuration option or feature described in the docs does not appear to exist in your Loki deployment, a version mismatch is the most common explanation. Check your installed Loki version (run `loki -version` or check the container image tag) and compare it against the version shown in the docs before assuming the documentation is wrong.

### How can I contribute to or report a problem with the docs?

The Loki documentation is open source and lives in the [`docs/sources`](https://github.com/grafana/loki/tree/main/docs/sources) directory of the Loki GitHub repository. Community contributions are welcome.

At the bottom of every documentation page you will find three options:

- **Suggest an edit** — opens the page source directly in GitHub so you can submit a pull request for a correction or improvement.
- **Contribute to docs** — for reporting a minor issue or mistake without making the edit yourself.
- **Report a problem** — for reporting technical issues with how the documentation page renders or behaves.

For questions that the documentation has not answered, the [Grafana Community Forum](https://community.grafana.com/) is the best place to ask. You can also join the `#loki` channel in the [Grafana Labs Community Slack](https://slack.grafana.com/) to discuss Loki-specific topics with maintainers and other users.

### How can I contribute to or report a problem with the code?

Loki is developed in the open on GitHub at [grafana/loki](https://github.com/grafana/loki). There are several ways to contribute:

- **Report a bug.** Open an [issue](https://github.com/grafana/loki/issues/new/choose) on GitHub. Include your Loki version, deployment mode, relevant configuration, and steps to reproduce the problem.
- **Suggest a feature.** Feature requests are also tracked as GitHub issues. Describe the use case and the behavior you would like to see.
- **Submit a pull request.** The full contribution guide is in [CONTRIBUTING.md](https://github.com/grafana/loki/blob/main/CONTRIBUTING.md). It covers building from source, running tests, code style, and the review process. The [Contributing to Loki](https://grafana.com/docs/loki/latest/community/contributing/) docs page provides a quick-start summary.

{{< admonition type="note" >}}
First-time contributors should look for issues labeled [`good first issue`](https://github.com/grafana/loki/labels/good%20first%20issue) for approachable starting points.
{{< /admonition >}}

# Contacting the Loki Team

For questions regarding Loki:

- Ask a question in the `#loki` channel of our [Slack](https://slack.grafana.com/) community
- Open source Loki users are welcome to post technical questions on the Grafana Labs Community Forums under the Grafana Loki category at [community.grafana.com](https://community.grafana.com). Please be mindful that this is a community-driven support channel moderated by Grafana Labs staff where Loki maintainers and community members answer questions when bandwidth allows. Be sure to review the [Community Guidelines](https://community.grafana.com/guidelines) before posting.
- Users using Loki via [Grafana Cloud](/products/cloud/) can submit support tickets via the [Grafana.com Account Portal](/login).
- For questions regarding Enterprise support for Loki, you can get in touch with the Grafana Labs team [here](/contact?pg=docs).

Your feedback is always welcome! To submit feedback or a report a potential bug:

- [File a GitHub issue](https://github.com/grafana/loki/issues/new) for bugs, issues and feature suggestions. Grafana UI issues for the Loki data source plugin should be posted directly to the [Grafana repository](https://github.com/grafana/grafana/issues/new), not the Loki repository.
- Send an email to [lokiproject@googlegroups.com](mailto:lokiproject@googlegroups.com), or visit the [google groups](https://groups.google.com/forum/#!forum/lokiproject) page.
- Join us for our monthly [Loki Community Call](https://docs.google.com/document/d/1MNjiHQxwFukm2J4NJRWyRgRIiK7VpokYyATzJ5ce-O8/edit?usp=sharing) on first Thursday of every month alternating between EU (12:00 UTC) and US (17:00 UTC) timezones. Subscribe to the calendar in the doc for being up to date.

# Governance

This document describes the rules and governance of the project. It is meant to be followed by all the developers of the project and the Loki community. Common terminology used in this governance document are listed below:

- **Team members**: Any members of the private [team mailing list][team].

- **Maintainers**: Maintainers lead an individual project or parts thereof ([`MAINTAINERS.md`][maintainers]).

- **Projects**: A single repository in the Grafana GitHub organization and listed below is referred to as a project:
  - loki
  - puppet-promtail

- **The Loki project**: The sum of all activities performed under this governance, concerning one or more repositories or the community.

- **The Loki SIG (Special Interest Group) Operator**: The sum of all activities performed under this governance, concerning the repository subfolder `operator` or its specific community.

## Values

The Loki developers and community are expected to follow the values defined in the [Code of Conduct][coc]. Furthermore, the Loki community strives for kindness, giving feedback effectively, and building a welcoming environment. The Loki developers generally decide by consensus and only resort to conflict resolution by a majority vote if consensus cannot be reached.

## Projects

Each project must have a [`MAINTAINERS.md`][maintainers] file with at least one maintainer. Where a project has a release process, access and documentation should be such that more than one person can perform a release. Releases should be announced on the [announcement][announce] and [users][users] mailing lists. Any new projects should be first proposed on the [team mailing list][team] following the voting procedures listed below.

## Decision making

### Team members

Team member status may be given to those who have made ongoing contributions to the Loki project for at least 3 months. This is usually in the form of code improvements and/or notable work on documentation, but organizing events or user support could also be taken into account.

New members may be proposed by any existing member by email to the [team mailing list][team]. It is highly desirable to reach consensus about acceptance of a new member. However, the proposal is ultimately voted on by a formal [supermajority vote](#supermajority-vote).

If the new member proposal is accepted, the proposed team member should be contacted privately via email to confirm or deny their acceptance of team membership. This email will also be CC'd to the [team mailing list][team] for record-keeping purposes.

If they choose to accept, the [onboarding](#onboarding) procedure is followed.

Team members may retire at any time by emailing [the team][team].

Team members can be removed by [supermajority vote](#supermajority-vote) on [the team mailing list][team].
For this vote, the member in question is not eligible to vote and does not count towards the quorum.
Any removal vote can cover only one single person.

Upon death of a member, they leave the team automatically.

In case a member leaves, the [offboarding](#offboarding) procedure is applied.

The current team members are:
<!-- vale Grafana.GrafanaSpelling = NO -->
- Aditya C S - [adityacs](https://github.com/adityacs)
- Ashwanth Goli - [ashwanthgoli](https://github.com/ashwanthgoli) ([Grafana Labs](/))
- Cyril Tovena - [cyriltovena](https://github.com/cyriltovena) ([Grafana Labs](/))
- Danny Kopping - [dannykopping](https://github.com/dannykopping) ([Grafana Labs](/))
- David Kaltschmidt - [davkal](https://github.com/davkal) ([Grafana Labs](/))
- Dylan Guedes - [dylanguedes](https://github.com/dylanguedes) ([Grafana Labs](/))
- Edward Welch - [slim-bean](https://github.com/slim-bean) ([Grafana Labs](/))
- Goutham Veeramachaneni - [gouthamve](https://github.com/gouthamve) ([Grafana Labs](/))
- Joe Elliott - [joe-elliott](https://github.com/joe-elliott) ([Grafana Labs](/))
- Karsten Jeschkies - [jeschkies](https://github.com/jeschkies) ([Grafana Labs](/))
- Kaviraj Kanagaraj - [kavirajk](https://github.com/kavirajk) ([Grafana Labs](/))
- Li Guozhong - [liguozhong](https://github.com/liguozhong) ([Alibaba Cloud](https://alibabacloud.com/))
- Michel Hollands - [michelhollands](https://github.com/michelhollands) ([Grafana Labs](/))
- Owen Diehl - [owen-d](https://github.com/owen-d) ([Grafana Labs](/))
- Periklis Tsirakidis - [periklis](https://github.com/periklis) ([Red Hat](https://www.redhat.com/))
- Salva Corts - [salvacorts](https://github.com/salvacorts) ([Grafana Labs](/))
- Sandeep Sukhani - [sandeepsukhani](https://github.com/sandeepsukhani) ([Grafana Labs](/))
- Susana Ferreira - [ssncferreira](https://github.com/ssncferreira)
- Tom Braack - [sh0rez](https://github.com/sh0rez) ([Grafana Labs](/))
- Tom Wilkie - [tomwilkie](https://github.com/tomwilkie) ([Grafana Labs](/))

The current Loki SIG Operator team members are:

- Brett Jones - [blockloop](https://github.com/blockloop/) ([InVision](https://www.invisionapp.com/))
- Cyril Tovena - [cyriltovena](https://github.com/cyriltovena) ([Grafana Labs](/))
- Gerard Vanloo - [Red-GV](https://github.com/Red-GV) ([IBM](https://www.ibm.com))
- Periklis Tsirakidis - [periklis](https://github.com/periklis) ([Red Hat](https://www.redhat.com))
- Sashank Agrawal - [sasagarw](https://github.com/sasagarw/) ([Red Hat](https://www.redhat.com))
<!-- vale Grafana.GrafanaSpelling = YES -->
### Maintainers

Maintainers lead one or more project(s) or parts thereof and serve as a point of conflict resolution amongst the contributors to this project. Ideally, maintainers are also team members, but exceptions are possible for suitable maintainers that, for whatever reason, are not yet team members.

Changes in maintainership have to be announced on the [developers mailing list][devs]. They are decided by [rough consensus](#consensus) and formalized by changing the [`MAINTAINERS.md`][maintainers] file of the respective repository.

Maintainers are granted commit rights to all projects covered by this governance.

A maintainer or committer may resign by notifying the [team mailing list][team]. A maintainer with no project activity for a year is considered to have resigned. Maintainers that wish to resign are encouraged to propose another team member to take over the project.

A project may have multiple maintainers, as long as the responsibilities are clearly agreed upon between them. This includes coordinating who handles which issues and pull requests.

### Technical decisions

Technical decisions that only affect a single project are made informally by the maintainer of this project, and [rough consensus](#consensus) is assumed. Technical decisions that span multiple parts of the project should be discussed and made on the [developer mailing list][devs].

Decisions are usually made by [rough consensus](#consensus). If no consensus can be reached, the matter may be resolved by [majority vote](#majority-vote).

### Governance changes

Changes to this document are made by Grafana Labs.

### Other matters

Any matter that needs a decision may be called to a vote by any member if they deem it necessary. For private or personnel matters, discussion and voting takes place on the [team mailing list][team], otherwise on the [developer mailing list][devs].

## Voting

The Loki project usually runs by informal consensus, however sometimes a formal decision must be made.

Depending on the subject matter, as laid out [above](#decision-making), different methods of voting are used.

For all votes, voting must be open for at least one week. The end date should be clearly stated in the call to vote. A vote may be called and closed early if enough votes have come in one way so that further votes cannot change the final decision.

In all cases, all and only [team members](#team-members) are eligible to vote, with the sole exception of the forced removal of a team member, in which said member is not eligible to vote.

Discussion and votes on personnel matters (including but not limited to team membership and maintainership) are held in private on the [team mailing list][team]. All other discussion and votes are held in public on the [developer mailing list][devs].

For public discussions, anyone interested is encouraged to participate. Formal power to object or vote is limited to [team members](#team-members).

### Consensus

The default decision making mechanism for the Loki project is [rough][rough] consensus. This means that any decision on technical issues is considered supported by the [team][team] as long as nobody objects or the objection has been considered but not necessarily accommodated.

Silence on any consensus decision is implicit agreement and equivalent to explicit agreement. Explicit agreement may be stated at will. Decisions may, but do not need to be called out and put up for decision on the [developers mailing list][devs] at any time and by anyone.

Consensus decisions can never override or go against the spirit of an earlier explicit vote.

If any [team member](#team-members) raises objections, the team members work together towards a solution that all involved can accept. This solution is again subject to rough consensus.

In case no consensus can be found, but a decision one way or the other must be made, any [team member](#team-members) may call a formal [majority vote](#majority-vote).

### Majority vote

Majority votes must be called explicitly in a separate thread on the appropriate mailing list. The subject must be prefixed with `[VOTE]`. In the body, the call to vote must state the proposal being voted on. It should reference any discussion leading up to this point.

Votes may take the form of a single proposal, with the option to vote yes or no, or the form of multiple alternatives.

A vote on a single proposal is considered successful if more vote in favor than against.

If there are multiple alternatives, members may vote for one or more alternatives, or vote “no” to object to all alternatives. It is not possible to cast an “abstain” vote. A vote on multiple alternatives is considered decided in favor of one alternative if it has received the most votes in favor, and a vote from more than half of those voting. Should no alternative reach this quorum, another vote on a reduced number of options may be called separately.

### Supermajority vote

Supermajority votes must be called explicitly in a separate thread on the appropriate mailing list. The subject must be prefixed with `[VOTE]`. In the body, the call to vote must state the proposal being voted on. It should reference any discussion leading up to this point.

Votes may take the form of a single proposal, with the option to vote yes or no, or the form of multiple alternatives.

A vote on a single proposal is considered successful if at least two thirds of those eligible to vote vote in favor.

If there are multiple alternatives, members may vote for one or more alternatives, or vote “no” to object to all alternatives. A vote on multiple alternatives is considered decided in favor of one alternative if it has received the most votes in favor, and a vote from at least two thirds of those eligible to vote. Should no alternative reach this quorum, another vote on a reduced number of options may be called separately.

## On- / Offboarding

### Onboarding

The new member is

- added to the list of [team members](#team-members). Ideally by sending a PR of their own, at least approving said PR.
- announced on the [developers mailing list][devs] by an existing team member. Ideally, the new member replies in this thread, acknowledging team membership.
- added to the projects with commit rights.
- added to the [team mailing list][team].

### Offboarding

The ex-member is

- removed from the list of [team members](#team-members). Ideally by sending a PR of their own, at least approving said PR. In case of forced removal, no approval is needed.
- removed from the projects. Optionally, they can retain maintainership of one or more repositories if the [team](#team-members) agrees.
- removed from the team mailing list and demoted to a normal member of the other mailing lists.
- not allowed to call themselves an active team member any more, nor allowed to imply this to be the case.
- added to a list of previous members if they so choose.

If needed, we reserve the right to publicly announce removal.

[announce]: https://groups.google.com/forum/#!forum/loki-announce
[coc]: https://github.com/grafana/loki/blob/main/CODE_OF_CONDUCT.md
[devs]: https://groups.google.com/forum/#!forum/loki-developers
[maintainers]: https://github.com/grafana/loki/blob/main/MAINTAINERS.md
[rough]: https://tools.ietf.org/html/rfc7282
[team]: https://groups.google.com/forum/#!forum/loki-team
[users]: https://groups.google.com/forum/#!forum/loki-users

# Promtail Push API

- Author: Robert Fratto (@rfratto)
- Date: Feb 4 2020
- Status: DRAFT

Despite being an optional piece of software, Promtail provides half the power
of Loki's story: log transformations, service discovery, metrics from logs,
and context switching between your existing metrics and logs. Today, Promtail
can only be operated to consume logs from very specific sources: files, journal,
or syslog. If users wanted to write custom tooling to ship logs, the tooling
has to bypass Promtail and push directly to Loki. This can lead users to
reimplement functionality Promtail already provides, including its error retries
and batching code.

This document proposes a Push API for Promtail. The preferred implementation is
by copying the existing Loki Push API and implementing it for Promtail. By
being compatible with the Loki Push API, the Promtail Push API can allow batches
of logs to be processed at once for optimize performance. Matching the
Promtail API also allows users to transparently switch their push URLs from
their existing tooling. Finally, a series of alternative solutions are detailed.

## Configuration

Promtail will have a new target called HTTPTarget, configurable in the
`scrape_config` array with the following schema:

```yaml
# Defines an HTTP target, which exposes an endpoint against the Promtail
# HTTP server to accept log traffic.
http:
  # Defines the base URL for the push path, adding a prefix to the
  # exposed endpoint. The final endpoint path is
  # <base_url>loki/api/v1/push. If omitted, defaults to /.
  #
  # Multiple http targets with the same base_url must not exist.
  base_url: /

  # Map of labels to add to every log line passed through to the target.
  labels: {}
```

### Considerations

Users will be able to define multiple `http` scrape configs, but the base URL
value must be different for each instance. This allows to cleanly separate
pipelines through different push endpoints.

Users must also be aware about problems with running Promtail with an HTTP
target behind a load balancer: if payloads are load balanced between multiple
Promtail instances, ordering of logs in Loki will be disrupted leading to
rejected pushes. Users are recommended to do one of the following:

1. Have a dedicated Promtail instance for receiving pushes. This also applies to
   using the syslog target.
1. Have a separate Kubernetes service that always resolves to the same Promtail pod,
   bypassing the load balancing issue.

## Implementation

As discussed in this document, this feature will be implemented by copying the
existing [Loki Push API](/docs/loki/<LOKI_VERSION>/api/#post-lokiapiv1push)
and exposing it via Promtail.

## Considered Alternatives

Using the existing API was chosen for its simplicity and capabilities of being
used for interesting configurations (e.g., chaining Promtails together). These
other options were considered but rejected as not the best solution for the
problem being solved.

Note that Option 3 has value and may be implemented separately from this
feature.

### Option 1: JSON / Protobuf Payload

A new JSON and Protobuf payload format can be designed instead of the existing
Loki push payload. Both formats would have to be exposed to support clients that
either can't or won't use protobuf marshalling.

The primary benefit of this approach is to allow us to tweak the payload schema
independently of Loki's existing schema, but otherwise may not be very useful
and is essentially just code duplication.

### Option 2: gRPC Service

The
[logproto.Pusher](https://github.com/grafana/loki/blob/f7ee1c753c76ef63338d53cfba782188a165144d/pkg/logproto/logproto.proto#L8-L10)
service could be exposed through Promtail. This would enable clients stubs to be
generated for languages that have gRPC support, and, for HTTP1 support, a
[gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) would be embedded
in Promtail itself.

This implementation option is similar to the original proposed solution, but
uses the gRPC gateway to handle HTTP/1 traffic instead of the HTTP1 shim that
Loki uses. There are some concerns with this approach:

1. The gRPC Gateway reverse proxy will need to play nice with the existing HTTP
   mux used in Promtail.
1. We couldn't control the HTTP and Protobuf formats separately as Loki can.
1. Log lines will be double-encoded thanks to the reverse proxy.
1. A small overhead of using a reverse proxy in-process will be introduced.
1. This breaks our normal pattern of writing our own shim functions; may add
   some cognitive overhead of having to deal with the gRPC gateway as an outlier
   in the code.

### Option 3: Plaintext Payload

Prometheus' [Push Gateway API](https://github.com/prometheus/pushgateway#command-line)
is cleverly designed and we should consider implementing our API in the same
format: users would push to `http://promtail-url/push/label1/value1?timestamp=now`
with a plaintext POST body. For example:

```
curl -X POST http://promtail.default/push/foo/bar/fizz/buzz -d “hello, world!”
```

This approach may be slightly faster when compared to a non-plaintext payload as
no unmarshaling needs to be performed. This URL path and timestamp still needs
to be parsed, but this will generally be faster than the reflection requirements
imposed by JSON.

However, note that this API limits Promtail to accepting one line at a time and
may cause performance issues when trying to handle large volumes of traffic. As
an alternative, this API could also be implemented by external tooling and be
built on top of any of the other implementation options.

An [example implementation](https://github.com/grafana/loki/pull/1270) was
created and has received positive support for its simplicity and ease of
integration.

# Write-Ahead Logs

Author: Owen Diehl - [owen-d](https://github.com/owen-d) ([Grafana Labs](/))

Date: 30/09/2020

## Impetus

Loki already takes numerous steps to ensure the persistence of log data, most notably the use of a configurable replication factor (redundancy) in the ingesters. However, this still leaves much to be desired in persistence guarantees, especially for single binary deployments. This proposal outlines a write ahead log (WAL) in order to complement existing measures by allowing storage/replay of incoming writes via local disk on the ingester components.

## Strategy

We suggest a two pass WAL implementation which includes an initial recording of accepted writes (`segments`) and a subsequent checkpointing (`checkpoints`) which coalesces the first pass into more efficient representations to speed up replaying.

### Segments

Segments are the first pass and most basic WAL. They store individual records of incoming writes that have been accepted and can be used to reconstruct the in memory state of an ingester without any external input. Each segment is some multiple of 32kB and upon filling one segment, a new segment is created. Initially Loki will try 256kb segment sizes, readjusting as necessary. They are sequentially named on disk and are automatically created when a target size is hit as follows:

```
data
└── wal
   ├── 000000
   ├── 000001
   └── 000002
```

### Truncation

In order to prevent unbounded growth and remove operations which have been flushed to storage from the WAL, it is regularly truncated and all but the last segment (which is currently active) are deleted at a configurable interval (`ingester.checkpoint-duration`). This is where checkpoints come into the picture.

### Checkpoints

Before truncating the WAL, we advance the WAL segments by one in order to ensure we don't delete the currently writing segment. The directory will look like:

```
data
└── wal
   ├── 000000
   ├── 000001
   ├── 000002 <- likely not full, no matter
   └── 000003 <- newly written, empty
```

Each in memory stream is iterated across an interval, calculated by `checkpoint_duration / in_memory_streams` and written to the checkpoint. After the checkpoint completes, it is moved from its temp directory to the `ingester.wal-dir`, taking the name of the last segment before it started (`checkpoint.000002`) and then all applicable segments (`00000`, `00001`, `00002`) and any previous checkpoint are deleted.

Afterwards, it will look like:

```
data
└── wal
   ├── checkpoint.000002 <- completed checkpoint
   └── 000003 <- currently active wal segment
```

#### Queueing Checkpoint operations

It’s possible that one checkpoint operation will start at the same time another is running. In this case, the existing checkpoint operation should disregard its internal ticker and flush its series as fast as possible. Afterwards, the next checkpoint operation can begin. This will likely create a localized spike in IOPS before the amortization of the following checkpoint operation takes over and is another important reason to run the WAL on an isolated disk in order to mitigate noisy neighbor problems. After we've written/moved the current checkpoint, we reap the old one.

### WAL Record Types

#### Streams

A `Stream` record type is written when an ingester receives a push for a series it doesn't yet have in memory. At a high level, this will contain
```golang
type SeriesRecord struct {
	UserID  string
	Labels labels.Labels
	Fingerprint uint64 // label fingerprint
}
```

#### Logs

A `Logs` record type is written when an ingester receives a push, containing the fingerprint of the series it refers to and a list of `(timestamp, log_line)` tuples, _after_ a `Stream` record type is written, if applicable.
```golang
type LogsRecord struct {
	UserID  string
	Fingperprint uint64 // label fingerprint for the series these logs refer to
	Entries []logproto.Entry
}
```

### Restoration

Replaying a WAL is done by loading any available checkpoints into memory and then replaying any operations from successively named segments on top (`checkpoint.000003` -> `000004` -> `000005`, etc). It's likely some of these operations will fail because they're already included in the checkpoint (due to delay introduced in our amortizations), but this is ok -- we won't _lose_ any data, only try to write some data twice, which will be ignored.

### Deployment

Introduction of the WAL requires that ingesters have persistent disks which are reconnected across restarts (this is a good fit for StatefulSets in Kubernetes). Additionally, it's recommended that the WAL uses an independent disk such that it's isolated from being affected by or causing noisy neighbor problems, especially during any IOPS spike(s).

### Implementation goals

- Use underlying prometheus wal pkg when possible for consistency and to mitigate undifferentiated heavy lifting. Interfaces handle page alignment and use []byte.
  - Ensure this package handles arbitrarily long records (log lines in Loki’s case).
- Ensure our in memory representations can be efficiently moved to/from `[]byte` in order to generate conversions for fast/efficient loading from checkpoints.
- Ensure chunks which have already been flushed to storage are kept around for `ingester.retain-period`, even after a WAL replay.

### Alternatives

#### Use the Cortex WAL

Since we're not checkpointing from the WAL records but instead doing a memory dump, this isn't bottlenecked by throughput but rather memory size. Therefore we can start with checkpointing by duration rather than accounting for throughput as well. This makes the proposed solution nearly identical to the Cortex WAL approach. The one caveat is that wal segments will accrue between checkpoint operations and may constitute a large amount of data (log throughput varies). We may eventually consider other routes to handle this if duration based checkpointing proves insufficient.

#### Don't build checkpoints from memory, instead write new WAL elements

Instead of building checkpoints from memory, this would build the same efficiencies into two distinct WAL Record types: `Blocks` and `FlushedChunks`. The former is a record type which will contain an entire compressed block after it's cut and the latter will contain an entire chunk + the sequence of blocks it holds when it's flushed. This may offer good enough amortization of writes because block cuts are assumed to be evenly distributed and chunk flushes have the same property and use jitter for synchronization.

This could be used to drop WAL records which have already elapsed the `ingester.retain-period`, allowing for faster WAL replays and more efficient loading.
```golang
type FlushRecord struct {
  Fingerprint uint64 // labels
  FlushedAt uint64 // timestamp when it was flushed, can be used with `ingester.retain-period` to either keep or discard records on replay
  LastEntry logproto.Entry // last entry included in the flushed chunk
}
```

It would also allow building checkpoints without relying on an ingester's internal state, but would likely require multiple WALs, partitioned by record type in order to be able to iterate all `FlushedChunks` -> `Blocks` -> `Series` -> `Samples` such that we could no-op the later (lesser priority) types that are superseded by the former types. The benefits do not seem worth the cost here, especially considering the simpler suggested alternative and the extensibility costs if we need to add new record types if/when the ingester changes internally.

# Ordering Constraint Removal

Author: Owen Diehl - [owen-d](https://github.com/owen-d) ([Grafana Labs](/))

Date: 28/01/2021

## Problem

Loki imposes an ordering constraint on ingested data; that is to say incoming data must have monotonically increasing timestamps, partitioned by stream. This has historical inertia from our parent project, Cortex, but presents unintended consequences specific to log ingestion. In contrast to metric scraping, Loki has reasonable use cases where the ordering constraint poses a problem, including:

- Ingesting logs from a cloud function without feeling pressured to add high cardinality labels like invocation_id to avoid out of order errors.
- Ingesting logs from other agents/mechanisms that don’t take into account Loki’s ordering constraint. For instance, fluent{d,bit} variants may batch and retry writes independently of other batches, causing unpredictable log loss via out of order errors.

Many of these illustrate the adversity between _ordering_ and _cardinality_. In addition to enabling some previously difficult/impossible use cases, removing the ordering constraint lets us avoid potential conflict between these two concepts and helps incentivize good practice in the form of fewer useful labels.

### Requirements

- Enable out of order writes
- Maintain query interface parity

#### Bonuses

- Optimize for in order writes


## Alternatives

- Implement order-agnostic blocks + increase memory usage by compression_ratio: Deemed unacceptable due to TCO (total cost of ownership).
- Implement order-agnostic blocks + scale horizontally (reduce per-ingester streams): Deemed unacceptable due to TCO and increasing ring pressure.
- Implement order-agnostic blocks + flush chunks more frequently: Deemed unacceptable due to negatively increasing the index and the number of chunks requiring merge during reads.

## Design

### Background

I suggest allowing a stream's head block to accept unordered writes and later re-order cut blocks similar to merge-sort before flushing them to storage. Currently, writes are accepted in monotonically increasing timestamp order to a _headBlock_, which is occasionally "cut" into a compressed, immutable _block_. In turn, these _blocks_ are combined into a _chunk_ and persisted to storage.

```
Figure 1

    Data while being buffered in Ingester          |                                Chunk in storage
                                                   |
    Blocks                    Head                 |       ---------------------------------------------------------------------
                                                   |       |   ts0   ts1    ts2   ts3    ts4   ts5    ts6   ts7    ts8    ts9  |
--------------           ----------------          |       |   ---------    ---------    ---------    ---------    ---------   |
|    blocks  |--         |  head block  |          |       |   |block 0|    |block 1|    |block 2|    |block 4|    |block 5|   |
|(compressed)| |         |(uncompressed)|          |       |   |       |    |       |    |       |    |       |    |       |   |
|            | | ------> |              |          |       |   ---------    ---------    ---------    ---------    ---------   |
|            | |         |              |          |       |                                                                   |
-------------- |         ----------------          |       ---------------------------------------------------------------------
  |            |                                   |
  --------------                                   |
```

Historically because of Loki's ordering constraint, these blocks maintain a monotonically increasing timestamp (abbreviated `ts`) order where

```
Figure 2

start       end
ts0         ts1          ts2        ts3
--------------           --------------
|            |           |            |
|            | --------> |            |
|            |           |            |
|            |           |            |
--------------           --------------
```

This allows us two optimizations:

1) We can store much more data in memory because each block is compressed after being cut from a head block.
2) We can query the block's metadata, such as `ts0` and `ts1` and skip querying it in the case of i.e. the timestamps are outside a request's bounds.

### Unordered Head Blocks

The head block's internal structure will be replaced with a tree structure, enabling logarithmic inserts/lookups and `n log(n)` scans. _Cutting_ a block from the head block will iterate through this tree, creating a sorted block identical to the ones currently in use. However, because we'll be accepting arbitrarily-ordered writes, there will no longer be any guaranteed inter-block order. In contrast to figure 2, blocks may have overlapping data:

```
Figure 3

start       end
ts1         ts3          ts0        ts2
--------------           --------------
|            |           |            |
|            | --------> |            |
|            |           |            |
|            |           |            |
--------------           --------------
```

Thus _all_ blocks must have their metadata checked against a query. In this example, a query for the bounds `[ts1,ts2]`  would need to decompress and scan the `[ts1, ts2]` range across both of them, but a query against `[ts3, ts4]` would only decompress and scan _one_ block.

```
Figure 4

     chunk1
-------------------
                chunk2
         ---------------------
         query range requiring both
         ----------
                             query range requiring chunk2 only
                             -----------
ts0     ts1      ts2       ts3        ts4 (not in any block)
------------------------------
|        |        |          |
|        |        |          |
|        |        |          |
|        |        |          |
------------------------------

```

The performance losses against the current approach includes:

1) Appending a log line is now performed in logarithmic time instead of amortized (due to array resizing) constant time.
2) Blocks may contain overlapping data (although ordering is still guaranteed within each block).
3) Head block scans are now `O(n log(n))` instead of `O(n)`

### Flushing and Chunk Creation

Loki regularly combines multiple blocks into a chunk and "flushes" it to storage. In order to ensure that reads over flushed chunks remain as performant as possible, we will re-order a possibly-overlapping set of blocks into a set of blocks that maintain monotonically increasing order between them. From the perspective of the rest of Loki’s components (queriers/rulers fetching chunks from storage), nothing has changed.

{{< admonition type="note" >}}
**In the case that data for a stream is ingested in order, this is effectively a no-op, making it well optimized for in-order writes (which is both the requirement and default in Loki currently). Thus, this should have little performance impact on ordered data while enabling Loki to ingest unordered data.**
{{< /admonition >}}


#### Chunk Durations

When `--validation.reject-old-samples` is enabled, Loki accepts incoming timestamps within the range
```
[now() - `--validation.reject-old-samples.max-age`, now() + `--validation.create-grace-period`]
```
For most of our clusters, this would mean the range of acceptable data is one week long. In contrast, our max chunk age is `2h`. Allowing unordered writes would mean that ingesters would willingly receive data for 168h, or up to 84 distinct chunk lengths. This presents a problem: a malicious user could be writing to many (84 in this case) distinct chunks simultaneously, flooding Loki with underutilized chunks which bloat the index.

In order to mitigate this, there are a few options (not mutually exclusive):
1) Lower the valid acceptance range
2) Create an _active_ validity window, such as `[most_recent_sample-max_chunk_age, now() + creation_grace_period]`.

The first option is simple, already available, and likely somewhat reasonable.
The second is simple to implement and an effective way to ensure Loki can ingest unordered logs but maintain a sliding validity window. I expect this to cover nearly all reasonable use cases and effectively mitigate bad actors.

#### Chunk Synchronization

We also cut chunks according to the `sync_period`. The first timestamp ingested past this bound will trigger a cut. This process aids in increasing chunk determinism and therefore our deduplication ratio in object storage because chunks are [content addressed](https://en.wikipedia.org/wiki/Content-addressable_storage). With the removal of our ordering constraint, it's possible that in some cases the synchronization method will not be as effective, such as during concurrent writes to the same stream across this bound.

{{< admonition type="note" >}}
**It's important to mention that this is possible today with the current ordering constraint, but we'll be increasing the likelihood by removing it.**
{{< /admonition >}}

```
Figure 5

       Concurrent Writes over threshold
                   ^ ^
                   | |
                   | |
-----------------|-----------------
                 |
                 v
             Sync Marker
```


To mitigate this problem and preserve the benefits of chunk deduplication, we'll need to make chunk synchronization less susceptible to non-determinism during concurrent writes. To do this, we can move the synchronization trigger from the `Append` code path to the asynchronous `FlushLoop`. Note, the semantics for _when_ a chunk is cut will not change: that is, on the first timestamp crossing the synchronization bound. However, _cutting_ the chunks for synchronization on the flush path mitigates the likelihood of _different_ chunks being cut. In order to cut multiple chunks with different hashes, appends would then need to cross this boundary at the same time the flush loop checks the stream, which should be very unlikely.

### Future Opportunities

This ends the initial design portion of this document. Below, I'll describe some possible changes we can address in the future, should they become warranted.

#### Variance Budget

The intended approach of a "sliding validity" window for each stream is simple and effective at preventing misuse and bad actors from writing across the entire acceptable range for incoming timestamps. However, we may in the future wish to take a more sophisticated approach, introducing per tenant "variance" budgets, likely derived from the stream limit. This ingester limit could, for example use an incremental (online) standard deviation/variance algorithm such as [Welford's](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance), which would allow writing to larger ranges than option (2) in the _Chunk Durations_ section.

#### LSM Tree

Much of the proposed approach mirrors an [LSM-Tree](http://www.benstopford.com/2015/02/14/log-structured-merge-trees/) (Log Structured Merge Tree), albeit in memory instead of using disk. What a weird choice -- LSM Trees are designed to effectively use disk, so why not go that route? We currently have no wish to add extra disk dependencies to Loki where we can avoid it, but below I will outline what an LSM-Tree approach would look like. Ultimately, using disk would enable buffering more data in the ingester before flushing,

**Allowing us to**
- Flush more efficiently utilized chunks (in some cases)
- Keep open a wider validity window for incoming logs

**At the cost of**
- being susceptible to disk-related complexity and problems

##### MemTable (head block)

Writes in an LSM-Tree are first accepted to an in-memory structure called a _memtable_ (generally a balancing tree such as red-black) until the memtable hits a preconfigured size. In Loki, this corresponds to the stream’s head block, which is uncompressed.

##### SSTables (blocks)

Once a Memtable (head block) in an LSM-Tree hits a predefined size, it is flushed to disk as an immutable sorted structure called an SSTable (sorted strings table). In Loki, we can use either the pre-existing MemChunk format, which is ordered, compact, and contains a block index within it, or the pre-existing block format directly. These are stored on disk to lessen memory pressure and loaded for queries when necessary.

##### Block Index

Incoming reads in an LSM-Tree may need access to the SSTable entries in addition to the currently active memtable (head block). In order to improve this, we may cache the metadata including block offsets, start and end timestamps within an SSTable (block || MemChunk) in memory to mitigate lookups, seeking, and loading unnecessary data from disk.

##### Compaction (flushing)

Compaction in an LSM-Tree combines and reorders multiple SSTables (blocks || MemChunks). This is mainly covered in the _Flushing_ section of the in-memory approach, but _compaction_ is equivalent to _flushing_ for our case. That is, merge multiple SSTables on disk together in an algorithm reminiscent of merge sort and flush them to storage in our ordered chunk format.

# Labels

Author: Ed Welch
Date: February 2019

This is the official version of this doc as of 2019/04/03, the original discussion was had via a [Google doc](https://docs.google.com/document/d/16y_XFux4h2oQkJdfQgMjqu3PUxMBAq71FoKC_SkHzvk/edit?usp=sharing), which is being kept for posterity but will not be updated moving forward.

## Problem Statement

We should be able to filter logs by labels extracted from log content.

Keeping in mind:
Loki is not a log search tool and we need to discourage the use of log labels as an attempt to recreate log search functionality.  Having a label on “order number” would be bad, however, having a label on “orderType=plant” and then filtering the results on a time window with an order number would be fine.  (think: grep “plant” | grep “12324134” )
Loki as a grep replacement, log tailing or log scrolling tool is highly desirable, log labels will be useful in reducing query results and improving query performance, combined with logQL to narrow down results.

## Use Cases

As defined for prometheus “Use labels to differentiate the characteristics of the thing that is being measured” There are common cases where someone would want to search for all logs which had a level of “Error” or for a certain HTTP path (possibly too high cardinality), or of a certain order or event type.
Examples:
- Log levels.
- HTTP Status codes.
- Event type.

## Challenges

- Logs are often unstructured data, it can be very difficult to extract reliable data from some unstructured formats, often requiring the use of complicated regular expressions.
- Easy to abuse.  Easy to create a Label with high cardinality, even possibly by accident with a rogue regular expression.
- Where do we extract metrics and labels at the client (Promtail or other?) or Loki? Extraction at the server (Loki) side has some pros/cons.  Can we do both? At least with labels we could define a set of expected labels and if Loki doesn’t receive them they could be extracted.
  - Server side extraction would improve interoperability at the expense of increase server workload and cost.
  - Are there discoverability questions/concerns with metrics exposed via Loki vs the agent? Maybe this is better/easier to manage?
  - Potentially more difficult to manage configuration with the server side having to match configs to incoming log streams

## Existing Solutions

There already exist solutions for extracting processing and extracting metrics from unstructured log data, however, they will not quite work for extracting labels without some work and neither support easy inclusion as a library.  It’s worth noting and understanding how they work to try to get the best features in our solution.
mtail
https://github.com/google/mtail
1721 github stars, huge number of commits, releases and contributors, google project

All go, uses go RE2 regular expressions which is going to be more performant than grok_exporter below which uses a full regex implementation allowing backtracking and lookahead required to be compliant with Grok but which are also slower.

grok_exporter
https://github.com/fstab/grok_exporter
278 github stars, mature/active project

If you are familiar with Grok this would be more comfortable, many people use ELK stacks and would likely be familiar with or already have Grok strings for their logs, making it easy to use grok_exporter to extract metrics.

One caveat is the dependency on the oniguruma C library which parses the regular expressions.

## Implementation

### Details

As mentioned previously in the challenges for working with unstructured data, there isn’t a good one size fits all solution for extracting structured data.

The Docker log format is an example where multiple levels of processing may be required, where the docker log is json, however, it also contains the log message field which itself could be embedded json, or a log message which needs regex parsing.

A pipelined approach should allow for handling these more challenging scenarios

There are 2 interfaces within Promtail already that should support constructing a pipeline:

```go
type EntryMiddleware interface {
    Wrap(next EntryHandler) EntryHandler
}
```

```go
type EntryHandler interface {
    Handle(labels model.LabelSet, time time.Time, entry string) error
}
````

Essentially every entry in the pipeline will Wrap the log line with another EntryHandler which can add to the LabelSet, set the timestamp, and mutate(or not) the log line) before it gets handed to the next stage in the pipeline.

### Example

```json
{
  "log": "level=info msg=\”some log message\”\n",
  "stream": "stderr",
  "time": "2012-11-01T22:08:41+00:00"
}
```

This is a docker format log file which is JSON but also contains a log message which has some key-value pairs.

Our pipelined config might look like this:

```yaml
scrape_configs:
- job_name: system
  pipeline_stages:
    - json:
        timestamp:
          source: time
          format: RFC3339
        labels:
          stream:
            source: json_key_name.json_sub_key_name
        output: log
    - regex:
        expr: '.*level=(?P<level>[a-zA-Z]+).*'
        labels:
          level:
    - regex:
        expr: '.*msg=(?P<message>[a-zA-Z]+).*'
        output: message
```

Looking at this a little closer:

```yaml
     - json:
        timestamp:
          source: time
          format: TODO                               ①
        labels:
          stream:
            source: json_key_name.json_sub_key_name  ②
        output: log                                  ③
```


① The format key will likely be a format string for Go’s time.Parse or a format string for strptime, this still needs to be decided, but the idea would be to specify a format string used to extract the timestamp data, for the regex parser there would also need to be a expr key used to extract the timestamp.
② One of the json elements was “stream” so we extract that as a label, if the json value matches the desired label name it should only be required to specify the label name as a key, if some mapping is required you can optionally provide a “source” key to specify where to find the label in the document. (Note the use of `json_key_name.json_sub_key_name` is just an example here and doesn't match our example log)
③ Tell the pipeline which element from the json to send to the next stage.

```yaml
    - regex:
        expr: '.*level=(?P<level>[a-zA-Z]+).*'  ①
        labels:
          level:                                ②
```

① Define the Go RE2 regex, making sure to use a named capture group.
② Extract labels using the named capture group names.

Notice there was not an output section defined here, omitting the output key should instruct the parser to return the incoming log message to the next stage with no changes.

```yaml
    - regex:
        expr: '.*msg=(?P<message>[a-zA-Z]+).*'
        output: message                          ①
```

① Send the log message as the output to the last stage in the pipeline, this will be what you want Loki to store as the log message.

There is an alternative configuration that could be used here to accomplish the same result:

```yaml
scrape_configs:
- job_name: system
  pipeline_stages:
    - json:
        timestamp:
          source: time
          format: FIXME
        labels:
          stream:
        output: log
    - regex:
        expr: '.*level=(?P<level>[a-zA-Z]+).*msg=(?P<message>[a-zA-Z]+).*'
        labels:
          level:                                                             ①
          log:
            source: message                                                  ②
        output: message
```

① Similar to the json parser, if your log label matches the regex named group, you need only specify the label name as a yaml key
② If you had a use case for specifying a different label name from the regex group name you can optionally provide the `source` key with the value matching the named capture group.

You can define a more complicated regular expression with multiple capture groups to extract many labels and/or the output log message in one entry parser.  This has the advantage of being more performant, however, the regular expression will also get much more complicated.

Note the regex for `message` is incomplete and would do a terrible job of matching any standard log message which might contain spaces or non alpha characters.

### Concerns
- Debugging, especially if a pipeline stage is mutating the log entry.
- Clashing labels and how to handle this (two stages try to set the same label)
- Performance vs ease of writing/use, if every label is extracted one at a time and there are a lot of labels and a long line, it would force reading the line many times, however contrast this to a really long complicated regex which only has to read the line once but is difficult to write and/or change and maintain

### Further improvements
There are some basic building blocks for our pipeline which will use the EntryMiddleware interface, the two most commonly used will likely be:

- Regex Parser
- JSON Parser

However we don’t want to ask people to copy and paste basic configs over and over for very common use cases, so it would make sense to add some additional parsers which would really be supersets of the base parsers above.

For example, the config above might be simplified to:

```yaml
scrape_configs:
- job_name: system
  pipeline_stages:
    - docker:
```

or

```yaml
scrape_configs:
- job_name: system
  pipeline_stages:
    - cri:
```

Which could still easily be extended to extract additional labels:

```yaml
scrape_configs:
- job_name: system
  pipeline_stages:
    - docker:
    - regex:
        expr: '.*level=(?P<level>[a-zA-Z]+).*'
        labels:
          level:
```

### Auto Detection?
An even further simplification would be to attempt to autodetect the log format, a PR for this work has been submitted, then the config could be as simple as:

```yaml
scrape_configs:
- job_name: system
  pipeline_stages:
    - auto:
```

This certainly has some advantages for people first adopting and testing Loki, allowing them to point it at their logs and at least get the timestamp and log message extracted properly for the common formats like Docker and CRI.

There are also some challenges with auto detection and edge cases, though most people are going to want to augment the basic config with additional labels, so maybe it makes sense to default to auto but suggest when people start writing configs they chose the correct parser?

## Other Thoughts and Considerations

- We should have a standalone client in some fashion which allows for testing of log parsing at the command line, allowing users to validate regular expressions or configurations to see what information is extracted.
- Other input formats to the pipeline which are not reading from log files, such as containerd grpc api, or from stdin or unix pipes, etc.
- It would be nice if at some point we could support loading code into the pipeline stages for even more advanced/powerful parsing capabilities.

# Releasing Loki Build Image

The [`loki-build-image`](https://github.com/grafana/loki/blob/main/loki-build-image)
is the Docker image used to run tests and build Grafana Loki binaries in CI.

The build and publish process of the image is triggered upon a merge to `main`
if any changes were made in the folder `./loki-build-image/`.

**To build and use the `loki-build-image`:**

1. Create a branch with the desired changes to the `./loki-build-image/Dockerfile`.
1. Update the `BUILD_IMAGE_VERSION` variable in the `Makefile`.
1. Commit your changes.
1. Run `make build-image-push` to build and publish the new version of the build image.
1. Run `make release-workflows` to update the Github workflows.
1. Commit your changes.
1. Push your changes to the remote branch.
1. Open a PR against the `main` branch.

# Backport commits

Any PRs or commits not on the release branch that you want to include in the release must be backported to the release branch.

## Before you begin

1. Determine the [VERSION_PREFIX](../concepts/version/).

2. If the release branch already has all the code changes on it, skip this step.

## Steps

1. Pick a PR that you want to backport to `release-VERSION_PREFIX` branch.

1. Add two labels to the PR. First, one of the `product-approved`, `type/doc` or `type/bug` appropriately. This is to make sure the PRs that are backported are done with right intention. Second `backport release-VERSION_PREFIX` label.
   Now CI should automatically create backport PR to the correct release branch. Example [PR](https://github.com/grafana/loki/pull/10333)

   {{< admonition type="note" >}}
   CI automation can fail sometimes if there are some merge conflicts in cherry picking the commits. In those cases, the original PR where you added the label should have additional comment explaining how to backport it manually.
   {{< /admonition >}}

   {{< admonition type="note" >}}
   The CI job that helps with backporting PR is `.github/workflows/backport.yml`. Useful for debugging purposes.
   {{< /admonition >}}

1. Repeat the above steps for any PRs that need to be backported.

## Backporting Release PRs

If backporting a release PR, make sure you remove any `autorelease: pending` or `autorelease: tagged` labels before merging the backport PR. By default our backport action brings over all labels, but these labels are reserved for the release workflow and will cause future pipelines to fail if left of backport PRs.

# Create Release Branch

A single release branch is created for every `major` or `minor` release (not for patch releases). That release
branch is then used for all the Stable Releases, and all Patch Releases for that `major` and `minor` versions of the Grafana Loki.

## Before you begin

1. Determine the [VERSION_PREFIX](../concepts/version/).
1. Announce about the upcoming release in `#loki-releases` internal slack channel.
1. Skip this announcement for a patch release. Create an issue to communicate beginning of the release process with the community. Example issue [here](https://github.com/grafana/loki/issues/10468).

## Steps

1. Determine which commit should be used as a base for the release branch. Usually this is a `kxx` weekly release branch.

1. Create and push the release branch from the selected base commit:

    The name of the release branch should be `release-VERSION_PREFIX`, such as `release-2.9.x`.

	{{< admonition type="note" >}}
	Branches are only made for VERSION_PREFIX; do not create branches for the full VERSION such as `release-2.9.1`.
	{{< /admonition >}}

	{{< admonition type="note" >}}
	Don't create any other branches that are prefixed with `release` when creating PRs or those branches will collide with our automated release build publish rules.
	{{< /admonition >}}

1. Create a label to make backporting PRs to this branch easy.

   The name of the label should be `backport release-VERSION_PREFIX`, such as `backport release-2.9.x`.

	{{< admonition type="note" >}}
	Note there is space in the label name. The label name must follow this naming convention to trigger CI related jobs.
	{{< /admonition >}}

# Document metrics and configurations changes

Any metrics and configurations that are removed or modified need to be documented in the upgrade guide. Configurations whose default values changed should also be documented in the upgrade guide.

## Before you begin

All the steps are performed on `release-VERSION_PREFIX` branch.

## Steps

1. Check which configs changed, including whose default values changed.
   ```
   $ OLD_VERSION=X.Y.Z ./tools/diff-config.sh
   ```

1. Record configurations that are modified (either renamed or had its default value changed) in the [upgrade guide](../prepare-upgrade-guide/).

1. Check if any metrics have changed.
   ```
   $ OLD_VERSION=X.Y.Z ./tools/diff-metrics.sh
   ```

1. Record metrics whose names have been modified in the [upgrade guide](../prepare-upgrade-guide/).

# Prepare Major Release

A major release follows the same process as [minor and patch releases](../prepare-release/), but requires a custom workflow to be created to run on the branch we want to release from. The reason for this is that we don't do major releases very often, so it is not economical to keep those workflows running all the time.

To create a major release workflow, follow the steps below.

1. Edit `./github/release-workflows.jsonnet`
1. Add a new workflow for the major release. For example, the 3.0 release looked like the following:

```jsonnet
  'major-release-pr.yml': std.manifestYamlDoc(
    lokiRelease.releasePRWorkflow(
      branches=['release-*.0.0'],
      buildImage=buildImage,
      checkTemplate=checkTemplate,
      distRunsOn='ubuntu-x64',
      distOptionalTargets=['dist/loki-linux-riscv64'],
      golangCiLintVersion=golangCiLintVersion,
      imageBuildTimeoutMin=imageBuildTimeoutMin,
      imageJobs=imageJobs,
      imagePrefix=imagePrefix,
      releaseLibRef=releaseLibRef,
      releaseRepo='grafana/loki',
      skipArm=false,
      skipValidation=false,
      // Instead of hard-coding the release version, push an empty to the target branch `release-X.0.0`
      // git commit --allow-empty -m "chore: Release X.0.0-rcY" -m "Release-As: X.0.0-rcY"
      // https://github.com/googleapis/release-please#how-do-i-change-the-version-number
      // releaseAs='x.0.0',
    ) + {
      name: 'Prepare Major Release PR',
    }, false, false
  ),

```

1. Make sure the `branches` field is set to the release branch you want to release from.
1. Make sure the `releaseAs` field is set to the version you want to release.
1. Run `make release-workflows` to generate the new workflow. Merge this change to both the main and release branch. You may also want to disable the patch release workflow as part of this PR, see below.

## Race Condition with Patch Release

Once this new workflow is created there is a bit of a race condition with the patch workflow. This can be resolved in 1 of 2 ways:

1. Disable the patch release workflow until after the major release is complete.
1. Watch all actions for the release branch (ie. <https://github.com/grafana/loki/actions?query=branch%3Arelease-3.0.x>) and cancel and patch release runs.

# Merge Release PR

To release Loki, merge the release PR. This PR will have the title `chore(<BRANCH>): release <VERSION>`. Here's what the 3.0 release PR looked like.

![3.0 release PR](.3.0-release-pr.png)

3.0 was the first major release using this new process, and enforcing conventional commits was relatively new. Going forward the release notes in the release PR will be much more thorough, as we're now enforcing that every PR has a conventional commit message.

To test the artifacts before releasing, you can download the artifacts from the link in the footer of the PR description. Merging the PR will:

1. Fetch the built artifacts.
1. Create a draft GitHub release with the release notes in the PR description.
1. Upload fetched binaries to the draft release.
1. Publish fetched images to Docker Hub as multi-arch images.
1. Publish the draft release and create the GitHub tag.
1. (Optionally) Mark the release as the latest if it represents the newest version of Loki.

## Troubleshooting / Retrying Release

If something goes wrong with the release automation, you may need to rerun the job. This may require updating the release code in the `grafana/loki` repo via a PR, or it may include updating the code in `grafana/loki-release` that the release pipeline fetches. In either case, if you need to re-release a merge release PR, you'll need to remove the `autorelease: tagged` label from that PR and add the `autorelease: pending` label. The automation relies on these labels to determine which merged PRs have and have not yet been released. After fixing the labels, re-run the release workflow, and it should correctly get passed the `should-release` step. The release process is idempotent, so if a draft release has already been created via a previously failed release, the process will continue, re-upload the binaries, and re-publish the images.

Once the release is finished, you may need to manually flip the label back to `autorelease: tagged`.

# Patch Go version

Update vulnerable Go version to non-vulnerable Go version to build Grafana Loki binaries.

## Steps

1. Find Go version to which you need to update. Example `1.20.5` to `1.20.6`
1. Update Go version (`GO_VERSION`) in the `Makefile`.
1. Run `make release-workflows` to update the version in generated release workflows.
1. Run `make update-go-version` to update all relevant files.
1. Open a pull request against the target branch (`main` or `release-MAJOR.MINOR.x`).

# Patch vulnerabilities

This step patches vulnerabilities in Grafana Loki binaries and Docker images.

## Before you begin

1. Determine the [VERSION_PREFIX](../concepts/version/).

Vulnerabilities can be from two main sources.

1. Grafana Loki source code.

1. Grafana Loki dependencies.

Grafana Loki dependencies can be

1. Go dependencies in `go.mod`

1. The Go compiler itself

1. Grafana Loki Docker dependencies, for example, the base images

Before start patching vulnerabilities, know what are you patching. It can be one or more from sources mentioned above. Use `#security-go`, `#security` slack channels to clarify.

## Steps

1. Patch Grafana Loki source code.

	Means, there are vulnerabilities in Grafana Loki source code itself.

	1. Patch it on `main` branch

	1. [Backport](../backport-commits/) to `release-$VERSION_PREFIX` branch.

1. Patch Go dependencies.

	1. Pick all the Go dependencies that need to be patched.

	1. Check if [dependabot already patched the dependency](https://github.com/grafana/loki/pulls?q=is%3Apr+label%3Adependencies+is%3Aclosed) or [have a PR opened to patch](https://github.com/grafana/loki/pulls?q=is%3Apr+is%3Aopen+label%3Adependencies) . If not, manually upgrade the package on the `main` branch as follows.

		```shell
		go get -u -v <PACKAGE_PATH>@<PATCHED_VERSION>
		go mod tidy
		go mod vendor
		```
	1. [Backport](../backport-commits/) it to `release-$VERSION_PREFIX` branch.

	1. Repeat for each Go dependency

1. [Patch Go compiler](../patch-go-version/).

1. Patch Grafana Loki Docker dependencies, for example: Alpine Linux base images).

   1. Update Docker image version. [Example PR](https://github.com/grafana/loki/pull/10573).

   1. [Backport](../backport-commits/) to `release-$VERSION_PREFIX` branch

# Prepare Release

Releasing Grafana Loki consists of merging a long-running release PR. Two workflows keep these PRs up to date, one for patch releases (which runs on commits to branches matching `release-[0-9]+.[0-9]+.x`) and one for minor releases (which runs on commits to branches matching `k[0-9]+`). These pipelines use release please to do the following:

1. Run tests and linting
1. Build binaries and images for the proposed release version
1. Generate release notes based on conventional commits since the last release
1. Create or update the long-running release PR for that release, indicating which commit will be released if the PR is merged with a link to the built artifacts that will be published.

## Major releases

Major releases follow the same process as minor and patch releases, but require a custom workflow to be created to run on the branch we want to release from. The reason for this is that we don't do major releases very often, so it is not economical to keep those workflows running all the time.To create a major release workflow, follow the steps in the [major release workflow](../major-release/) document.

# Prepare Upgrade guide

The upgrade guide records changes that require user attention or interaction to upgrade to specific Loki version from previous versions.

## Before you begin

The upgrade guide in Grafana Loki works as follows.

We have a `setup/upgrade/_index.md` file that records upgrade information for all Loki releases.

## Steps

1. Make sure the upgrade guide is up to date on the `release-VERSION_PREFIX` branch under the `Main/Unreleased` section.

1. On the `release-VERSION_PREFIX` branch promote `Main/Unreleased` to `VERSION`. Example [PR](https://github.com/grafana/loki/pull/10470).

1. On the `main` branch remove entries from `Main/Unreleased` that are already part of `VERSION (YYY-MM-DD)`.

# Update version numbers

Upgrade the Loki version to the new release version in documents, examples, jsonnets, etc.

## Before you begin

1. Determine the [VERSION_PREFIX](../concepts/version/).

2. Skip this step if you are doing a patch release on old release branch.

	Example: Latest release branch is `release-2.9.x` but you are releasing patch version in `release-2.8.x` (or older)

## Steps

1. Upgrade the versions in the `release-VERSION_PREFIX` branch.

    Example commands:

    ```
	LOKI_NEW_VERSION=$VERSION ./tools/release_update_tags.sh
    ```

	{{< admonition type="note" >}}
	Do not upgrade the version numbers in the `operator/` directory as @periklis and team have a different process to upgrade the Operator version.
	{{< /admonition >}}

# Version

Grafana Loki uses Semantic Versioning. The next version can be determined
by looking at the current version and incrementing it. When the release is a new `major` version (for example, after 2.9.1 we decide to go 3.0.0 instead of 2.9.2) this should not be done.

You need to set two environmental values `VERSION` and `VERSION_PREFIX` to do the release process.

## Version

To determine the `VERSION` for a Stable Release or Patch Release, use the Semantic Version `A.B.C`.

- Examples
  - For example, `2.9.0` is the Stable Release `VERSION` for the v2.9.0 release.
  - For example, `2.9.1` is the first Patch Release `VERSION` for the v2.9.0 release.

## Version Prefix

To determine the `VERSION PREFIX`, use only the major and minor version `A.B.x`.

- Examples
  - `2.9.x`

# Configure

# Configuration best practices

Grafana Loki is under active development, and the Loki team is constantly working to improve performance. But here are some of the most current best practices for configuration that will give you the best experience with Loki.

## Configure authentication

Grafana Loki does not come with any included authentication layer. You must run an authenticating reverse proxy in front of your services to prevent unauthorized access to Loki (for example, nginx). Refer to [Manage authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/) for a list of open-source reverse proxies you can use.

## Configure caching

Loki can cache data at many levels, which can drastically improve performance. Details of this will be in a future post.

## Time ordering of logs

Loki [accepts out-of-order writes](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#accept-out-of-order-writes) _by default_.
This section identifies best practices when Loki is _not_ configured to accept out-of-order writes.

One issue many people have with Loki is their client receiving errors for out of order log entries. This happens because of this hard and fast rule within Loki:

- For any single log stream, logs must always be sent in increasing time order. If a log is received with a timestamp older than the most recent log received for that stream, that log will be dropped.

There are a few things to dissect from that statement. The first is this restriction is per stream. Let’s look at an example:

```bash
{job="syslog"} 00:00:00 i'm a syslog!
{job="syslog"} 00:00:01 i'm a syslog!
```

If Loki received these two lines which are for the same stream, everything would be fine. But what about this case:

```bash
{job="syslog"} 00:00:00 i'm a syslog!
{job="syslog"} 00:00:02 i'm a syslog!
{job="syslog"} 00:00:01 i'm a syslog!  <- Rejected out of order!
```

What can you do about this? What if this was because the sources of these logs were different systems? You can solve this with an additional label which is unique per system:

```bash
{job="syslog", instance="host1"} 00:00:00 i'm a syslog!
{job="syslog", instance="host1"} 00:00:02 i'm a syslog!
{job="syslog", instance="host2"} 00:00:01 i'm a syslog!  <- Accepted, this is a new stream!
{job="syslog", instance="host1"} 00:00:03 i'm a syslog!  <- Accepted, still in order for stream 1
{job="syslog", instance="host2"} 00:00:02 i'm a syslog!  <- Accepted, still in order for stream 2
```

But what if the application itself generated logs that were out of order? Well, I'm afraid this is a problem. If you are extracting the timestamp from the log line with something like the [Alloy `stage.timestamp` block](https://grafana.com/docs/alloy/latest/reference/components/loki/loki.process/#stagetimestamp), you could instead _not_ do this and let your log shipping agent assign a timestamp to the log lines. Or you can hopefully fix it in the application itself.

It's also worth noting that the batching nature of the Loki push API can lead to some instances of out of order errors being received which are really false positives. (Perhaps a batch partially succeeded and was present; or anything that previously succeeded would return an out of order entry; or anything new would be accepted.)

## Consider the `snappy` compression algorithm

The default `chunk_encoding` is `gzip`, which compresses well but is comparatively slow, and can cause slow query responses under load.

`Snappy` is a recommended override for `chunk_encoding` in higher-throughput environments. It performs much better than `gzip` for speed, but it is not as efficient in storage. Whether that tradeoff is acceptable depends on your workload.

`LZ4` is a good compromise of speed and compression performance. While compression is slightly slower than `snappy`, the compression ratio is higher, resulting in smaller chunks in object storage.

## Use `chunk_target_size`

Using `chunk_target_size` instructs Loki to try to fill all chunks to a target _compressed_ size of 1.5MB. These larger chunks are more efficient for Loki to process.

Other configuration variables affect how full a chunk can get. Loki has a default `max_chunk_age` of 2h and `chunk_idle_period` of 30m to limit the amount of memory used as well as the exposure of lost logs if the process crashes.

Depending on the compression used (for example, `snappy`, which has less compressibility but faster performance than the `gzip` default), you need 5-10x or 7.5-10MB of raw log data to fill a 1.5MB chunk. Remembering that a chunk is per stream, the more streams you break up your log files into, the more chunks that sit in memory, and the higher likelihood they get flushed by hitting one of those timeouts mentioned above before they are filled.

Lots of small, unfilled chunks negatively affect Loki. The team is always working to improve this and may consider a compactor to improve this in some situations. But, in general, the guidance should stay about the same: try your best to fill chunks.

If you have an application that can log fast enough to fill these chunks quickly (much less than `max_chunk_age`), then it becomes more reasonable to use dynamic labels to break that up into separate streams.

## Use `-print-config-stderr` or `-log-config-reverse-order`

Loki has flags which will dump the entire config object to stderr or the log file when it starts.

`-print-config-stderr` works well when invoking Loki from the command line, as you can get a quick output of the entire Loki configuration.

`-log-config-reverse-order` is the flag Grafana runs Loki with in all our environments. The configuration entries are reversed, so that the order of the configuration reads correctly top to bottom when viewed in Grafana's Explore.

## Recommended production limits

```yaml
limits_config:
  # Rate limits
  ingestion_rate_strategy: global
  ingestion_rate_mb: 10
  ingestion_burst_size_mb: 20
  per_stream_rate_limit: 3MB
  per_stream_rate_limit_burst: 15MB
  
  # Stream limits
  max_global_streams_per_user: 10000
  max_streams_per_user: 0
  
  # Validation
  max_line_size: 256KB
  max_line_size_truncate: false
  max_label_name_length: 1024
  max_label_value_length: 2048
  max_label_names_per_series: 15
  
  # Time constraints
  reject_old_samples: true
  reject_old_samples_max_age: 168h  # 7 days
  creation_grace_period: 10m
```

## Recommended Ingester configuration

```yaml
ingester:
  # Chunk settings
  chunk_idle_period: 30m
  chunk_target_size: 1572864  # 1.5 MB
  chunk_encoding: snappy
  max_chunk_age: 2h
  
  # WAL settings
  wal:
    enabled: true
    dir: /loki/wal
    checkpoint_duration: 5m
    flush_on_shutdown: true
```

# Understanding Loki configuration limits

Loki implements configuration limits to maintain system stability, ensure fair resource distribution, and protect performance across all users. You can adjust many limits for specific use cases. Other limits are hard boundaries that you should not exceed, to prevent performance degradation and system instability.

This topic explains the critical configuration limits in Loki, why they exist, and the consequences of exceeding them.

## Why limits exist

Configuration limits in Loki serve multiple essential purposes:

* **System stability:** Limits prevent individual queries or ingestion patterns from exhausting system resources. Without limits, a single tenant or misconfigured application could consume all available memory, CPU, or I/O (input/output) capacity. This can cause cascading failures across the entire system.

* **Performance protection:** Loki is optimized for specific usage patterns. Limits ensure that workloads stay within the parameters where Loki performs efficiently. Exceeding these limits often results in exponential performance degradation rather than linear scaling.

* **Fair resource allocation:** In multi-tenant environments, limits ensure that no single tenant can monopolize shared resources. Query workers, ingester memory, and storage I/O must be distributed fairly across all users.

* **Operational predictability:** Limits allow operators to plan capacity and scale infrastructure appropriately. When operators understand the boundaries of expected workloads, they can provision resources that meet service level agreement (SLA) requirements.

Exceeding a limit doesn't just affect the tenant who exceeded it. It can also impact overall system performance and stability, often affecting all tenants in a shared environment.

## Default ingest limits

The following limits apply when you push logs to Loki. These are the default values in the Loki open source release. If a request exceeds one of these limits, Loki rejects the affected log lines or streams and returns an error.

| Limit | Default value | What happens when you exceed it |
| --- | --- | --- |
| Maximum active streams per tenant (`max_global_streams_per_user`) | 5,000 | New streams are rejected (`stream_limit`) until existing streams age out. |
| Ingestion rate (`ingestion_rate_mb`) | 4 MB per second, per tenant | Requests are rejected and can be retried (`rate_limited`). |
| Ingestion burst size (`ingestion_burst_size_mb`) | 6 MB | Requests that exceed the burst allowance are rejected until the rate limit bucket refills. |
| Per-stream rate limit (`per_stream_rate_limit`) | 3 MB per second | The stream is rate limited (`per_stream_rate_limit`). |
| Per-stream burst limit (`per_stream_rate_limit_burst`) | 15 MB | Bursts that exceed this size are rejected. |
| Maximum log line size (`max_line_size`) | 256 KB | Log lines that exceed the limit are discarded (`line_too_long`), unless you configure Loki to truncate them instead. |
| Maximum labels per stream (`max_label_names_per_series`) | 15 | Streams that exceed the limit are rejected (`max_label_names_per_series`). |
| Maximum length of a label name (`max_length_label_name`) | 1,024 bytes | Label names that exceed the limit are rejected (`label_name_too_long`). |
| Maximum length of a label value (`max_length_label_value`) | 2,048 bytes | Label values that exceed the limit are rejected (`label_value_too_long`). |
| Maximum structured metadata entries per log line (`max_structured_metadata_entries_count`) | 128 entries | Log lines with more entries are discarded. |
| Maximum structured metadata size per log line (`max_structured_metadata_size`) | 64 KB | Log lines that exceed the limit are discarded. |
| Maximum accepted sample age (`reject_old_samples_max_age`) | 1 week | Samples older than this age are rejected (`greater_than_max_sample_age`). |

Loki rejects samples based on their age only when the `reject_old_samples` setting is `true`, which is the default. If you set `reject_old_samples` to `false`, Loki ignores `reject_old_samples_max_age`.

Ingestion rate and maximum active streams aren't fixed for every deployment. Many operators raise these limits for larger deployments. For guidance on how far you can safely raise them, see [Limits we strongly recommend not exceeding](#limits-we-strongly-recommend-not-exceeding).

## Default query limits

The following limits apply when you query logs in Loki. These are the default values in the Loki open source release.

| Limit | Default value | What happens when you exceed it |
| --- | --- | --- |
| Query timeout (`query_timeout`) | 1 minute | The query is terminated before it finishes. |
| Maximum log entries returned per query (`max_entries_limit_per_query`) | 5,000 | Queries that request more entries than this limit are rejected. |
| Maximum unique series returned by a metric query (`max_query_series`) | 500 | The query is rejected. |
| Maximum query parallelism (`max_query_parallelism`) | 32 | Additional subqueries wait in a queue until a query worker is free. |
| Maximum query time range (`max_query_length`, `store.max-query-length`) | 721 hours (30 days and 1 hour) | The query is rejected. |
| Maximum chunks fetched per query (`store.query-chunk-limit`) | 2,000,000 | The query is rejected. |
| Maximum stream matchers per query (`max_streams_matcher_per_query`) | 1,000 | The query is rejected. |
| Maximum concurrent tail requests (`max_concurrent_tail_requests`) | 10 | Additional tail requests are rejected. |

## Limits we strongly recommend not exceeding

Some limits can technically be raised well beyond their defaults, but doing so puts your Loki cluster at risk. This section explains which limits fall into that category, why they exist, and what to do instead of raising them further.

### ingestion_rate_mb

- **Default**: 4 MB per second
- **Example large deployment value**: 100 MB per second (approximately 260 TB of data per month)
- **Recommended maximum for large deployments**: up to 250 MB per second

This limit controls the per-tenant ingestion rate in megabytes per second.

**Why this limit matters**:

Ingestion rate limits protect against cascading failures:

- Sudden traffic spikes can overwhelm ingesters before your infrastructure can scale to meet demand.
- Unbounded ingestion can exhaust ingester memory.
- Unbounded ingestion can exhaust distributor memory.
- Network and storage I/O can become bottlenecks.

Rate limits provide a safety mechanism that allows the system to scale gracefully, rather than failing all at once.

**Consequences of not setting rate limits properly**:

- Cascading failure of distributors and ingesters, leading to an outage of the entire write path and the recent-data read path.
- Ingesters overwhelmed to the point that their write-ahead logs (WALs) run out of disk space, causing slow WAL replay and difficult recovery.
- Object storage overwhelmed by excessive requests.

**What to do instead**:

- Always set rate limits for every tenant.
- Implement exponential backoff in your log shipping clients.
- Monitor ingestion patterns and adjust limits proactively.
- Scale infrastructure before increasing limits significantly.

### max_global_streams_per_user

- **Default**: 5,000
- **Recommended maximum for large deployments**: 80,000
- **Hard limit**: approximately 200,000 (performance issues become unavoidable beyond this point)

This limit controls the maximum number of active streams a single tenant can have across all ingesters. Each unique combination of labels creates a separate stream.

**Why you should not exceed 200,000 streams**:

Streams consume resources throughout the system:

- **Ingester memory**: Each stream maintains in-memory chunks.
- **Index entries**: Each stream creates entries in the index.
- **Object storage operations**: Each stream generates flush operations.
- **Query overhead**: More streams means more chunks to scan during queries.

Beyond approximately 200,000 streams, these costs become prohibitive regardless of infrastructure size.

**Consequences of exceeding**:

- Ingester memory exhaustion and crashes.
- Extremely slow query performance.
- Index query timeouts.
- Excessive object storage operations and costs.
- Inability to meet performance SLAs.
- Potential data loss during ingester failures.

**What to do instead**:

- Audit your label usage to identify high-cardinality labels.
- Use static labels for source identification only.
- Move high-cardinality data to structured metadata.
- Calculate your expected stream count: `streams = label1_values × label2_values × ...`
- Design for fewer than 80,000 streams.

### max_label_names_per_series

- **Default**: 15
- **Recommended maximum**: 15 (do not exceed)

This limit controls the maximum number of labels you can attach to a single log stream.

**Why you should not exceed 15 labels**:

You should be able to identify and categorize your logs with fewer than 15 labels. More labels increase the likelihood of higher cardinality and cause logs to fragment into too many streams and chunks. Even labels with a small, fixed set of values still require space in the index, and each additional label makes the index larger and can slow down index operations. Always use as few labels as possible.

Having more than 15 labels per stream usually indicates a labeling problem, not a limit you need to raise. Loki is designed for a small set of labels that describe the log source, such as `cluster`, `namespace`, or `job`. Labels aren't meant for storing high-cardinality metadata.

**Consequences of exceeding**:

- Increased memory usage on ingesters.
- A larger index, which leads to slower index operations and slower queries.

**What to do instead**:

- Use labels only for low-cardinality source identification.
- Parse high-cardinality fields at query time using LogQL parsers.
- Store high-cardinality metadata in [structured metadata](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/structured-metadata/) instead of labels.

### max_line_size

- **Default**: 256 KB
- **Maximum recommended**: 256 KB (do not exceed)

This limit controls the maximum size, in bytes, of a single log line during ingestion.

**Why you should not exceed 256 KB**:

A limit on log line size is fundamental to Loki's design. Increasing this limit can have several unintended consequences that make it hard to run a stable Loki cluster:

- Chunks are expected to be a few MB at most. If a log line is larger than this, it can create very large chunks, which hurts query performance because a chunk is the unit of parallelism and is expected to stay around 2 MB or smaller.
- Queriers and query frontends become much more likely to run out of memory (OOM).
- Increasing this limit also increases the risk of exhausting the gRPC message size limit, which can affect the performance of other tenants.

**What to do instead**:

- Structure your applications to produce reasonably sized log lines.
- Avoid embedding large payloads (such as base64-encoded data or full HTTP bodies) in logs.
- Log references to external data instead of the data itself.
- Split multi-line events into separate log entries.
- Set `max_line_size_truncate` to truncate large log lines instead of dropping them. Note that truncation can break JSON parsing of large log lines.

### Out-of-order ingestion window

- **Default**: 1 hour (half of `max_chunk_age`, which defaults to 2 hours)
- **Adjustable**: No, not on a per-tenant basis

The out-of-order ingestion window controls how far back in time, relative to the newest entry already received for a stream, Loki accepts a new log entry. Loki calculates this window as `max_chunk_age / 2`, so with the default 2-hour `max_chunk_age`, Loki accepts entries up to 1 hour older than the current newest entry in each stream.

This window isn't a per-tenant override. It's a cluster-wide setting, because it derives from `max_chunk_age`, which affects chunk flushing behavior across the whole deployment.

**Why this limit exists**:

The reasons users hit this limit vary widely: misconfiguration, re-ingestion of old data, or logs from temporarily disconnected sources such as vehicles or IoT devices. Loki needs a bounded window so that ingesters know when a chunk is complete and safe to flush.

**What to do for historical data**:

If you need to ingest logs older than the out-of-order window allows, you have two options:

- Temporarily increase `reject_old_samples_max_age` during migrations or backfills, so Loki doesn't reject the old samples outright.
- Enable `shard_streams.time_sharding_enabled` for the affected tenant. This setting splits incoming streams by injecting a synthetic `__time_shard__` label, so that no stream is ever longer than `max_chunk_age / 2`. This lets Loki accept very old logs, because the new, shorter streams are never "too far behind" by design.

Time sharding has some limitations:

- Newly ingested old logs aren't queryable for a few hours, until Loki flushes them to object storage.
- Very old logs can still be rejected with a `greater_than_max_sample_age` error if they fall outside `reject_old_samples_max_age`.
- Recording rules don't work against the old data.
- Time sharding creates new time series (up to 24 per day), so check whether this affects your active streams limit.

Disable time sharding after your backfill completes.

### per_stream_rate_limit

- **Default**: 3 MB per second sustained, 15 MB burst
- **Recommended maximum sustained rate**: 5 MB per second

Individual streams have their own rate limits. This prevents a single high-volume stream from overwhelming the ingesters it is distributed to, based on the replication factor.

**Why you should not exceed 5 MB per second per stream**:

Streams are distributed to a fixed number of ingesters (typically 3, for replication). If a single stream sends data faster than those ingesters can process it:

- Ingester memory queues grow.
- Chunk flush operations lag behind incoming data.
- Memory usage spikes on the affected ingesters.
- The affected ingesters risk running out of memory (OOM).

Unlike the global rate limit, you cannot solve per-stream limit problems by adding more ingesters, because each stream is always handled by the same set of ingesters.

**Consequences of exceeding**:

- Memory buildup on specific ingesters.
- Increased flush latency.
- Potential out-of-memory (OOM) failures on affected ingesters.
- Data loss if an ingester crashes.
- Uneven load distribution.

**What to do instead**:

- First, try adjusting automatic stream sharding by lowering `desired_rate` (not below 128 KB per second).
- If that isn't enough, increase `per_stream_rate_limit` and `per_stream_rate_limit_burst`. This might require more memory headroom on your ingesters. Increase the burst limit more aggressively than the sustained rate.

For example, to handle "bursty" streams, you could raise the sustained rate a little and the burst limit a lot:

- `per_stream_rate_limit: 5MB/s`
- `per_stream_rate_limit_burst: 30MB/s`

This gives each stream a bucket that holds 30 MB. A 30 MB push drains the entire bucket, which then refills at 5 MB per second up to the 30 MB cap. You could raise the burst limit further, to 60 MB, to handle spikier streams, but avoid raising the sustained rate too far, since that keeps overall memory use predictable. Automatic stream sharding also continuously works to keep stream volumes near `desired_rate`, but it can't always react fast enough to very sudden changes.

### query_timeout

- **Default**: 1 minute
- **Recommended maximum**: 5 minutes (do not exceed)

The query timeout controls how long a query can run before Loki terminates it. This limit exists because Loki is designed as a synchronous query engine, not for very long-running queries.

**Why you should not exceed 5 minutes**:

Loki was not engineered for very long-running query operations. The way Loki queries work, each querier runs a fixed number of worker routines. Query frontends and schedulers split a query by time range or by label set and enqueue each resulting subquery, and a single worker routine executes each one. All tenants share this pool of query workers.

Raising the timeout for one tenant lets that tenant reserve query workers for longer, which reduces the query capacity available to every other tenant. In multi-tenant or large multi-user environments, this can make it difficult to maintain system stability.

**Consequences of exceeding**:

- Reduced query capacity for all tenants.
- Increased query queue wait times.
- Potential denial of service to other users.
- Unpredictable query performance.

**What to do instead**: Optimize your queries to complete faster. Use smaller time ranges, more specific labels, and filters applied as early as possible in the query.

## Understanding limit violations

When you exceed a limit, Loki provides metrics and errors to help you understand what happened.

### Monitoring metrics

- **`loki_discarded_samples_total`**: Counts log lines discarded, with a `reason` label indicating which limit was exceeded.
- **`loki_discarded_bytes_total`**: Measures bytes discarded, by reason.
- **`loki_ingester_memory_streams`**: Current number of active streams in memory, per tenant.

### Common error reasons

- **`rate_limited`**: Exceeded `ingestion_rate_mb`.
- **`per_stream_rate_limit`**: An individual stream exceeded its rate limit.
- **`stream_limit`**: Exceeded `max_global_streams_per_user`.
- **`line_too_long`**: A log line exceeded `max_line_size`.
- **`max_label_names_per_series`**: Too many labels on a stream.
- **`label_name_too_long`**: A label name exceeded `max_length_label_name`.
- **`label_value_too_long`**: A label value exceeded `max_length_label_value`.
- **`too_far_behind`**: A log entry fell outside the out-of-order ingestion window.
- **`greater_than_max_sample_age`**: A log entry is older than `reject_old_samples_max_age`.

## Best practices for limits

To work effectively within Loki's limits:

1. **Design for low cardinality**: Use 5 to 10 labels at most, with static values.
2. **Keep logs reasonably sized**: Target 1 to 10 KB per log line.
3. **Forward logs promptly**: Aim for sub-second delivery latency.
4. **Monitor your usage**: Set up alerts on discarded-sample metrics.
5. **Scale infrastructure first**: Increase resources before you increase limits.
6. **Use alternatives**: Use structured metadata and query-time parsing instead of more labels.
7. **Plan capacity**: Calculate your expected stream count before deployment.
8. **Test at scale**: Validate your label design with production-like data volumes.

## Next steps

To change any of these limits for your own deployment, see the `limits_config` block in the [Configuration reference](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/), and review the related topics below before you raise a limit.

## Related documentation

- [Configure Loki](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/bp-configure/) - Practical guide to configuring Loki
- [Rate limits and validation](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/request-validation-rate-limits/) - Detailed error handling
- [Understanding cardinality](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/cardinality/) - How cardinality affects performance
- [Labels best practices](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/bp-labels/) - Guide to label design
- [Structured metadata](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/structured-metadata/) - Alternative to high-cardinality labels

# Storage

Unlike other logging systems, Grafana Loki is built around the idea of only indexing
metadata about your logs: labels (just like Prometheus labels). Log data itself
is then compressed and stored in chunks in object stores such as S3 or GCS, or
even locally on the filesystem. A small index and highly compressed chunks
simplifies the operation and significantly lowers the cost of Loki.

Loki 2.8 introduced TSDB as a new mode for the Single Store and is now the recommended way to persist data in Loki. This type only requires one store, the object store, for both the index and chunks.
More detailed information about TSDB can be found under the [manage section](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/tsdb/).

## Single Store TSDB

Single Store refers to using object storage as the storage medium for both the Loki index as well as its data ("chunks"). There is one supported mode:

Starting in Loki 2.8, the [TSDB index store](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/tsdb/) improves query performance, reduces TCO and has the same feature parity as the deprecated "boltdb-shipper". TSDB is the recommended index store for Loki 2.8 and newer.

### Supported storage backends

See [Object Storage](#object-storage) for supported backends.

## Chunk storage

### File system

The file system is the simplest backend for chunks, although it's also susceptible to data loss as it's unreplicated. This is common for single binary deployments though, as well as for those trying out loki or doing local development on the project. It is similar in concept to many Prometheus deployments where a single Prometheus is responsible for monitoring a fleet.

### Object storage

#### Google Cloud Storage (GCS)

GCS is a hosted object store offered by Google. It is a good candidate for a managed object store, especially when you're already running on GCP, and is production safe.

#### Amazon Simple Storage Storage (S3)

S3 is AWS's hosted object store. It is a good candidate for a managed object store, especially when you're already running on AWS, and is production safe.

#### Azure Blob Storage

Blob Storage is Microsoft Azure's hosted object store. It is a good candidate for a managed object store, especially when you're already running on Azure, and is production safe.
You can authenticate Blob Storage access by using a storage account name and key or by using a Service Principal.

#### IBM Cloud Object Storage (COS)

[COS](https://www.ibm.com/cloud/object-storage) is IBM Cloud hosted object store. It is a good candidate for a managed object store, especially when you're already running on IBM Cloud, and is production safe.

#### Baidu Object Storage (BOS)

[BOS](https://intl.cloud.baidu.com/product/bos.html) is the Baidu Cloud hosted object storage.

#### Alibaba Object Storage Service (OSS)

[OSS](https://www.alibabacloud.com/product/object-storage-service) is the Alibaba Cloud hosted object storage.

#### Other notable mentions

You may use any substitutable services, such as those that implement the S3 API like [MinIO](https://min.io/).

## Schema Config

Loki aims to be backwards compatible and over the course of its development has had many internal changes that facilitate better and more efficient storage/querying. Loki allows incrementally upgrading to these new storage _schemas_ and can query across them transparently. This makes upgrading a breeze.

For a more detailed reference on schema configuration, including required values and the recommended settings for new installs, refer to [Storage schema](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/schema/).

For instance, this is what it looks like when migrating from BoltDB with v11 schema to TSDB with v13 schema starting 2023-07-01:

```yaml
schema_config:
  configs:
    - from: 2019-07-01
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h
    - from: 2023-07-01
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h
```

For all data ingested before 2023-07-01, Loki used BoltDB with the v11 schema, and then switched after that point to the more effective TSDB with the v13 schema. This dramatically simplifies upgrading, ensuring it's simple to take advantage of new storage optimizations. These configs should be immutable for as long as you care about retention.

## Upgrading Schemas

When a new schema is released and you want to gain the advantages it provides, you can! Loki can transparently query and merge data from across schema boundaries so there is no disruption of service and upgrading is easy.

First, you'll want to create a new [period_config](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#period_config) entry in your [schema_config](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#schema_config). The important thing to remember here is to set this at some point in the _future_ and then roll out the config file changes to Loki. This allows the table manager to create the required table in advance of writes and ensures that existing data isn't queried as if it adheres to the new schema.

As an example, let's say it's 2023-07-14 and you want to start using the `v13` schema on the 20th:

```yaml
schema_config:
  configs:
    - from: 2019-07-14
      store: tsdb
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h
    - from: 2023-07-20
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h
```

It's that easy; you just created a new entry starting on the 20th.

## Retention

Loki manages retention through the Compactor when using TSDB. When retention is enabled, the Compactor identifies data that falls outside of the configured retention period, removes the corresponding index entries, and deletes the underlying chunk objects asynchronously.

For object storage backends (S3, GCS, Azure Blob) Loki no longer relies solely on external time to live (TTL) or bucket lifecycle rules; these may still be used as an additional safeguard, but Loki itself performs retention-driven deletion when configured.

When using the filesystem chunk store, Loki does not delete data based on disk usage or free-space conditions. Deletion is determined only by the retention settings, and disk-full scenarios must be handled operationally outside of Loki.

Loki also supports targeted deletion at the tenant or stream level.

For more information, see the [retention configuration](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/retention/) documentation.

## Examples

{{< admonition type="note" >}}
Loki uses the Thanos-based object storage clients by default, because the `use_thanos_objstore` setting defaults to `true`. With this default, Loki reads the configuration under `storage_config.object_store` and ignores the legacy client sections such as `storage_config.aws` or `storage_config.gcs`.

Each example below shows the Thanos configuration first. The legacy configuration follows it for reference, because the legacy clients are deprecated. To keep using a legacy client, you must set `use_thanos_objstore: false`.

To convert an existing configuration to the new format, refer to [Migrate to Thanos storage clients](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/migrate/migrate-storage-clients/).
{{< /admonition >}}

### Single machine/local development (tsdb+filesystem)

[The repo contains a working example](https://github.com/grafana/loki/blob/main/cmd/loki/loki-local-config.yaml), you may want to checkout a tag of the repo to make sure you get a compatible example.

### GCP deployment (GCS Single Store)

Configuration using the [Thanos-based object store client](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/examples/thanos-storage-configs/#gcs-example):

```yaml
storage_config:
  use_thanos_objstore: true
  object_store:
    gcs:
      bucket_name: <BUCKET_NAME>
      service_account: |
        {
          "type": "service_account",
          ...
        }
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h # Can be increased for faster performance over longer query periods, uses more disk space

schema_config:
  configs:
    - from: 2020-07-01
      store: tsdb
      object_store: gcs
      schema: v13
      index:
        prefix: index_
        period: 24h
```

The same deployment using the deprecated GCS client:

```yaml
storage_config:
  use_thanos_objstore: false # required to use the deprecated client
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h
  gcs:
    bucket_name: <BUCKET_NAME>
    service_account: |
      {
        "type": "service_account",
        ...
      }

schema_config:
  configs:
    - from: 2020-07-01
      store: tsdb
      object_store: gcs
      schema: v13
      index:
        prefix: index_
        period: 24h
```

`service_account` should contain JSON from either a GCP Console `client_credentials.json` file or a GCP service account key. If this value is blank, most services will fall back to GCP's Application Default Credentials (ADC) strategy. For more information about ADC, refer to [How Application Default Credentials works](https://cloud.google.com/docs/authentication/application-default-credentials).

The [pre-defined `storage.objectUser` role](https://cloud.google.com/storage/docs/access-control/iam-roles) (or a custom role modeled after it) contains sufficient permissions for Loki to operate.

{{< admonition type="note" >}}
GCP recommends [Workload Identity Federation](https://cloud.google.com/iam/docs/workload-identity-federation) instead of a service account key.
{{< /admonition >}}

### AWS deployment (S3 Single Store)

Configuration using the [Thanos-based object store client](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/examples/thanos-storage-configs/#s3-example):

```yaml
storage_config:
  use_thanos_objstore: true
  object_store:
    s3:
      bucket_name: <BUCKET_NAME>
      # The endpoint is required. For AWS, use the regional S3 endpoint.
      endpoint: s3.<REGION>.amazonaws.com
      region: <REGION>
      # You can either declare the access key and secret in the config or
      # use environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY,
      # which will be picked up by the AWS SDK.
      access_key_id: <ACCESS_KEY_ID>
      secret_access_key: <SECRET_ACCESS_KEY>
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h         # Can be increased for faster performance over longer query periods, uses more disk space

schema_config:
  configs:
    - from: 2020-07-01
      store: tsdb
      object_store: s3
      schema: v13
      index:
        prefix: index_
        period: 24h
```

The Thanos-based client supports one bucket only. If you previously used `bucketnames` with several buckets, you must consolidate to a single bucket.

If you don't wish to hard-code S3 credentials, you can use an EC2 instance role instead. Leave `access_key_id` and `secret_access_key` unset. The client then looks for credentials in environment variables, in the AWS credentials file, and finally in the EC2 instance metadata:

```yaml
storage_config:
  use_thanos_objstore: true
  object_store:
    s3:
      bucket_name: <BUCKET_NAME>
      endpoint: s3.<REGION>.amazonaws.com
      region: <REGION>
```

The same deployment using the deprecated S3 client:

```yaml
storage_config:
  use_thanos_objstore: false # required to use the deprecated client
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h
  aws:
    s3: s3://<ACCESS_KEY>:<URI_ENCODED_SECRET_ACCESS_KEY>@<REGION>
    bucketnames: <BUCKET_1>,<BUCKET_2>

schema_config:
  configs:
    - from: 2020-07-01
      store: tsdb
      object_store: s3
      schema: v13
      index:
        prefix: index_
        period: 24h
```

To use an EC2 instance role with the deprecated client, change the `storage_config` section:

```yaml
storage_config:
  use_thanos_objstore: false
  aws:
    s3: s3://<REGION>
    bucketnames: <BUCKET_1>,<BUCKET_2>
```

The role should have a policy with the following permissions attached.

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "LokiStorage",
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::<ACCOUNT_ID>"
                ]
            },
            "Action": [
                "s3:ListBucket",
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::<BUCKET_NAME>",
                "arn:aws:s3:::<BUCKET_NAME>/*"
            ]
        }
    ]
}
```

**To setup an S3 bucket and an IAM role and policy:**

This guide assumes a provisioned EKS cluster.

1. Checkout the Loki repository and navigate to [production/terraform/modules/s3](https://github.com/grafana/loki/tree/main/production/terraform/modules/s3).

2. Initialize Terraform `terraform init`.

3. Export the AWS profile and region if not done so:

   ```bash
   export AWS_PROFILE=<AWS_PROFILE_NAME>
   export AWS_REGION=<EKS_CLUSTER_REGION>
   ```

4. Save the OIDC provider in an environment variable:

   ```bash
   oidc_provider=$(aws eks describe-cluster --name <EKS_CLUSTER_NAME> --query "cluster.identity.oidc.issuer" --output text | sed -e "s/^https:\/\///")
   ```

   See the [IAM OIDC provider guide](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html) for a guide for creating a provider.

5. Apply the Terraform module `terraform -var region="$AWS_REGION" -var cluster_name=<EKS_CLUSTER_NAME> -var oidc_id="$oidc_provider"`

   Note, the bucket name defaults to `loki-data` but can be changed via the
   `bucket_name` variable.

### Azure deployment (Azure Blob Storage Single Store)

#### Using account name and key

Configuration using the [Thanos-based object store client](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/examples/thanos-storage-configs/#azure-example):

```yaml
schema_config:
  configs:
    - from: "2020-12-11"
      index:
        period: 24h
        prefix: index_
      object_store: azure
      schema: v13
      store: tsdb
storage_config:
  use_thanos_objstore: true
  object_store:
    azure:
      # Your Azure storage account name
      account_name: <ACCOUNT_NAME>
      # See https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction#containers
      container_name: <CONTAINER_NAME>
      # For the account key, see https://docs.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal
      # If you leave the account key unset, Loki uses an Azure managed identity instead.
      account_key: <ACCOUNT_KEY>
      # Set this to use a user assigned managed identity. If you leave it empty,
      # Loki uses the system assigned identity.
      user_assigned_id: <USER_ASSIGNED_IDENTITY_ID>
      # Configure this if you use a private Azure cloud, such as Azure Stack Hub.
      # Loki composes the storage URL as https://account_name.endpoint_suffix/container_name/blob_name
      endpoint_suffix: <ENDPOINT_SUFFIX>
      # If `connection_string` is set, the `account_name` and `endpoint_suffix` values are not used.
      # Use this instead of `account_key` to authenticate with a SAS token, or to use the Azurite emulator.
      connection_string: <CONNECTION_STRING>
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h
```

The same deployment using the deprecated Azure Blob Storage client:

```yaml
schema_config:
  configs:
    - from: "2020-12-11"
      index:
        period: 24h
        prefix: index_
      object_store: azure
      schema: v13
      store: tsdb
storage_config:
  use_thanos_objstore: false # required to use the deprecated client
  azure:
    # Your Azure storage account name
    account_name: <ACCOUNT_NAME>
    # For the account-key, see docs: https://docs.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal
    account_key: <ACCOUNT_KEY>
    # See https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction#containers
    container_name: <CONTAINER_NAME>
    use_managed_identity: <TRUE|FALSE>
    # Providing a user assigned ID will override use_managed_identity
    user_assigned_id: <USER_ASSIGNED_IDENTITY_ID>
    request_timeout: 0
    # Configure this if you are using private azure cloud like azure stack hub and will use this endpoint suffix to compose container and blob storage URL. Ex: https://account_name.endpoint_suffix/container_name/blob_name
    endpoint_suffix: <ENDPOINT_SUFFIX>
    # If `connection_string` is set, the values of `account_name` and `endpoint_suffix` values will not be used. Use this method over `account_key` if you need to authenticate via a SAS token. Or if you use the Azurite emulator.
    connection_string: <CONNECTION_STRING>
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h
  filesystem:
    directory: /loki/chunks
```

#### Using a service principal

The [Thanos-based object store client](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/examples/thanos-storage-configs/#azure-example) has no service principal settings in `storage_config`. Instead, pass the credentials in the standard environment variables read by the [Azure Identity Client Module for Go](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity), which are `AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, and `AZURE_CLIENT_SECRET`. Leave `account_key` unset so that Loki uses those credentials. You must still set `account_name`, because Loki uses it to build the storage URL.

```yaml
schema_config:
  configs:
    - from: "2020-12-11"
      index:
        period: 24h
        prefix: index_
      object_store: azure
      schema: v13
      store: tsdb
storage_config:
  use_thanos_objstore: true
  object_store:
    azure:
      account_name: <ACCOUNT_NAME>
      container_name: <CONTAINER_NAME>
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h
```

The same deployment using the deprecated Azure Blob Storage client:

```yaml
schema_config:
  configs:
    - from: "2020-12-11"
      index:
        period: 24h
        prefix: index_
      object_store: azure
      schema: v13
      store: tsdb
storage_config:
  use_thanos_objstore: false # required to use the deprecated client
  azure:
    use_service_principal: true
    # Azure tenant ID used to authenticate through Azure OAuth
    tenant_id : <TENANT_ID>
    # Azure Service Principal ID
    client_id: <CLIENT_ID>
    # Azure Service Principal secret key
    client_secret: <CLIENT_SECRET>
    # See https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction#containers
    container_name: <CONTAINER_NAME>
    request_timeout: 0
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h
  filesystem:
    directory: /loki/chunks
```

### IBM Deployment (COS Single Store)

{{< admonition type="note" >}}
The Thanos-based object store client does not support IBM COS. Because `use_thanos_objstore` defaults to `true`, you must set it to `false` to use COS. If you leave the default in place, Loki fails to start with the error `unrecognized object_store type cos`.

For the list of backends that the Thanos-based client supports, refer to the [Thanos storage configuration reference](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#thanos_object_store_config).
{{< /admonition >}}

```yaml
schema_config:
  configs:
    - from: 2020-10-01
      index:
        period: 24h
        prefix: loki_index_
      object_store: cos
      schema: v13
      store: tsdb

storage_config:
  use_thanos_objstore: false # COS is not supported by the Thanos-based client
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
  cos:
    bucketnames: <BUCKET_1>, <BUCKET_2>
    endpoint: <ENDPOINT>
    api_key: <API_KEY_TO_AUTHENTICATE_WITH_COS>
    region: <REGION>
    service_instance_id: <COS_SERVICE_INSTANCE_ID>
    auth_endpoint: <IAM_ENDPOINT_FOR_AUTHENTICATION>
```

### On premise deployment (MinIO Single Store)

You configure MinIO by using the S3 settings, because MinIO implements the S3 API.

Configuration using the [Thanos-based object store client](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/examples/thanos-storage-configs/#minio-s3-compatible-example):

```yaml
storage_config:
  use_thanos_objstore: true
  object_store:
    s3:
      bucket_name: <BUCKET_NAME>
      # Use a fully qualified domain name (fqdn), like localhost, without a scheme.
      endpoint: <FQDN>:<PORT>
      access_key_id: <USERNAME>
      secret_access_key: <SECRET>
      insecure: true            # set to false if MinIO is served over https
      bucket_lookup_type: path  # MinIO's equivalent of s3forcepathstyle
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h         # Can be increased for faster performance over longer query periods, uses more disk space

schema_config:
  configs:
    - from: 2020-07-01
      store: tsdb
      object_store: s3
      schema: v13
      index:
        prefix: index_
        period: 24h
```

The same deployment using the deprecated S3 client:

```yaml
storage_config:
  use_thanos_objstore: false # required to use the deprecated client
  aws:
    # Note: use a fully qualified domain name (fqdn), like localhost.
    # full example: http://loki:supersecret@localhost.:9000
    s3: http(s)://<USERNAME>:<SECRET>@<FQDN>:<PORT>
    s3forcepathstyle: true
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h

schema_config:
  configs:
    - from: 2020-07-01
      store: tsdb
      object_store: s3
      schema: v13
      index:
        prefix: index_
        period: 24h
```

# Usage statistics

Loki sends anonymous usage statistics to Grafana Labs. This reporting is enabled by default and helps the Loki team understand aggregate usage patterns. Reports are sent to `https://stats.grafana.org/loki-usage-report` by default.

## What Loki collects

Loki reports aggregate operational metadata, including:

- An anonymous cluster identifier.
- Build and runtime metadata, such as version, OS, architecture, and Go version.
- Deployment metadata, such as the configured Loki target.
- Go runtime metrics, such as memory and goroutine counts.
- Aggregate product metrics, such as ingestion, query throughput, and chunk flush statistics.
- Storage and compactor configuration metadata, such as schema, storage backend type, and retention settings.

Loki does not report log payloads, label values, tenant identifiers, or query strings. You can view the code in [stats.go](https://github.com/grafana/loki/blob/main/pkg/analytics/stats.go)

## Disable usage statistics reporting

To disable reporting in the Loki config file, set `analytics.reporting_enabled` to `false`:

```yaml
analytics:
  reporting_enabled: false
```

To disable reporting with a CLI flag, start Loki with:

```sh
-reporting.enabled=false
```

To disable reporting with the Helm chart, set the following values:

```yaml
loki:
  analytics:
    reporting_enabled: false
```

For all analytics configuration options, refer to the [analytics configuration reference](../#analytics).

# Legacy configuration examples

These examples configure Loki's legacy, non-default storage clients. Since Loki 4.0, Loki uses the Thanos-based storage clients by default (the `use_thanos_objstore` setting defaults to `true`). For the current default storage client configuration, refer to [Configuration examples for using Thanos-based storage clients](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/examples/thanos-storage-configs/).

## 1-Local-Configuration-Example.yaml

```yaml

# This is a complete configuration to deploy Loki backed by the filesystem.
# The index will be shipped to the storage via tsdb-shipper.

auth_enabled: false

server:
  http_listen_port: 3100

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory
  replication_factor: 1
  path_prefix: /tmp/loki

schema_config:
  configs:
    - from: 2020-05-15
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: false # legacy storage client; see the Thanos storage examples for the current default
  filesystem:
    directory: /tmp/loki/chunks

```


## 2-S3-Cluster-Example.yaml

```yaml

# This is a complete configuration to deploy Loki backed by a s3-compatible API
# like MinIO for storage.
# Index files will be written locally at /loki/index and, eventually, will be shipped to the storage via tsdb-shipper.

auth_enabled: false

server:
  http_listen_port: 3100

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory
  replication_factor: 1
  path_prefix: /loki

schema_config:
  configs:
    - from: 2020-05-15
      store: tsdb
      object_store: s3
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: false # legacy storage client; see the Thanos storage examples for the current default
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
  aws:
    s3: s3://access_key:secret_access_key@custom_endpoint/bucket_name
    s3forcepathstyle: true

```


## 3-S3-Without-Credentials-Snippet.yaml

```yaml

# If you don't wish to hard-code S3 credentials you can also configure an EC2
# instance role by changing the `storage_config` section.

storage_config:
  use_thanos_objstore: false # legacy storage client; see the Thanos storage examples for the current default
  aws:
    s3: s3://region/bucket_name
      

```


## 4-GCS-Example.yaml

```yaml

# This is a complete configuration to deploy Loki backed by a GCS.
# Index files will be written locally at /loki/index and, eventually, will be shipped to the storage via tsdb-shipper.

auth_enabled: false

server:
  http_listen_port: 3100

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory
  replication_factor: 1
  path_prefix: /loki

schema_config:
  configs:
    - from: 2020-05-15
      store: tsdb
      object_store: gcs
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: false # legacy storage client; see the Thanos storage examples for the current default
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
  gcs:
    bucket_name: replace_by_your_bucked_name

```


## 5-BOS-Example.yaml

```yaml

# This is a partial configuration to deploy Loki backed by Baidu Object Storage (BOS).
# The index will be shipped to the storage via tsdb-shipper.
common:
  path_prefix: /tmp/loki

schema_config:
  configs:
    - from: 2020-05-15
      store: tsdb
      object_store: bos
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: false # legacy storage client; see the Thanos storage examples for the current default
  bos:
    bucket_name: bucket_name_1
    endpoint: bj.bcebos.com
    access_key_id: access_key_id
    secret_access_key: secret_access_key

```


## 6-Compactor-Snippet.yaml

```yaml

# This partial configuration sets the compactor to use S3 and run the compaction every 5 minutes.
# Downloaded index files for compaction are stored in /loki/compactor.

compactor:
  working_directory: /tmp/loki/compactor
  compaction_interval: 5m

```


## 7-Schema-Migration-Snippet.yaml

```yaml

common:
  path_prefix: /tmp/loki

schema_config:
  configs:
    # Starting from 2018-04-15 Loki should store indexes on BoltDB with the v11 schema
    # using daily periodic tables and chunks on filesystem.
    # The index tables will be prefixed with "index_".
    # NOTE: `store: boltdb-shipper` is deprecated and scheduled for removal in Loki 4.0.
    # This entry is kept only to illustrate migrating an existing boltdb-shipper period to tsdb.
    - from: "2018-04-15"
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        period: 24h
        prefix: index_

    # Starting from 2023-6-15 Loki should store indexes on TSDB with the v13 schema
    # using daily periodic tables and chunks on AWS S3.
    - from: "2023-06-15"
      store: tsdb
      object_store: s3
      schema: v13
      index:
        period: 24h
        prefix: index_

```


## 8-alibaba-cloud-storage-Snippet.yaml

```yaml

# This partial configuration uses Alibaba for chunk storage.
common:
  path_prefix: /tmp/loki

schema_config:
  configs:
    - from: 2020-05-15
      store: tsdb
      object_store: alibabacloud
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: false # legacy storage client; see the Thanos storage examples for the current default
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
  alibabacloud:
    bucket: <bucket>
    # Alibabacloud region name.
    # Required when signature_version=v4.
    # region: <region>
    endpoint: <endpoint>
    access_key_id: <access_key_id>
    secret_access_key: <secret_access_key>
    # Specify the RAM role name of the ECS instance. ECS RAM role authentication is used only 
    # when neither access_key_id nor secret_access_key is configured and requires signature_version=v4. 
    # If not set, the role name will be automatically retrieved from the ECS instance metadata.
    # ram_role_name: <ram_role_name>
    # The signature version to use for authenticating against OSS. Supported values are: v1, v4. 
    # ECS RAM role authentication requires signature_version=v4.
    # signature_version: <signature_version>

```


## 9-S3-With-SSE-KMS-Snippet.yaml

```yaml

# This partial configuration uses S3 for chunk storage and a KMS CMK for encryption.

storage_config:
  use_thanos_objstore: false # legacy storage client; see the Thanos storage examples for the current default
  aws:
    s3: s3://access_key:secret_access_key@region/bucket_name
    sse:
      type: SSE-KMS
      kms_key_id: 1234abcd-12ab-34cd-56ef-1234567890ab

```


## 10-Expanded-S3-Snippet.yaml

```yaml

# S3 configuration supports an expanded configuration.
# Either an `s3` endpoint URL can be used, or an expanded configuration can be used.

storage_config:
  use_thanos_objstore: false # legacy storage client; see the Thanos storage examples for the current default
  aws:
    bucketnames: bucket_name1, bucket_name2
    endpoint: s3.endpoint.com
    region: s3_region
    access_key_id: s3_access_key_id
    secret_access_key: s3_secret_access_key
    insecure: false
    http_config:
      idle_conn_timeout: 90s
      response_header_timeout: 0s
      insecure_skip_verify: false
    s3forcepathstyle: true
    

```


## 11-COS-HMAC-Example.yaml

```yaml

# This partial configuration uses IBM Cloud Object Storage (COS) for chunk storage. HMAC will be used for authenticating with COS.
common:
  path_prefix: /tmp/loki

schema_config:
  configs:
    - from: 2020-10-01
      store: tsdb
      object_store: cos
      schema: v13
      index:
        period: 24h
        prefix: index_

storage_config:
  use_thanos_objstore: false # COS is not yet supported by Thanos storage client
  cos:
    bucketnames: <bucket1, bucket2>
    endpoint: <endpoint>
    region: <region>
    access_key_id: <access_key_id>
    secret_access_key: <secret_access_key>

```


## 12-COS-APIKey-Example.yaml

```yaml

# This partial configuration uses IBM Cloud Object Storage (COS) for chunk storage. APIKey will be used for authenticating with COS.
common:
  path_prefix: /tmp/loki

schema_config:
  configs:
    - from: 2020-10-01
      store: tsdb
      object_store: cos
      schema: v13
      index:
        period: 24h
        prefix: index_

storage_config:
  use_thanos_objstore: false # COS is not yet supported by Thanos storage client
  cos:
    bucketnames: <bucket1, bucket2>
    endpoint: <endpoint>
    region: <region>
    api_key: <api_key_to_authenticate_with_cos>
    service_instance_id: <cos_service_instance_id>
    auth_endpoint: <iam_endpoint_for_authentication>

```


## 13-COS-Trusted-Profile-Example.yaml

```yaml

# This partial configuration uses IBM Cloud Object Storage (COS) for chunk storage.
# A trusted profile will be used for authenticating with COS. We can either pass
# the trusted profile name or trusted profile ID along with the compute resource token file.
# If we pass both trusted profile name and trusted profile ID it should be of
# the same trusted profile.
# In order to use trusted profile authentication we need to follow an additional step to create a trusted profile.
# For more details about creating a trusted profile, see https://cloud.ibm.com/docs/account?topic=account-create-trusted-profile&interface=ui.
common:
  path_prefix: /tmp/loki

schema_config:
  configs:
    - from: 2020-10-01
      store: tsdb
      object_store: cos
      schema: v13
      index:
        period: 24h
        prefix: index_

storage_config:
  use_thanos_objstore: false # COS is not yet supported by Thanos storage client
  cos:
    bucketnames: <bucket1, bucket2>
    endpoint: <endpoint>
    region: <region>
    auth_endpoint: <iam_endpoint_for_authentication>
    cr_token_file_path: <path_to_compute_resource_token>
    trusted_profile_name: <name_of_the_trusted_profile> # You can also use trusted_profile_id instead of trusted_profile_name

```


## 15-Memberlist-Ring-Snippet.yaml

```yaml

# This partial configuration uses memberlist for the ring.

common:
  ring:
    kvstore:
      store: memberlist
  replication_factor: 1
  path_prefix: /loki

memberlist:
  join_members:
    # You can use a headless k8s service for all distributor, ingester and querier components.
    - loki-gossip-ring.loki.svc.cluster.local:7946 # :7946 is the default memberlist port.

```


## 16-Azure-Account-Name-Example.yaml

```yaml

# This partial configuration uses Azure for chunk storage
storage_config:
  use_thanos_objstore: false # legacy storage client; see the Thanos storage examples for the current default
  azure:
    # For the account-key, see docs: https://docs.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal
    account_key: <azure_blob_access_key>
    account_name: <azure_account_name>
    container_name: <azure_storage_bucket_name>
    use_managed_identity: false
    # Providing a user assigned ID will override use_managed_identity
    #user_assigned_id: <user-assigned-identity-id>
    request_timeout: 0
    # Configure `endpoint_suffix` if you are using private azure cloud like azure stack hub and will use this endpoint suffix to compose container and blob storage URL. Ex: https://account_name.endpoint_suffix/container_name/blob_name
    #endpoint_suffix: <endpoint-suffix>
    # If `connection_string` is set, the `account_name` and `endpoint_suffix` values will not be used. Use this method over `account_key` if you need to authenticate via an SAS token. Or if you use the Azurite emulator.
    #connection_string: <connection-string>

```


## 17-Azure-Service-Principal-Example.yaml

```yaml

# This partial configuration uses Azure for chunk storage and a service principal for authentication
storage_config:
  use_thanos_objstore: false # legacy storage client; see the Thanos storage examples for the current default
  azure:
    use_service_principal: true
    # Azure tenant ID used to authenticate through Azure OAuth
    tenant_id: <tenant-id>
    # Azure Service Principal ID
    client_id: <client-id>
    # Azure Service Principal secret key
    client_secret: <client-secret>
    # See https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction#containers
    container_name: <azure_storage_bucket_name>
    request_timeout: 0

```


## 18-Thanos-GCS-Example.yaml

```yaml

# This is a complete configuration to deploy Loki backed by GCS, using the
# Thanos-based object store client (storage_config.object_store).
# Index files will be written locally at /loki/index and, eventually, will be shipped to the storage via tsdb-shipper.

auth_enabled: false

server:
  http_listen_port: 3100

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory
  replication_factor: 1
  path_prefix: /loki

schema_config:
  configs:
    - from: 2020-05-15
      store: tsdb
      object_store: gcs
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: true
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
  object_store:
    gcs:
      bucket_name: <BUCKET_NAME>

```


## 19-Thanos-S3-Example.yaml

```yaml

# This is a complete configuration to deploy Loki backed by AWS S3,
# using the Thanos-based object store client (storage_config.object_store).
# Index files will be written locally at /loki/index and, eventually, will be shipped to the storage via tsdb-shipper.

auth_enabled: false

server:
  http_listen_port: 3100

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory
  replication_factor: 1
  path_prefix: /loki

schema_config:
  configs:
    - from: 2020-05-15
      store: tsdb
      object_store: s3
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: true
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
  object_store:
    s3:
      bucket_name: <BUCKET_NAME>
      # The endpoint is required. For AWS, use the regional S3 endpoint.
      endpoint: s3.<REGION>.amazonaws.com
      region: <REGION>
      # Leave the access key and secret unset to use an EC2 instance role, or
      # the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables.
      access_key_id: <ACCESS_KEY_ID>
      secret_access_key: <SECRET_ACCESS_KEY>

```


## 20-Thanos-Azure-Example.yaml

```yaml

# This is a complete configuration to deploy Loki backed by Azure Blob Storage,
# using the Thanos-based object store client (storage_config.object_store).
# Index files will be written locally at /loki/index and, eventually, will be shipped to the storage via tsdb-shipper.

auth_enabled: false

server:
  http_listen_port: 3100

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory
  replication_factor: 1
  path_prefix: /loki

schema_config:
  configs:
    - from: 2020-05-15
      store: tsdb
      object_store: azure
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: true
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
  object_store:
    azure:
      account_name: <ACCOUNT_NAME>
      account_key: <ACCOUNT_KEY>
      container_name: <CONTAINER_NAME>

```


## 21-Thanos-MinIO-Example.yaml

```yaml

# This is a complete configuration to deploy Loki backed by a MinIO cluster,
# using the Thanos-based object store client (storage_config.object_store).
# Index files will be written locally at /loki/index and, eventually, will be shipped to the storage via tsdb-shipper.

auth_enabled: false

server:
  http_listen_port: 3100

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory
  replication_factor: 1
  path_prefix: /loki

schema_config:
  configs:
    - from: 2020-05-15
      store: tsdb
      object_store: s3
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: true
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
  object_store:
    s3:
      bucket_name: <BUCKET_NAME>
      # Use a fully qualified domain name (fqdn), like localhost, without a scheme.
      endpoint: <FQDN>:<PORT>
      access_key_id: <ACCESS_KEY_ID>
      secret_access_key: <SECRET_ACCESS_KEY>
      insecure: true
      bucket_lookup_type: path

```

# Query frontend example

## Before you begin

This is a general-purpose example. It contains placeholder variables in the format `<variable_name>`. Replace them with values for your environment.

## Use case

Many users start by running Grafana Loki as a single binary to simplify deployment. As usage grows and the deployment becomes more production-ready, performance bottlenecks can appear on the read path. Queries that ran quickly on small data sets may slow down or fail on larger ones. Adding more queriers can help in some cases. However, when a single query is too large for one querier to handle, you need the query frontend.

### Parallelization

One of the most important functions of the query frontend is the ability to split larger queries into smaller ones, execute them in parallel, and combine the results. The `querier.split-queries-by-interval` flag (or the YAML config `limits_config.split_queries_by_interval`) controls how the frontend splits queries. For example, with this value set to `1h`, the frontend splits a 24-hour query into 24 one-hour queries, distributes them to the queriers, and collects the results. This approach has two benefits in production environments: it lets you run larger queries through aggregation, and it distributes work evenly across queriers.

## Kubernetes Deployment

### ConfigMap

Use this ConfigMap to enable query parallelization and caching with the query-frontend component.

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: loki-frontend
  namespace: <namespace>
data:
  config.yaml: |
    # Disable the requirement that every request to Loki has a
    # X-Scope-OrgID header. `fake` will be substituted in instead.
    auth_enabled: false

    server:
      http_listen_port: 3100

    query_range:
      # make queries more cache-able by aligning them with their step intervals
      align_queries_with_step: true
      max_retries: 5
      cache_results: true

      results_cache:
        cache:
          # Use the embedded cache
          embedded_cache:
            enabled: true
            max_size_mb: 100
            ttl: 24h

    limits_config:
      max_cache_freshness_per_query: '10m'
      # parallelize queries in 15min intervals
      split_queries_by_interval: 15m

    frontend:
      log_queries_longer_than: 5s
      downstream_url: http://querier.<namespace>.svc.cluster.local:3100
      compress_responses: true
```

### Frontend Service

```yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    name: query-frontend
  name: query-frontend
  namespace: <namespace>
spec:
  ports:
    - name: query-frontend-http
      port: 3100
      protocol: TCP
      targetPort: 3100
  selector:
    name: query-frontend
  sessionAffinity: None
  clusterIP: None
  publishNotReadyAddresses: true
```

### Frontend Deployment

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    name: query-frontend
  name: query-frontend
  namespace: <namespace>
spec:
  minReadySeconds: 10
  replicas: 2
  selector:
    matchLabels:
      name: query-frontend
  template:
    metadata:
      labels:
        name: query-frontend
    spec:
      containers:
        - args:
            - -config.file=/etc/loki/config.yaml
            - -log.level=debug
            - -target=query-frontend
          image: grafana/loki:<VERSION>
          imagePullPolicy: Always
          name: query-frontend
          ports:
            - containerPort: 3100
              name: http
              protocol: TCP
          resources:
            limits:
              memory: 1200Mi
            requests:
              cpu: '2'
              memory: 600Mi
          volumeMounts:
            - mountPath: /etc/loki
              name: loki-frontend
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      volumes:
        - configMap:
            defaultMode: 420
            name: loki-frontend
          name: loki-frontend
```

### Grafana

After you deploy these resources, point your Grafana data source to the new frontend service. The service is available within the cluster at `http://query-frontend.<namespace>.svc.cluster.local:3100`.

### gRPC mode (pull model)

The query frontend operates in one of two modes:

- **Push mode:** Specify `--frontend.downstream-url` or its YAML equivalent `frontend.downstream_url`. The frontend proxies requests over HTTP to the specified URL.
- **Pull mode:** If you do not set `--frontend.downstream-url` or its YAML equivalent `frontend.downstream_url`, the query frontend operates as a pull service. The frontend creates per-tenant queues. Downstream queriers pull queries from these queues using gRPC. To use pull mode, configure queriers with `-querier.frontend-address` or its YAML equivalent `frontend_worker.frontend_address`.

  Set `clusterIP: None` on the query frontend pull service.
  This setting causes DNS to resolve to individual query frontend pod IP addresses instead of the service IP.

  Set `publishNotReadyAddresses: true` on the query frontend pull service.
  This setting prevents a race condition where a querier needs the query frontend address before the frontend is ready.

# Configuration examples for using Thanos-based storage clients

Use these examples as a starting point for configuring [Thanos-based object storage clients](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#thanos_object_store_config) in Grafana Loki. Each example is a complete configuration you can adapt and run, not just a snippet.

## GCS example

```yaml
auth_enabled: false

server:
  http_listen_port: 3100

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory
  replication_factor: 1
  path_prefix: /loki

schema_config:
  configs:
    - from: 2020-07-01
      store: tsdb
      object_store: gcs
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: true
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h # Can be increased for faster performance over longer query periods, uses more disk space
  object_store:
    gcs:
      bucket_name: <BUCKET_NAME>

      # JSON either from a Google Developers Console client_credentials.json file,
      # or a Google Developers service account key. Needs to be valid JSON, not a
      # filesystem path. If empty, fallback to Google default logic:
      # 1. A JSON file whose path is specified by the GOOGLE_APPLICATION_CREDENTIALS
      # environment variable. For workload identity federation, refer to
      # https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation
      # on how to generate the JSON configuration file for on-prem/non-Google cloud
      # platforms.
      # 2. A JSON file in a location known to the gcloud command-line tool:
      # $HOME/.config/gcloud/application_default_credentials.json.
      # 3. On Google Compute Engine it fetches credentials from the metadata server.
      service_account:  |-
        {
            "type": "service_account",
            "project_id": "project",
            "private_key_id": "abcdefghijklmnopqrstuvwxyz12345678906666",
            "private_key": "-----BEGIN PRIVATE KEY-----\...\n-----END PRIVATE KEY-----\n",
            "client_email": "project@example.iam.gserviceaccount.com",
            "client_id": "123456789012345678901",
            "auth_uri": "https://accounts.google.com/o/oauth2/auth",
            "token_uri": "https://oauth2.googleapis.com/token",
            "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
            "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/project@example.iam.gserviceaccount.com"
        }
```

## S3 example

```yaml
auth_enabled: false

server:
  http_listen_port: 3100

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory
  replication_factor: 1
  path_prefix: /loki

schema_config:
  configs:
    - from: 2020-07-01
      store: tsdb
      object_store: s3
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: true
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h
  object_store:
    s3:
      bucket_name: <BUCKET_NAME>
      # The endpoint is required. For AWS, use the regional S3 endpoint.
      endpoint: s3.<REGION>.amazonaws.com
      region: <REGION>
      # You can either declare the access key and secret in the config or
      # use environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY which will be picked up by the AWS SDK.
      access_key_id: <ACCESS_KEY_ID>
      secret_access_key: <SECRET_ACCESS_KEY>
```

## Azure example

```yaml
auth_enabled: false

server:
  http_listen_port: 3100

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory
  replication_factor: 1
  path_prefix: /loki

schema_config:
  configs:
    - from: 2020-07-01
      store: tsdb
      object_store: azure
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: true
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h
  object_store:
    azure:
      account_name: <ACCOUNT_NAME>
      account_key: ${SECRET_ACCESS_KEY} # loki expands environment variables
      container_name: <CONTAINER_NAME>
```

## MinIO (S3-compatible) example

MinIO and other S3-compatible object stores use the same `object_store.s3` client as AWS S3. Set `bucket_lookup_type: path`, which is the equivalent of the deprecated `s3forcepathstyle` option. Set `insecure: true` if MinIO is served over plain HTTP.

```yaml
auth_enabled: false

server:
  http_listen_port: 3100

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory
  replication_factor: 1
  path_prefix: /loki

schema_config:
  configs:
    - from: 2020-07-01
      store: tsdb
      object_store: s3
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: true
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
    cache_ttl: 24h
  object_store:
    s3:
      bucket_name: <BUCKET_NAME>
      # Use a fully qualified domain name (fqdn), like localhost, without a scheme.
      endpoint: <FQDN>:<PORT>
      access_key_id: <ACCESS_KEY_ID>
      secret_access_key: <SECRET_ACCESS_KEY>
      insecure: true
      bucket_lookup_type: path
```

## Filesystem example

```yaml
auth_enabled: false

server:
  http_listen_port: 3100

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory
  replication_factor: 1
  path_prefix: /tmp/loki

schema_config:
  configs:
    - from: 2020-07-01
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

storage_config:
  use_thanos_objstore: true
  object_store:
    filesystem:
      dir: /tmp/loki/chunks
```

# Get Started

# Loki architecture

Grafana Loki has a microservices-based architecture and is designed to run as a horizontally scalable, distributed system.
The system has multiple components that can run separately and in parallel. The
Grafana Loki design compiles the code for all components into a single binary or Docker image.
The `-target` command-line flag controls which component(s) that binary will behave as.

To get started easily, run Grafana Loki in "single binary" mode with all components running simultaneously in one process, or in "simple scalable deployment" mode, which groups components into read, write, and backend parts.

Grafana Loki is designed to easily redeploy a cluster under a different mode as your needs change, with no configuration changes or minimal configuration changes.

For more information, refer to [Deployment modes](../deployment-modes/) and [Components](../components/).

![Loki components](../loki_architecture_components.svg "Loki components")

## Storage

Loki stores all data in a single object storage backend, such as Amazon Simple Storage Service (S3), Google Cloud Storage (GCS), Azure Blob Storage, among others.
This mode uses an adapter called **index shipper** (or short **shipper**) to store index (TSDB) files the same way we store chunk files in object storage.
This mode of operation became generally available with Loki 2.0 and is fast, cost-effective, and simple. It is where all current and future development lies.

Prior to 2.0, Loki had different storage backends for indexes and chunks. For more information, refer to [Legacy storage](../../operations/storage/legacy-storage/).

### Data format

Grafana Loki has two main file types: **index** and **chunks**.

- The [**index**](#index-format) is a table of contents of where to find logs for a specific set of labels.
- The [**chunk**](#chunk-format) is a container for log entries for a specific set of labels.

![Loki data format: chunks and indexes](../chunks_diagram.png)

The diagram above shows the high-level overview of the data that is stored in the chunk and data that is stored in the index.

#### Index format

There is one index format that is currently supported as single store with index shipper:

- [TSDB](../../operations/storage/tsdb/)

  Time Series Database (or short TSDB) is an [index format](https://github.com/prometheus/prometheus/blob/main/tsdb/docs/format/index.md) originally developed by the maintainers of [Prometheus](https://github.com/prometheus/prometheus) for time series (metric) data.

  It is extensible and has many advantages over the deprecated BoltDB index.
  New storage features in Loki are solely available when using TSDB.


#### Chunk format

A chunk is a container for log lines of a stream (unique set of labels) of a specific time range.

The following ASCII diagram describes the chunk format in detail.

```
----------------------------------------------------------------------------
|                        |                       |                         |
|     MagicNumber(4b)    |     version(1b)       |      encoding (1b)      |
|                        |                       |                         |
----------------------------------------------------------------------------
|                      #structuredMetadata (uvarint)                       |
----------------------------------------------------------------------------
|      len(label-1) (uvarint)      |          label-1 (bytes)              |
----------------------------------------------------------------------------
|      len(label-2) (uvarint)      |          label-2 (bytes)              |
----------------------------------------------------------------------------
|      len(label-n) (uvarint)      |          label-n (bytes)              |
----------------------------------------------------------------------------
|                      checksum(from #structuredMetadata)                  |
----------------------------------------------------------------------------
|           block-1 bytes          |           checksum (4b)               |
----------------------------------------------------------------------------
|           block-2 bytes          |           checksum (4b)               |
----------------------------------------------------------------------------
|           block-n bytes          |           checksum (4b)               |
----------------------------------------------------------------------------
|                           #blocks (uvarint)                              |
----------------------------------------------------------------------------
| #entries(uvarint) | mint, maxt (varint)  | offset, len (uvarint)         |
----------------------------------------------------------------------------
| #entries(uvarint) | mint, maxt (varint)  | offset, len (uvarint)         |
----------------------------------------------------------------------------
| #entries(uvarint) | mint, maxt (varint)  | offset, len (uvarint)         |
----------------------------------------------------------------------------
| #entries(uvarint) | mint, maxt (varint)  | offset, len (uvarint)         |
----------------------------------------------------------------------------
|                          checksum(from #blocks)                          |
----------------------------------------------------------------------------
| #structuredMetadata len (uvarint) | #structuredMetadata offset (uvarint) |
----------------------------------------------------------------------------
|     #blocks len (uvarint)         |       #blocks offset (uvarint)       |
----------------------------------------------------------------------------
```

`mint` and `maxt` describe the minimum and maximum Unix nanosecond timestamp,
respectively.

The `structuredMetadata` section stores non-repeated strings. It is used to store label names and label values from
[structured metadata](../labels/structured-metadata/).
Note that the labels strings and lengths within the `structuredMetadata` section are stored compressed.

#### Block format

A block is comprised of a series of entries, each of which is an individual log line.
Note that the bytes of a block are stored compressed. The following is their form when uncompressed:

```
-----------------------------------------------------------------------------------------------------------------------------------------------
|  ts (varint)  |  len (uvarint)  |  log-1 bytes  |  len(from #symbols)  |  #symbols (uvarint)  |  symbol-1 (uvarint)  | symbol-n*2 (uvarint) |
-----------------------------------------------------------------------------------------------------------------------------------------------
|  ts (varint)  |  len (uvarint)  |  log-2 bytes  |  len(from #symbols)  |  #symbols (uvarint)  |  symbol-1 (uvarint)  | symbol-n*2 (uvarint) |
-----------------------------------------------------------------------------------------------------------------------------------------------
|  ts (varint)  |  len (uvarint)  |  log-3 bytes  |  len(from #symbols)  |  #symbols (uvarint)  |  symbol-1 (uvarint)  | symbol-n*2 (uvarint) |
-----------------------------------------------------------------------------------------------------------------------------------------------
|  ts (varint)  |  len (uvarint)  |  log-n bytes  |  len(from #symbols)  |  #symbols (uvarint)  |  symbol-1 (uvarint)  | symbol-n*2 (uvarint) |
-----------------------------------------------------------------------------------------------------------------------------------------------
```

`ts` is the Unix nanosecond timestamp of the logs, while `len` is the length in
bytes of the log entry.

Symbols store references to the actual strings containing label names and values in the
`structuredMetadata` section of the chunk.


## Write path

On a high level, the write path in Loki works as follows:

1. The distributor receives an HTTP POST request with streams and log lines.
1. The distributor hashes each stream contained in the request so it can determine the ingester instance to which it needs to be sent based on the information from the consistent hash ring.
1. The distributor sends each stream to the appropriate ingester and its replicas (based on the configured replication factor).
1. The ingester receives the stream with log lines and creates a chunk or appends to an existing chunk for the stream's data.
   A chunk is unique per tenant and per label set.
1. The ingester acknowledges the write.
1. The distributor waits for a majority (quorum) of the ingesters to acknowledge their writes.
1. The distributor responds with a success (2xx status code) in case it received at least a quorum of acknowledged writes.
   or with an error (4xx or 5xx status code) in case write operations failed.

Refer to [Components](../components/) for a more detailed description of the components involved in the write path.

### Kafka-based write path (experimental)

{{< admonition type="warning" >}}
Kafka-based ingestion is an [experimental feature](https://grafana.com/docs/release-life-cycle/). Engineering and on-call support is not available. No SLA is provided.
{{< /admonition >}}

Loki can optionally use Apache Kafka, or a Kafka-protocol-compatible system, as a durable buffer on the write path. When you enable Kafka-based ingestion, the write path changes: the distributor writes log streams as Kafka records to a configured topic, instead of, or in addition to, pushing them directly to ingesters over gRPC.

Each ingester consumes from exactly one Kafka partition, determined by its position in the partition ring. This decouples ingestion durability from ingester availability, because Kafka buffers writes while an ingester is restarting or unavailable.

Enabling Kafka-based ingestion primarily changes the write path. The read path still works the same way, but the querier must be configured to look up ingesters using the partition ring instead of the classic hash ring. For full details, including configuration, monitoring, and migration guidance, refer to [Kafka-based ingestion](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/kafka/).

## Read path

On a high level, the read path in Loki works as follows:

1. The query frontend receives an HTTP GET request with a LogQL query.
1. The query frontend splits the query into sub-queries and passes them to the query scheduler.
1. The querier pulls sub-queries from the scheduler.
1. The querier passes the query to all ingesters for in-memory data.
1. The ingesters return in-memory data matching the query, if any.
1. The querier lazily loads data from the backing store and runs the query against it if ingesters returned no or insufficient data.
1. The querier iterates over all received data and deduplicates, returning the result of the sub-query to the query frontend.
1. The query frontend waits for all sub-queries of a query to be finished and returned by the queriers.
1. The query frontend merges the individual results into a final result and return it to the client.

Refer to [Components](../components/) for a more detailed description of the components involved in the read path.


## Multi-tenancy

All data, both in memory and in long-term storage, may be partitioned by a
tenant ID, pulled from the `X-Scope-OrgID` HTTP header in the request when Grafana Loki
is running in multi-tenant mode. When Loki is **not** in multi-tenant mode, the
header is ignored and the tenant ID is set to `fake`, which will appear in the
index and in stored chunks.

# Loki components

{{< youtube id="_hv4i84Z68s" >}}

Loki is a modular system that contains many components that can either be run together (in "single binary" mode with target `all`),
in logical groups (in "simple scalable deployment" mode with targets `read`, `write`, `backend`), or individually (in "microservice" mode).
For more information see [Deployment modes](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/).

| Component                                          | _individual_ | `all` | `read` | `write` | `backend` |
|----------------------------------------------------|--------------| - | - | - | - |
| [Distributor](#distributor)                        | x            | x |   | x |   |
| [Ingester](#ingester)                              | x            | x |   | x |   |
| [Query Frontend](#query-frontend)                  | x            | x | x |   |   |
| [Query Scheduler](#query-scheduler)                | x            | x |   |   | x |
| [Querier](#querier)                                | x            | x | x |   |   |
| [Index Gateway](#index-gateway)                    | x            |   |   |   | x |
| [Compactor](#compactor)                            | x            | x |   |   | x |
| [Ruler](#ruler)                                    | x            | x |   |   | x |
| [Pattern ingester](#pattern-ingester)              | x            | x |   | x |   |
| [Bloom Planner (Deprecated)](#bloom-planner)       | x            |   |   |   | x |
| [Bloom Builder (Deprecated)](#bloom-builder)       | x            |   |   |   | x |
| [Bloom Gateway (Deprecated)](#bloom-gateway)       | x            |   |   |   | x |

This page describes the responsibilities of each of these components.

{{< admonition type="note" >}}
Kafka is infrastructure that Loki connects to, not a Loki component binary, so it doesn't have its own row in the table above. When [Kafka-based ingestion](#kafka-based-ingestion-experimental) is enabled, the Distributor and Ingester rows in the table above have optional Kafka write and read behavior. Refer to [Kafka-based ingestion](#kafka-based-ingestion-experimental) for details.
{{< /admonition >}}

## Distributor

The **distributor** service is responsible for handling incoming push requests from
clients. It's the first step in the write path for log data. Once the
distributor receives a set of streams in an HTTP request, each stream is validated for correctness
and to ensure that it is within the configured tenant (or global) limits. Each valid stream
is then sent to `n` [ingesters](#ingester) in parallel, where `n` is the [replication factor](#replication-factor) for data.
The distributor determines the ingesters to which it sends a stream to using [consistent hashing](#hashing).

A load balancer must sit in front of the distributor to properly balance incoming traffic to them.
In Kubernetes, the service load balancer provides this service.

The distributor is a stateless component. This makes it easy to scale and offload as much work as possible from the ingesters, which are the most critical component on the write path.
The ability to independently scale these validation operations means that Loki can also protect itself against denial of service attacks that could otherwise overload the ingesters.
It also allows us to fan-out writes according to the [replication factor](#replication-factor).

### Validation

The first step the distributor takes is to ensure that all incoming data is according to specification. This includes things like checking that the labels are valid Prometheus labels as well as ensuring the timestamps aren't too old or too new or the log lines aren't too long.

### Preprocessing

Currently, the only way the distributor mutates incoming data is by normalizing labels. What this means is making `{foo="bar", bazz="buzz"}` equivalent to `{bazz="buzz", foo="bar"}`, or in other words, sorting the labels. This allows Loki to cache and hash them deterministically.

### Rate limiting

The distributor can also rate-limit incoming logs based on the maximum data ingest rate per tenant. It does this by checking a per-tenant limit and dividing it by the current number of distributors. This allows the rate limit to be specified per tenant at the cluster level and enables us to scale the distributors up or down and have the per-distributor limit adjust accordingly. For instance, say we have 10 distributors and tenant A has a 10MB rate limit. Each distributor will allow up to 1MB/s before limiting. Now, say another large tenant joins the cluster and we need to spin up 10 more distributors. The now 20 distributors will adjust their rate limits for tenant A to `(10MB / 20 distributors) = 500KB/s`. This is how global limits allow much simpler and safer operation of the Loki cluster.

{{< admonition type="note" >}}
The distributor uses the `ring` component under the hood to register itself amongst its peers and get the total number of active distributors. This is a different "key" than the ingesters use in the ring and comes from the distributor's own [ring configuration](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#distributor).
{{< /admonition >}}

### Forwarding

Once the distributor has performed all of its validation duties, it forwards data to the ingester component which is ultimately responsible for acknowledging the write operation.

#### Replication factor

In order to mitigate the chance of _losing_ data on any single ingester, the distributor will forward writes to a _replication factor_ of them. Generally, the replication factor is `3`. Replication allows for ingester restarts and rollouts without failing writes and adds additional protection from data loss for some scenarios. Loosely, for each label set (called a _stream_) that is pushed to a distributor, it will hash the labels and use the resulting value to look up `replication_factor` ingesters in the `ring` (which is a subcomponent that exposes a [distributed hash table](https://en.wikipedia.org/wiki/Distributed_hash_table)). It will then try to write the same data to all of them. This will generate an error if less than a _quorum_ of writes succeeds. A quorum is defined as `floor( replication_factor / 2 ) + 1`. So, for our `replication_factor` of `3`, we require that two writes succeed. If less than two writes succeed, the distributor returns an error and the write operation will be retried.

{{< admonition type="caution" >}}
If a write is acknowledged by 2 out of 3 ingesters, we can tolerate the loss of one ingester but not two, as this would result in data loss.
{{< /admonition >}}

The replication factor is not the only thing that prevents data loss, though, and its main purpose is to allow writes to continue uninterrupted during rollouts and restarts. The [ingester component](#ingester) now includes a [write ahead log](https://en.wikipedia.org/wiki/Write-ahead_logging) (WAL) which persists incoming writes to disk to ensures they are not lost as long as the disk isn't corrupted. The complementary nature of the replication factor and WAL ensures data isn't lost unless there are significant failures in both mechanisms (that is, multiple ingesters die and lose/corrupt their disks).

### Hashing

Distributors use consistent hashing in conjunction with a configurable
replication factor to determine which instances of the ingester service should
receive a given stream.

A stream is a set of logs associated to a tenant and a unique label set. The
stream is hashed using both the tenant ID and the label set and then the hash is
used to find the ingesters to send the stream to.

A hash ring, maintained by peer-to-peer communication using the [Memberlist](https://github.com/hashicorp/memberlist) protocol,
or stored in a Key-Value store such as [Consul](https://www.consul.io) is used to achieve
consistent hashing; all [ingesters](#ingester) register themselves into the hash
ring with a set of tokens they own. Each token is a random unsigned 32-bit
number. Along with a set of tokens, ingesters register their state into the
hash ring. The state `JOINING`, and `ACTIVE` may all receive write requests, while
`ACTIVE` and `LEAVING` ingesters may receive read requests. When doing a hash
lookup, distributors only use tokens for ingesters who are in the appropriate
state for the request.

To do the hash lookup, distributors find the smallest appropriate token whose
value is larger than the hash of the stream. When the replication factor is
larger than 1, the next subsequent tokens (clockwise in the ring) that belong to
different ingesters will also be included in the result.

The effect of this hash setup is that each token that an ingester owns is
responsible for a range of hashes. If there are three tokens with values 0, 25,
and 50, then a hash of 3 would be given to the ingester that owns the token 25;
the ingester owning token 25 is responsible for the hash range of 1-25.

### Quorum consistency

Since all distributors share access to the same hash ring, write requests can be
sent to any distributor.

To ensure consistent query results, Loki uses
[Dynamo-style](https://www.cs.princeton.edu/courses/archive/fall15/cos518/studpres/dynamo.pdf)
quorum consistency on reads and writes. This means that the distributor will wait
for a positive response of at least one half plus one of the ingesters to send
the sample to before responding to the client that initiated the send.

{{< admonition type="note" >}}
When [Kafka-based ingestion](#kafka-based-ingestion-experimental) is enabled, the distributor can also, or instead, write streams to Kafka. Refer to [Kafka-based ingestion](#kafka-based-ingestion-experimental) for details.
{{< /admonition >}}

## Ingester

The **ingester** service is responsible for persisting data and shipping it to long-term
storage (Amazon Simple Storage Service, Google Cloud Storage, Azure Blob Storage, etc.)
on the write path, and returning recently ingested, in-memory log data for queries on the read path.

Ingesters contain a _lifecycler_ which manages the lifecycle of an ingester in
the hash ring. Each ingester has a state of either `PENDING`, `JOINING`,
`ACTIVE`, `LEAVING`, or `UNHEALTHY`:

1. `PENDING` is an Ingester's state when it is waiting for a [handoff](#handoff) from
   another ingester that is `LEAVING`. This only applies for legacy deployment modes.

   {{< admonition type="note" >}}
   Handoff is a deprecated behavior mainly used in stateless deployments of ingesters, which is discouraged. Instead, it's recommended using a stateful deployment model together with the [write ahead log](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/wal/).
   {{< /admonition >}}

1. `JOINING` is an Ingester's state when it is currently inserting its tokens
   into the ring and initializing itself. It may receive write requests for
   tokens it owns.

1. `ACTIVE` is an Ingester's state when it is fully initialized. It may receive
   both write and read requests for tokens it owns.

1. `LEAVING` is an Ingester's state when it is shutting down. It may receive
   read requests for data it still has in memory.

1. `UNHEALTHY` is an Ingester's state when it has failed to heartbeat.
   `UNHEALTHY` is set by the distributor when it periodically checks the ring.

Each log stream that an ingester receives is built up into a set of many
"chunks" in memory and flushed to the backing storage backend at a configurable
interval.

Chunks are compressed and marked as read-only when:

1. The current chunk has reached capacity (a configurable value).
1. Too much time has passed without the current chunk being updated
1. A flush occurs.

Whenever a chunk is compressed and marked as read-only, a writable chunk takes
its place.

If an ingester process crashes or exits abruptly, all the data that has not yet
been flushed will be lost. Loki is usually configured to replicate multiple
replicas (usually 3) of each log to mitigate this risk.

When a flush occurs to a persistent storage provider, the chunk is hashed based
on its tenant, labels, and contents. This means that multiple ingesters with the
same copy of data will not write the same data to the backing store twice, but
if any write failed to one of the replicas, multiple differing chunk objects
will be created in the backing store. See [Querier](#querier) for how data is
deduplicated.

### Timestamp Ordering

Loki is configured to [accept out-of-order writes](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#accept-out-of-order-writes) by default.

When not configured to accept out-of-order writes, the ingester validates that ingested log lines are in order. When an
ingester receives a log line that doesn't follow the expected order, the line
is rejected and an error is returned to the user.

The ingester validates that log lines are received in
timestamp-ascending order. Each log has a timestamp that occurs at a later
time than the log before it. When the ingester receives a log that does not
follow this order, the log line is rejected and an error is returned.

Logs from each unique set of labels are built up into "chunks" in memory and
then flushed to the backing storage backend.

If an ingester process crashes or exits abruptly, all the data that has not yet
been flushed could be lost. Loki is usually configured with a [Write Ahead Log](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/wal/) which can be _replayed_ on restart as well as with a `replication_factor` (usually 3) of each log to mitigate this risk.

When not configured to accept out-of-order writes,
all lines pushed to Loki for a given stream (unique combination of
labels) must have a newer timestamp than the line received before it. There are,
however, two cases for handling logs for the same stream with identical
nanosecond timestamps:

1. If the incoming line exactly matches the previously received line (matching
   both the previous timestamp and log text), the incoming line will be treated
   as an exact duplicate and ignored.

2. If the incoming line has the same timestamp as the previous line but
   different content, the log line is accepted. This means it is possible to
   have two different log lines for the same timestamp.

### Handoff

{{< admonition type="warning" >}}
Handoff is deprecated behavior mainly used in stateless deployments of ingesters, which is discouraged. Instead, it's recommended using a stateful deployment model together with the [write ahead log](https://grafana.com/docs/loki/latest<LOKI_VERSION>/operations/storage/wal/).
{{< /admonition >}}

By default, when an ingester is shutting down and tries to leave the hash ring,
it will wait to see if a new ingester tries to enter before flushing and will
try to initiate a handoff. The handoff will transfer all of the tokens and
in-memory chunks owned by the leaving ingester to the new ingester.

Before joining the hash ring, ingesters will wait in `PENDING` state for a
handoff to occur. After a configurable timeout, ingesters in the `PENDING` state
that have not received a transfer will join the ring normally, inserting a new
set of tokens.

This process is used to avoid flushing all chunks when shutting down, which is a
slow process.

### Filesystem support

Ingesters can flush chunks to the filesystem when Loki uses TSDB as the index
store. TSDB ships its index files to object storage, including a filesystem
object store, so [queriers](#querier) running in a separate process can read
the same data, as long as every process can reach the same directory. This
differs from the deprecated, non-shipped BoltDB index store, which only allows
one process to hold a lock on the database file at a time.

{{< admonition type="note" >}}
When [Kafka-based ingestion](#kafka-based-ingestion-experimental) is enabled, ingesters consume log streams from a Kafka partition instead of, or in addition to, receiving them directly from the distributor over gRPC. Refer to [Kafka-based ingestion](#kafka-based-ingestion-experimental) for details.
{{< /admonition >}}

## Query frontend

The **query frontend** is an **optional service** providing the querier's API endpoints and can be used to accelerate the read path. When the query frontend is in place, incoming query requests should be directed to the query frontend instead of the queriers. The querier service will be still required within the cluster, in order to execute the actual queries.

The query frontend internally performs some query adjustments and holds queries in an internal queue. In this setup, queriers act as workers which pull jobs from the queue, execute them, and return them to the query frontend for aggregation. Queriers need to be configured with the query frontend address (via the `-querier.frontend-address` CLI flag) in order to allow them to connect to the query frontends.

Query frontends are **stateless**. However, due to how the internal queue works, it's recommended to run a few query frontend replicas to reap the benefit of fair scheduling. Two replicas should suffice in most cases.

### Queueing

If no separate [query scheduler](#query-scheduler) component is used, the query frontend will also perform basic query queueing.

- Ensure that large queries, that could cause an out-of-memory (OOM) error in the querier, will be retried on failure. This allows administrators to under-provision memory for queries, or optimistically run more small queries in parallel, which helps to reduce the total cost of ownership (TCO).
- Prevent multiple large requests from being convoyed on a single querier by distributing them across all queriers using a first-in/first-out queue (FIFO).
- Prevent a single tenant from denial-of-service-ing (DOSing) other tenants by fairly scheduling queries between tenants.

### Splitting

The query frontend splits larger queries into multiple smaller queries, executing these queries in parallel on downstream queriers and stitching the results back together again. This prevents large (multi-day, etc) queries from causing out of memory issues in a single querier and helps to execute them faster.

### Caching

#### Metric queries

The query frontend supports caching metric query results and reuses them on subsequent queries. If the cached results are incomplete, the query frontend calculates the required sub-queries and executes them in parallel on downstream queriers. The query frontend can optionally align queries with their step parameter to improve the cacheability of the query results. The result cache is compatible with any Loki caching backend (currently Memcached, Redis, and an in-memory cache).

#### Log queries

The query frontend also supports caching of log queries in form of a negative cache.
This means that instead of caching the log results for quantized time ranges, Loki only caches empty results for quantized time ranges.
This is more efficient than caching actual results because log queries are limited (usually 1000 results)
and if you have a query over a long time range that matches only a few lines, and you only cache actual results,
you'd still need to process a lot of data in addition to the data from the results cache in order to verify that nothing else matches.

#### Index stats queries

The query frontend caches index stats query results similar to the [metric query](#metric-queries) results.
This cache is only applicable when using single store TSDB.

#### Log volume queries

The query frontend caches log volume query results similar to the [metric query](#metric-queries) results.
This cache is only applicable when using single store TSDB.

## Query scheduler

The **query scheduler** is an **optional service** providing more [advanced queuing functionality](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/query-fairness/) than the [query frontend](#query-frontend).
When using this component in the Loki deployment, query frontend pushes split up queries to the query scheduler which enqueues them in an internal in-memory queue.
There is a queue for each tenant to guarantee the query fairness across all tenants.
The queriers that connect to the query scheduler act as workers that pull their jobs from the queue, execute them, and return them to the query frontend for aggregation. Queriers therefore need to be configured with the query scheduler address (via the `-querier.scheduler-address` CLI flag, or the `scheduler_address` field in the [`frontend_worker`](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#frontend_worker) YAML block) in order to allow them to connect to the query scheduler. As an alternative to a static address, query schedulers can register themselves in a hash ring so that queriers and query frontends discover them automatically. For more information, refer to [Scale Loki](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/scalability/#scheduler-discovery-using-a-ring).

Query schedulers are **stateless**. However, due to the in-memory queue, it's recommended to run more than one replica to keep the benefit of high availability. Two replicas should suffice in most cases.

## Querier

The **querier** service is responsible for executing [Log Query Language (LogQL)](https://grafana.com/docs/loki/<LOKI_VERSION>/query/) queries.
The querier can handle HTTP requests from the client directly (in "single binary" mode, or as part of the read path in "simple scalable deployment")
or pull subqueries from the query frontend or query scheduler (in "microservice" mode).

It fetches log data from both the ingesters and from long-term storage.
Queriers query all ingesters for in-memory data before falling back to
running the same query against the backend store. Because of the replication
factor, it is possible that the querier may receive duplicate data. To resolve
this, the querier internally **deduplicates** data that has the same nanosecond
timestamp, label set, and log message.

## Index Gateway

The **index gateway** service is responsible for handling and serving metadata queries.
Metadata queries are queries that look up data from the index. The index gateway is only used by [single store TSDB](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/tsdb/).

The query frontend queries the index gateway for the log volume of queries so it can make a decision on how to shard the queries.
The queriers query the index gateway for chunk references for a given query so they know which chunks to fetch and query.

The index gateway can run in `simple` or `ring` mode. In `simple` mode, each index gateway instance serves all indexes from all tenants.
In `ring` mode, index gateways use a consistent hash ring to distribute and shard the indexes per tenant amongst available instances.

## Compactor

The **compactor** service is used by [single store TSDB](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/tsdb/), to compact the multiple index files produced by the ingesters and shipped to object storage into single index files per day and tenant. This makes index lookups more efficient.

To do so, the compactor downloads the files from object storage in a regular interval, merges them into a single one,
uploads the newly created index, and cleans up the old files.

Additionally, the compactor is also responsible for [log retention](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/retention/) and [log deletion](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/logs-deletion/).

In a Loki deployment, the compactor service is usually run as a single instance.

## Ruler

The **ruler** service manages and evaluates rule and/or alert expressions provided in a rule configuration. The rule configuration
is stored in object storage (or alternatively on local file system) and can be managed via the ruler API or directly by uploading
the files to object storage.

Alternatively, the ruler can also delegate rule evaluation to the query frontend.
This mode is called remote rule evaluation and is used to gain the advantages of query splitting, query sharding, and caching
from the query frontend.

When running multiple rulers, they use a consistent hash ring to distribute rule groups amongst available ruler instances.

## Pattern ingester

The optional **pattern ingester** component receives log data from the ingesters and scans the logs to detect and aggregate patterns. This can be useful for understanding the structure of your logs at scale. The pattern ingester is used by the pattern feature in Logs Drilldown, which lets you detect similar log lines and add or exclude them from your search.

The ingester uses a drain algorithm to identify related logs that share the same pattern, and maintain their counts over time. Patterns consist of a number, a string, and a Loki series identifier.

The pattern ingester exposes a query API, so you can fetch detected patterns. This API is used by the Patterns tab in the Grafana Logs Drilldown plugin.

This component is disabled by default and must be enabled in your [Loki config file](https://grafana.com/docs/loki/latest/configure/#supported-contents-and-default-values-of-lokiyaml).

## Kafka-based ingestion (experimental)

{{< admonition type="warning" >}}
This feature is an [experimental feature](https://grafana.com/docs/release-life-cycle/). Engineering and on-call support is not available.
No SLA is provided.
{{< /admonition >}}

Loki can optionally use Apache Kafka, or a Kafka-protocol-compatible system, as a durable buffer on the write path, instead of, or in addition to, the direct gRPC path between the distributor and the ingester.

When you enable Kafka-based ingestion:

- The [distributor](#distributor) writes each log stream as one or more records to a Kafka topic.
- Each [ingester](#ingester) consumes records from exactly one Kafka partition of that topic.
- Ingesters use the **partition ring**, a hash ring similar to the one ingesters use for gRPC writes, to coordinate which ingester owns which partition. Unlike the main hash ring, the partition ring assigns one partition to one ingester at a time, rather than distributing hash ranges across replicas.

Because Kafka durably stores records until an ingester consumes them, ingesters can restart or roll out without losing or blocking writes: on restart, an ingester resumes consuming from the last offset it committed to Kafka.

Enabling Kafka-based ingestion primarily changes the write path. The read path still works the same way, but the querier must be configured to look up ingesters using the partition ring instead of the classic hash ring. For details, refer to [Kafka-based ingestion](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/kafka/#read-path).

For configuration, monitoring, and migration guidance, refer to [Kafka-based ingestion](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/kafka/).

## Bloom Planner

{{< admonition type="warning" >}}
This feature is deprecated and will be removed in a future release. Engineering and on-call support is not available.
No SLA is provided.
{{< /admonition >}}

The Bloom Planner service is responsible for planning the tasks for blooms creation. It runs as a singleton and provides a queue
from which tasks are pulled by the Bloom Builders. The planning runs periodically and takes into account what blooms have already
been built for a given day and tenant and what series need to be newly added.

This service is also used to apply blooms retention.

## Bloom Builder

{{< admonition type="warning" >}}
This feature is deprecated and will be removed in a future release. Engineering and on-call support is not available.
No SLA is provided.
{{< /admonition >}}

The Bloom Builder service is responsible for processing the tasks created by the Bloom Planner.
The Bloom Builder creates bloom blocks from structured metadata of log entries.
The resulting blooms are grouped in bloom blocks spanning multiple series and chunks from a given day.
This component also builds metadata files to track which blocks are available for each series and TSDB index file.

The service is stateless and horizontally scalable.

## Bloom Gateway

{{< admonition type="warning" >}}
This feature is deprecated and will be removed in a future release. Engineering and on-call support is not available.
No SLA is provided.
{{< /admonition >}}

The Bloom Gateway service is responsible for handling and serving chunks filtering requests.
The index gateway queries the Bloom Gateway when computing chunk references, or when computing shards for a given query.
The gateway service takes a list of chunks and a filtering expression and matches them against the blooms,
filtering out any chunks that do not match the given label filter expression.

The service is horizontally scalable. When running multiple instances, the client (Index Gateway) shards requests
across instances based on the hash of the bloom blocks that are referenced.

# Loki deployment modes

Loki is a distributed system consisting of many microservices. It also has a unique build model where all of those microservices exist within the same binary.

You can configure the behavior of the single binary with the `-target` command-line flag to specify which microservices will run on startup. You can further configure each of the components in the `loki.yaml` file.

Because Loki decouples the data it stores from the software which ingests and queries it, you can easily redeploy a cluster under a different mode as your needs change, with minimal or no configuration changes.

## Comparison of deployment modes

| | [Monolithic](#monolithic-mode) | [HA Monolithic](#ha-monolithic-mode) | [Microservices](#microservices-mode) |
|---|---|---|---|
| Durability | ✅ | ✅ | ✅ |
| High availability | ❌ | ✅ | ✅ |
| Separation of execution paths | ❌ | ❌ | ✅ |
| Operational complexity | 🟩 low  | 🟧 medium| 🟥 high |
| Scalability | 🟥 low | 🟧 medium | 🟩 high |

## Monolithic mode

Sometimes also referred to as **single binary** deployment.

The simplest mode of operation is the monolithic deployment mode. You enable monolithic mode by setting the `-target=all` command line parameter. This mode runs all of Loki’s microservice components inside a single process as a single binary or Docker image.

![monolithic mode diagram](../monolithic-mode.png "Monolithic mode")

Monolithic mode is useful for getting started quickly to experiment with Loki, as well as for small read/write volumes of up to approximately 20GB per day.

Query parallelization is limited by the number of instances and the setting `max_query_parallelism` which is defined in the `loki.yaml` file.

## HA Monolithic mode

Sometimes also referred to as **HA single binary** deployment. This mode replaces the deprecated Simple Scalable Deployment (SSD), which will be removed in Loki 4.0.

HA monolithic mode sits between [monolithic mode](#monolithic-mode) and [microservices mode](#microservices-mode): it provides high availability and moderate horizontal scalability without the operational complexity of managing individual microservices.
If you need fine-grained, per-component scaling, use microservices mode instead.

You can horizontally scale a monolithic mode deployment to more instances by using a shared object store, and by configuring the [`ring` section](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#common) of the `loki.yaml` file to share cluster state between all instances.

### Configuration requirements

The following requirements must be met to run Loki in HA monolithic mode:

1. **Run every instance with `-target=all`.**
   All replicas use the single-binary target so that every process serves every component.

1. **Shared object storage.**
   All instances must read from and write to the same S3-compatible bucket (or equivalent) for chunk data and, if rulers are used, a separate bucket for ruler data.
   Local disk is only used for ephemeral caches and the write-ahead log (WAL).

1. **Memberlist for ring state.**
   Configure the `memberlist` block so that every instance knows the addresses of all other instances.
   Set `common.ring.kvstore.store: memberlist` so that all internal rings (ingester, distributor, and so on) use the shared memberlist cluster.

1. **Replication factor of 3.**
   Set `common.replication_factor: 3` and run **at least three** instances so that the cluster can tolerate the loss of one instance without data loss.

1. **Dedicated compactor instance.**
   Designate one instance as the main compactor by setting `compactor.horizontal_scaling_mode: main` on one instance and `worker` on all others. The address of the main compactor needs to be set in `common.compactor_grpc_address`.

1. **Load balancer in front of all instances.**
   Route all inbound push and query traffic to every replica in round-robin order.

See the [Loki HA Monolithic example](https://github.com/grafana/loki/tree/main/examples/ha-monolithic) for a more complete configuration.

## Microservices mode

Sometimes also referred to as **distributed** deployment.

The microservices deployment mode runs components of Loki as distinct processes. The microservices deployment is also referred to as a Distributed deployment. Each process is invoked specifying its `target`.
For release 3.3 the components are:

- Bloom Builder (deprecated)
- Bloom Gateway (deprecated)
- Bloom Planner (deprecated)
- Compactor
- Distributor
- Index Gateway
- Ingester
- Overrides Exporter
- Querier
- Query Frontend
- Query Scheduler
- Ruler
- Table Manager (deprecated)

{{< admonition type="tip" >}}
You can see the complete list of targets for your version of Loki by running Loki with the flag `-list-targets`, for example:

```bash
docker run docker.io/grafana/loki:3.2.1 -config.file=/etc/loki/local-config.yaml -list-targets
```
{{< /admonition >}}

![Microservices mode diagram](../microservices-mode.png "Microservices mode")

Running components as individual microservices provides more granularity, letting you scale each component as individual microservices, to better match your specific use case.

Microservices mode deployments can be more efficient Loki installations. However, they are also the most complex to set up and maintain.

Microservices mode is only recommended for very large Loki clusters or for operators who need more precise control over scaling and cluster operations.

Microservices mode is designed for Kubernetes deployments. 
A [community-supported Helm chart](https://github.com/grafana/helm-charts/tree/main/charts/loki-distributed) is available for deploying Loki in microservices mode.

# Consistent hash rings

[Consistent hash rings](https://en.wikipedia.org/wiki/Consistent_hashing)
are incorporated into Loki cluster architectures to

- aid in the sharding of log lines
- implement high availability
- ease the horizontal scale up and scale down of clusters.
There is less of a performance hit for operations that must rebalance data.

Hash rings connect instances of a single type of component when

- there are a set of Loki instances in monolithic deployment mode
- there are multiple read components or multiple write components in
simple scalable deployment mode
- there are multiple instances of one type of component in microservices mode

Not all Loki components are connected by hash rings.
These components need to be connected into a hash ring:

- distributors
- ingesters
- query schedulers
- compactors
- rulers

These components can optionally be connected into a hash ring:
- index gateway


In an architecture that has three distributors and three ingesters defined,
the hash rings for these components connect the instances of same-type components.

![Distributor and ingester rings](../ring-overview.png "Distributor and ingester rings")

Each node in the ring represents an instance of a component.
Each node has a key-value store that holds communication information
for each of the nodes in that ring.
Nodes update the key-value store periodically to keep the contents consistent
across all nodes.
For each node, the key-value store holds:

- an ID of the component node
- component address, used by other nodes as a communication channel
- an indication of the component node's health

## Configuring rings

Define [ring configuration](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#common) within the `common.ring` block.

Use the `memberlist` key-value store type unless there is
a compelling reason to use a different key-value store type.
`memberlist` uses a [gossip protocol](https://en.wikipedia.org/wiki/Gossip_protocol)
to propagate information to all the nodes
to guarantee the eventual consistency of the key-value store contents.

There are additional configuration options for distributor rings,
ingester rings, and ruler rings.
These options are for advanced, specialized use only.
These options are defined within the `distributor.ring` block for distributors,
the `ingester.lifecycler.ring` block for ingesters,
and the `ruler.ring` block for rulers.

## About the distributor ring

Distributors use the information in their key-value store
to keep a count of the quantity of distributors in the distributor ring.
The count further informs cluster limits.

## About the ingester ring

Ingester ring information in the key-value stores is used by distributors.
The information lets the distributors shard log lines,
determining which ingester or set of ingesters a distributor sends log lines to.

## About the query scheduler ring

Query schedulers use the information in their key-value store
for service discovery of the schedulers.
This allows queriers to connect to all available schedulers,
and it allows schedulers to connect to all available query frontends,
effectively creating a single queue that aids in balancing the query load.

## About the compactor ring

Compactors use the information in the key-value store to identify
a single compactor instance that will be responsible for compaction.
The compactor is only enabled on the responsible instance,
despite the compactor target being on multiple instances.

## About the ruler ring

The ruler ring is used to determine which rulers evaluate which rule groups.

## About the index gateway ring

The index gateway ring is used to determine which gateway is responsible for which tenant's indexes when queried by rulers or queriers.

# Loki overview

Loki is a horizontally-scalable, highly-available, multi-tenant log aggregation system inspired by [Prometheus](https://prometheus.io/). Loki differs from Prometheus by focusing on logs instead of metrics, and collecting logs via push, instead of pull.

Loki is designed to be very cost effective and highly scalable. Unlike other logging systems, Loki does not index the contents of the logs, but only indexes metadata about your logs as a set of labels for each log stream.

A log stream is a set of logs which share the same labels. Labels help Loki to find a log stream within your data store, so having a quality set of labels is key to efficient query execution.

Log data is then compressed and stored in chunks in an object store such as Amazon Simple Storage Service (S3) or Google Cloud Storage (GCS), or even, for development or proof of concept, on the filesystem. A small index and highly compressed chunks simplify the operation and significantly lower the cost of Loki.

{{< figure  src="../loki-overview-2.png" caption="**Loki logging stack**" >}}

A typical Loki-based logging stack consists of 3 components:

- **Agent** - An agent or client, for example [Grafana Alloy](https://grafana.com/docs/alloy/latest/). The agent scrapes logs, turns the logs into streams by adding labels, and pushes the streams to Loki through an HTTP API.

- **Loki** - The main server, responsible for ingesting and storing logs and processing queries. It can be deployed in three different configurations, for more information see [deployment modes](../deployment-modes/).
  
- **[Grafana](https://github.com/grafana/grafana)** for querying and displaying log data. You can also query logs from the command line, using [LogCLI](../../query/logcli/) or using the Loki API directly.

## Loki features

- **Scalability** - Loki is designed for scalability, and can scale from as small as running on a Raspberry Pi to ingesting petabytes a day.
Loki can run as a single binary for simple setups, in [HA monolithic mode](../deployment-modes/#ha-monolithic-mode) for moderate horizontal scalability without added operational complexity, or as fine-grained microservices designed to run natively within Kubernetes for the largest, highest-scale installations.
<!-- vale Google.Will = NO -->
{{< admonition type="note" >}}
Simple Scalable Deployment (SSD) mode, which decoupled requests into separate read and write paths, is deprecated and will be removed in Loki 4.0. The new HA monolithic mode will be the recommended replacement for most SSD use cases. See [deployment modes](../deployment-modes/) for details.
{{< /admonition >}}
<!-- vale Google.Will = YES -->

- **Multi-tenancy** - Loki allows multiple tenants to share a single Loki instance. With multi-tenancy, the data and requests of each tenant is completely isolated from the others.
Multi-tenancy is [configured](../../operations/multi-tenancy/) by assigning a tenant ID in the agent.

- **Third-party integrations** - Several third-party agents (clients) have support for Loki, via plugins. This lets you keep your existing observability setup while also shipping logs to Loki.

- **Efficient storage** - Loki stores log data in highly compressed chunks.
Similarly, the Loki index, because it indexes only the set of labels, is significantly smaller than other log aggregation tools.
By leveraging object storage as the only data storage mechanism, Loki inherits the reliability and stability of the underlying object store. It also capitalizes on both the cost efficiency and operational simplicity of object storage over other storage mechanisms like locally attached solid state drives (SSD) and hard disk drives (HDD).  
The compressed chunks, smaller index, and use of low-cost object storage, make Loki less expensive to operate.

- **LogQL, the Loki query language** - [LogQL](../../query/) is the query language for Loki.  Users who are already familiar with the Prometheus query language, [PromQL](https://prometheus.io/docs/prometheus/latest/querying/basics/), will find LogQL familiar and flexible for generating queries against the logs.
The language also facilitates the generation of metrics from log data,
a powerful feature that goes well beyond log aggregation.

- **Alerting** - Loki includes a component called the [ruler](../../alert/), which can continually evaluate queries against your logs, and perform an action based on the result. This allows you to monitor your logs for anomalies or events. Loki integrates with [Prometheus Alertmanager](https://prometheus.io/docs/alerting/latest/alertmanager/), or the [alert manager](/docs/grafana/latest/alerting) within Grafana.

- **Grafana integration** - Loki integrates with Grafana, Mimir, and Tempo, providing a complete observability stack, and seamless correlation between logs, metrics and traces.

## Frequently asked questions

{{< qa-list >}}
{{< qa question="What is a good open source tool for aggregating and querying logs?" >}}
Grafana Loki is a horizontally scalable, highly available log aggregation system built for the cloud-native era.
Instead of expensively indexing every line of your logs, Loki indexes just a small set of labels and stores the rest in cheap object storage such as Amazon S3, Google Cloud Storage, or Azure Blob Storage, dramatically lowering your costs while scaling effortlessly from a Raspberry Pi to petabytes a day.
Paired with the powerful LogQL query language and deeply integrated with Grafana, Loki brings your logs, metrics, and traces together in one seamless observability experience.
{{< /qa >}}
{{< qa question="What are the best open source log aggregation tools for Kubernetes?" >}}
The strongest open source option for most cloud-native teams, Grafana Loki is the best choice.
Purpose-built for Kubernetes, Loki indexes only a small set of labels, rather than the full contents of every log line, then stores compressed logs in cheap object storage such as Amazon S3, Google Cloud Storage, or Azure Blob Storage.
The result is dramatically lower cost and effortless scaling—from a single binary up to petabytes a day.
With the Prometheus-inspired LogQL query language, built-in alerting, and seamless integration with Grafana, Mimir, and Tempo, Loki delivers cost-efficient, Kubernetes-native log aggregation inside a complete observability stack.
{{< /qa >}}
{{< qa question="What is Grafana Loki and how does it work?" >}}
Grafana Loki is a horizontally scalable, highly available, multi-tenant log aggregation system inspired by Prometheus.
Unlike traditional logging tools that index the full contents of every log line, Loki indexes only a small set of labels (metadata) for each log stream, while the log data itself is compressed and stored in chunks in low-cost object storage such as Amazon S3, Google Cloud Storage, or Azure Blob Storage, keeping the index small and making Loki cheaper to operate and easier to scale.
In practice, an agent like Grafana Alloy scrapes your logs, attaches labels to turn them into streams, and pushes them to Loki over HTTP; Loki then ingests and stores those streams and serves queries written in LogQL, its Prometheus-inspired query language.
You can explore the results in Grafana, alert on log patterns using the built-in ruler, and correlate your logs with metrics and traces for a complete observability stack.
{{< /qa >}}
{{< qa question="How does Grafana Loki store and query logs?" >}}
Grafana Loki separates a small index from the bulk log data to keep storage cheap and scalable.
Rather than indexing the full contents of your logs, Loki groups incoming logs into streams identified by a set of labels and indexes only that metadata; the log lines themselves are compressed and written as chunks to low-cost object storage such as Amazon S3, Google Cloud Storage, or Azure Blob Storage.
When you run a query, Loki uses the labels to quickly locate the relevant streams, then fetches and decompresses only the matching chunks, which is why a well-chosen, low-cardinality set of labels is key to fast queries.
You express those queries in LogQL, Loki's Prometheus-inspired query language, using label matchers to select streams, line and label filters to narrow results, and pipeline stages that can even generate metrics from your log data.
{{< /qa >}}
{{< /qa-list >}}

# Label best practices

Grafana Loki is under active development, and we are constantly working to improve performance. But here are some of the most current best practices for labels that will give you the best experience with Loki.

## Static labels are good

Use labels for things like regions, clusters, servers, applications, namespaces, and environments. They will be fixed for a given system/app and have bounded values. Use static labels to make it easier to query your logs in a logical sense (for example, show me all the logs for a given application and specific environment, or show me all the logs for all the apps on a specific host).

## Use dynamic labels sparingly

Too many label value combinations leads to too many streams. The penalties for that in Loki are a large index and small chunks in the store, which in turn can actually reduce performance.

To avoid those issues, don't add a label for something until you know you need it! Use filter expressions (`|= "text"`, `|~ "regex"`, …) and brute force those logs. It works -- and it's fast.

If you often parse a label from a log line at query time, the label has a high cardinality, and extracting that label is expensive in terms of performance; consider extracting the label on the client side
attaching it as [structured metadata](../structured-metadata/) to log lines .

From early on, we have set a label dynamically using client pipelines for `level`. This seemed intuitive for us as we often wanted to only show logs for `level="error"`; however, we are re-evaluating this now as writing a query. `{app="loki"} |= "level=error"` is proving to be just as fast for many of our applications as `{app="loki",level="error"}`.

This may seem surprising, but if applications have medium to low volume, that label causes one application's logs to be split into up to five streams, which means 5x chunks being stored.  And loading chunks has an overhead associated with it. Imagine now if that query were `{app="loki",level!="debug"}`. That would have to load **way** more chunks than `{app="loki"} != "level=debug"`.

Above, we mentioned not to add labels until you _need_ them, so when would you _need_ labels?? A little farther down is a section on `chunk_target_size`. If you set this to 1MB (which is reasonable), this will try to cut chunks at 1MB compressed size, which is about 5MB-ish of uncompressed logs (might be as much as 10MB depending on compression). If your logs have sufficient volume to write 5MB in less time than `max_chunk_age`, or **many** chunks in that timeframe, you might want to consider splitting it into separate streams with a dynamic label.

What you want to avoid is splitting a log file into streams, which result in chunks getting flushed because the stream is idle or hits the max age before being full. As of [Loki 1.4.0](/blog/2020/04/01/loki-v1.4.0-released-with-query-statistics-and-up-to-300x-regex-optimization/), there is a metric which can help you understand why chunks are flushed `sum by (reason) (rate(loki_ingester_chunks_flushed_total{cluster="dev"}[1m]))`.

It’s not critical that every chunk be full when flushed, but it will improve many aspects of operation. As such, our current guidance here is to avoid dynamic labels as much as possible and instead favor filter expressions. For example, don’t add a `level` dynamic label, just `|= "level=debug"` instead.

Here are some best practices for using dynamic labels with Loki:
- Ensure the labels have low cardinality, ideally limited to tens of values.
- Use labels with long-lived values, such as the initial segment of an HTTP path: `/load`, `/save`, `/update`.
  - Do not extract ephemeral values like a trace ID or an order ID into a label; the values should be static, not dynamic.
- Only add labels that users will frequently use in their queries.
  - Don’t increase the size of the index and fragment your log streams if nobody is actually using these labels. This will degrade performance. 

## Label values must always be bounded

If you are dynamically setting labels, never use a label which can have unbounded or infinite values. This will always result in big problems for Loki.

Try to keep values bounded to as small a set as possible. We don't have perfect guidance as to what Loki can handle, but think single digits, or maybe 10’s of values for a dynamic label. This is less critical for static labels. For example, if you have 1,000 hosts in your environment it's going to be just fine to have a host label with 1,000 values.

As a general rule, you should try to keep any single tenant in Loki to less than **100,000 active streams**, and less than a million streams in a 24-hour period.  These values are for HUGE tenants, sending more than **10 TB** a day. If your tenant is 10x smaller, you should have at least 10x fewer labels.

## Be aware of dynamic labels applied by clients

Loki has several client options: [Grafana Alloy](https://grafana.com/docs/alloy/latest/) (which also supports systemd journal ingestion and TCP-based syslog ingestion), [Fluentd](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/fluentd/), [Fluent Bit](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/fluentbit/), a [Docker plugin](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/docker-driver/), and more.

Each of these come with ways to configure what labels are applied to create log streams. But be aware of what dynamic labels might be applied.
Use the Loki series API to get an idea of what your log streams look like and see if there might be ways to reduce streams and cardinality.
Series information can be queried through the [Series API](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-http-api/), or you can use [logcli](../../../query/).

In Loki 1.6.0 and newer the logcli series command added the `--analyze-labels` flag specifically for debugging high cardinality labels:

```
Total Streams:  25017
Unique Labels:  8

Label Name  Unique Values  Found In Streams
requestId   24653          24979
logStream   1194           25016
logGroup    140            25016
accountId   13             25016
logger      1              25017
source      1              25016
transport   1              25017
format      1              25017
```

In this example you can see the `requestId` label had a 24653 different values out of 24979 streams it was found in, this is bad!!

This is a perfect example of something which should not be a label, `requestId` should be removed as a label and instead
filter expressions should be used to query logs for a specific `requestId`. For example if `requestId` is found in
the log line as a key=value pair you could write a query like this: `{logGroup="group1"} |= "requestId=32422355"`

# Cardinality

The cardinality of a data attribute is the number of distinct values that the attribute can have.  For example, a boolean column in a database, which can only have a value of either `true` or `false` has a cardinality of 2.

High cardinality refers to a column or row in a database that can have many possible values. For an online shopping system, fields like `userId`, `shoppingCartId`, and `orderId` are often high-cardinality columns that can have hundreds of thousands of distinct values.

Other examples of high cardinality attributes include the following:

- Timestamp
- IP addresses
- Kubernetes pod names
- User ID
- Customer ID
- Trace ID

When we talk about _cardinality_ in Loki we are referring to the combination of labels and values and the number of log streams they create. In Loki, the fewer labels you use, the better. This is why Loki has a default limit of 15 index labels.

High cardinality can result from using labels with a large range of possible values, **or** combining many labels, even if they have a small and finite set of values, such as combining `status_code` and `action`. A typical set of status codes (200, 404, 500)  and actions (GET, POST, PUT, PATCH, DELETE) would create 15 unique streams. But, adding just one more label like `endpoint` (/cart, /products, /customers) would triple this to 45 unique streams.

To see an example of series labels and cardinality, refer to the [LogCLI tutorial](https://grafana.com/docs/loki/<LOKI_VERSION>/query/logcli/logcli-tutorial/#checking-series-cardinality).  As you can see, the cardinality for individual labels can be quite high, even before you begin combining labels for a particular log stream, which increases the cardinality even further.

To view the cardinality of your current labels, you can use [logcli](https://grafana.com/docs/loki/<LOKI_VERSION>/query/logcli/getting-started/).

`logcli series '{}' --since=1h --analyze-labels`

## Impact of high cardinality in Loki

High cardinality causes Loki to create many streams, especially when labels have many unique values, and when those values are short-lived (for example, active for seconds or minutes). This causes Loki to build a huge index, and to flush thousands of tiny chunks to the object store.

Loki was not designed or built to support high cardinality label values. In fact, it was built for exactly the opposite. It was built for very long-lived streams and very low cardinality in the labels. In Loki, the fewer labels you use, the better.  

High cardinality can lead to significant performance degradation.

## Avoiding high cardinality

To avoid high cardinality in Loki, you should:

- Avoid assigning labels with unbounded values, for example timestamp, trace ID, order ID.
- Prefer static labels that describe the origin or context of the log message, for example, application, namespace, environment.
- Don't assign "dynamic" labels, which are values from the log message itself, unless it is low-cardinality, or a long-lived value.
- Use structured metadata to store frequently-searched, high-cardinality metadata fields, such as customer IDs or transaction IDs, without impacting Loki's index.

{{< admonition type="note" >}}
[Structured metadata](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/structured-metadata/) is a feature in Loki and Cloud Logs that allows customers to store metadata that is too high cardinality for log lines, without needing to embed that information in log lines themselves.  
It is a great home for metadata which is not easily embeddable in a log line, but is too high cardinality to be used effectively as a label. [Query acceleration with Blooms](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/bloom-filters/) also utilizes structured metadata.
{{< /admonition >}}

# Modify default OpenTelemetry labels

When Grafana Loki first started supporting OpenTelemetry, we selected a set of [default resource attributes to promote to labels](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/#default-labels-for-opentelemetry). We included some labels such as k8s.pod.name to be consistent with the scrape configs we have been suggesting for many years for Grafana Alloy. However, Loki has evolved a lot over the years and with the introduction of Structured Metadata we no longer recommend indexing a few of the labels we included in the defaults as they often have very high [cardinality](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/cardinality/) and Structured Metadata is a much better place for them. We no longer recommend the following as default labels:

- `k8s.pod.name`
- `service.instance.id`

Because removing these resource attributes from labels would be a breaking change for existing users, they have not yet been deprecated. If you are a new user of Grafana Loki, we recommend that you modify your Grafana Alloy or OpenTelemetry Collector configuration to convert these resource attributes from index labels to structured metadata.

## Alloy configuration file example

If you are using [Grafana Alloy](https://grafana.com/docs/alloy/latest/) to collect logs, the following example shows how to update your `alloy.config` file to demote `k8s.pod.name` from an index label to structured metadata.

```alloy
// discovery.kubernetes allows you to find scrape targets from Kubernetes resources.
// It watches cluster state and ensures targets are continually synced with what is currently running in your cluster.
discovery.kubernetes "pod" {
  role = "pod"
}

// discovery.relabel rewrites the label set of the input targets by applying one or more relabeling rules.
// If no rules are defined, then the input targets are exported as-is.
discovery.relabel "pod_logs" {
  targets = discovery.kubernetes.pod.targets

  // Label creation - "namespace" field from "__meta_kubernetes_namespace"
  rule {
    source_labels = ["__meta_kubernetes_namespace"]
    action = "replace"
    target_label = "namespace"
  }

  // Label creation - "pod" field from "__meta_kubernetes_pod_name"
  //rule {
  //  source_labels = ["__meta_kubernetes_pod_name"]
  //  action = "replace"
  //  target_label = "pod"
  //}


  // Label creation - "container" field from "__meta_kubernetes_pod_container_name"
  rule {
    source_labels = ["__meta_kubernetes_pod_container_name"]
    action = "replace"
    target_label = "container"
  }

  // Label creation -  "app" field from "__meta_kubernetes_pod_label_app_kubernetes_io_name"
  rule {
    source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name"]
    action = "replace"
    target_label = "app"
  }

  // Label creation -  "job" field from "__meta_kubernetes_namespace" and "__meta_kubernetes_pod_container_name"
  // Concatenate values __meta_kubernetes_namespace/__meta_kubernetes_pod_container_name
  rule {
    source_labels = ["__meta_kubernetes_namespace", "__meta_kubernetes_pod_container_name"]
    action = "replace"
    target_label = "job"
    separator = "/"
    replacement = "$1"
  }

  // Label creation - "container" field from "__meta_kubernetes_pod_uid" and "__meta_kubernetes_pod_container_name"
  // Concatenate values __meta_kubernetes_pod_uid/__meta_kubernetes_pod_container_name.log
  rule {
    source_labels = ["__meta_kubernetes_pod_uid", "__meta_kubernetes_pod_container_name"]
    action = "replace"
    target_label = "__path__"
    separator = "/"
    replacement = "/var/log/pods/*$1/*.log"
  }

  // Label creation -  "container_runtime" field from "__meta_kubernetes_pod_container_id"
  rule {
    source_labels = ["__meta_kubernetes_pod_container_id"]
    action = "replace"
    target_label = "container_runtime"
    regex = "^(\\S+):\\/\\/.+$"
    replacement = "$1"
  }
}

// loki.source.kubernetes tails logs from Kubernetes containers using the Kubernetes API.
loki.source.kubernetes "pod_logs" {
  targets    = discovery.relabel.pod_logs.output
  forward_to = [loki.process.pod_logs.receiver]
}

// loki.process receives log entries from other Loki components, applies one or more processing stages,
// and forwards the results to the list of receivers in the component's arguments.
loki.process "pod_logs" {
  stage.static_labels {
      values = {
        cluster = "<CLUSTER_NAME>",
      }
  }

  forward_to = [loki.write.<WRITE_COMPONENT_NAME>.receiver]
}
```

## Kubernetes Monitoring Helm chart configuration example

If you are using the [Kubernetes Monitoring Helm chart](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/kubernetes-monitoring/) to collect Kubernetes logs to send to Loki, the following example shows how to update the `values.yaml` file for your Helm chart to demote `k8s.pod.name` and `service.instance.id` from index labels to structured metadata when gathering Pod logs via the Kubernetes API (the `podLogsViaKubernetesApi` feature).

```yaml
# Enable Pod log collection for the cluster. Collects logs from all Pods in both the meta and loki namespaces.
podLogsViaKubernetesApi:
  enabled: true
  # collector must match a name defined under your own top-level collectors: map.
  collector: alloy-singleton
  namespaces:
    - meta
    - loki
  # This is the section that specifies to send k8s.pod.name and service.instance.id to structured metadata
  # instead of promoting them to indexed labels. Format is <key>: <extracted_key>.
  structuredMetadata:
    k8s.pod.name: k8s.pod.name
    service.instance.id: service.instance.id
```

{{< admonition type="note" >}}
Chart version 4.x replaced the old `labelsToKeep` filter list with explicit `labels:` and `annotations:` promotion maps: instead of filtering down a larger generated label set, you opt in to the specific Pod labels and annotations you want copied to log labels. By default, `podLogsViaKubernetesApi` already keeps `k8s.pod.name` and `service.instance.id` out of the indexed labels and sends them as structured metadata; the configuration above shows how to set this explicitly. For the full set of available `labels:`, `annotations:`, and `structuredMetadata:` options, refer to the [feature-pod-logs-via-kubernetes-api documentation](https://github.com/grafana/k8s-monitoring-helm/tree/main/charts/k8s-monitoring/charts/feature-pod-logs-via-kubernetes-api).
{{< /admonition >}}

## Loki configuration example

If you are running Loki in a Docker container or on-premises, here is an example of how to modify your Loki `values.yaml` file to demote `k8s.pod.name` and `service.instance.id` from index labels to structured metadata.

```yaml

distributor:
  otlp_config:
    # List of default otlp resource attributes to be picked as index labels - EDIT TO REMOVE k8s.pod.name AND service.instance.id FROM THE LIST
    # CLI flag: -distributor.otlp.default_resource_attributes_as_index_labels
    default_resource_attributes_as_index_labels: [service.name service.namespace deployment.environment deployment.environment.name cloud.region cloud.availability_zone k8s.cluster.name k8s.namespace.name k8s.container.name container.name k8s.replicaset.name k8s.deployment.name k8s.statefulset.name k8s.daemonset.name k8s.cronjob.name k8s.job.name]

```

Kubernetes example `values.yaml`:

```yaml
loki:
  distributor:
    otlp_config:
    # List of default otlp resource attributes to be picked as index labels - EDIT TO REMOVE k8s.pod.name AND service.instance.id FROM THE LIST
    # CLI flag: -distributor.otlp.default_resource_attributes_as_index_labels
      default_resource_attributes_as_index_labels: [service.name service.namespace deployment.environment deployment.environment.name cloud.region cloud.availability_zone k8s.cluster.name k8s.namespace.name k8s.container.name container.name k8s.replicaset.name k8s.deployment.name k8s.statefulset.name k8s.daemonset.name k8s.cronjob.name k8s.job.name]
```

# What is structured metadata

{{< admonition type="warning" >}}
Structured metadata was added to chunk format V4, which is used if the schema version is greater than or equal to `13`. Structured metadata also requires the `tsdb` index type; it is not supported by the deprecated `boltdb-shipper` store. See [Storage schema](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/schema/) for more details about schema versions and index types.
{{< /admonition >}}

Selecting proper, low cardinality labels is critical to operating and querying Loki effectively. Some metadata, especially infrastructure related metadata, can be difficult to embed in log lines, and is too high cardinality to effectively store as indexed labels (and therefore reducing performance of the index).

Structured metadata is a way to attach metadata to logs without indexing them or including them in the log line content itself. Examples of useful metadata are
kubernetes pod names, process ID's, or any other label that is often used in queries but has high cardinality and is expensive
to extract at query time.

Structured metadata can also be used to query commonly needed metadata from log lines without needing to apply a parser at query time. Large json blobs or a poorly written query using complex regex patterns, for example, come with a high performance cost. Examples of useful metadata include container_IDs or user IDs.

## When to use structured metadata

You should only use structured metadata in the following situations:

- If you are ingesting data in OpenTelemetry format, using Grafana Alloy or an OpenTelemetry Collector. Structured metadata was designed to support native ingestion of OpenTelemetry data.
- If you have high cardinality metadata that should not be used as a label and does not exist in the log line.  Some examples might include `process_id` or `thread_id` or Kubernetes pod names.
- If you are using [Logs Drilldown](https://grafana.com/docs/grafana-cloud/visualizations/simplified-exploration/logs/) to visualize and explore your Loki logs. You must enable `pattern_ingester.enabled`, `allow_structured_metadata`, `volume_enabled`, and `discover_log_levels` in your Loki configuration. For the full setup, refer to [Access or install Grafana Logs Drilldown](https://grafana.com/docs/grafana-cloud/visualizations/simplified-exploration/logs/access/).
- If you are a large-scale customer, who is ingesting more than 75TB of logs a month and are using [Bloom filters](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/bloom-filters/) (deprecated), starting in [Loki 3.3](https://grafana.com/docs/loki/<LOKI_VERSION>/release-notes/v3-3/) Bloom filters now utilize structured metadata.

## Enable or disable structured metadata

You enable structured metadata in the Loki config.yaml file.

```yaml
limits_config:
  allow_structured_metadata: true
```

If you are also using [Logs Drilldown](https://grafana.com/docs/grafana-cloud/visualizations/simplified-exploration/logs/), you need additional settings. The following example enables structured metadata along with the other settings required by Logs Drilldown:

```yaml
pattern_ingester:
  enabled: true
limits_config:
  allow_structured_metadata: true
  volume_enabled: true
  discover_log_levels: true
```

You can disable Structured Metadata by setting `allow_structured_metadata: false` in the `limits_config` section or set the command line argument `-validation.allow-structured-metadata=false`. Note structured metadata is required to support ingesting OTLP data.

## Attaching structured metadata to log lines

You have the option to attach structured metadata to log lines in the push payload along with each log line and the timestamp.
For more information on how to push logs to Loki via the HTTP endpoint, refer to the [HTTP API documentation](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/api/#ingest-logs).

Alternatively, you can use Grafana Alloy to extract and attach structured metadata to your log lines.
See the [Alloy `stage.structured_metadata` block](https://grafana.com/docs/alloy/latest/reference/components/loki/loki.process/#stagestructured_metadata) for more information.

With Loki version 1.2.0, support for structured metadata has been added to the Logstash output plugin. For more information, see [logstash](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/logstash/).

{{< admonition type="warning" >}}
Structured metadata size is taken into account while asserting ingestion rate limiting.
Along with that, there are separate limits on how much structured metadata can be attached per log line.

```yaml
# Maximum size accepted for structured metadata per log line.
# CLI flag: -limits.max-structured-metadata-size
[max_structured_metadata_size: <int> | default = 64KB]

# Maximum number of structured metadata entries per log line.
# CLI flag: -limits.max-structured-metadata-entries-count
[max_structured_metadata_entries_count: <int> | default = 128]
```

Log lines that exceed either limit are rejected by the distributor with an HTTP 400 response, and the discarded entries are counted in the `loki_discarded_samples_total` metric with reason `structured_metadata_too_large` or `structured_metadata_too_many`.
A large OpenTelemetry log attribute such as `exception.stacktrace` can be the cause — refer to [Handle large attributes such as stack traces](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/otel/#handle-large-attributes-such-as-stack-traces) for mitigations.
{{< /admonition >}}

## Querying structured metadata

Structured metadata is extracted automatically for each returned log line and added to the labels returned for the query.
You can use labels of structured metadata to filter log line using a [label filter expression](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#label-filter-expression).

For example, if you have a label `pod` attached to some of your log lines as structured metadata, you can filter log lines using:

```logql
{job="example"} | pod="myservice-abc1234-56789"
```

Of course, you can filter by multiple labels of structured metadata at the same time:

```logql
{job="example"} | pod="myservice-abc1234-56789" | trace_id="0242ac120002"
```

Note that since structured metadata is extracted automatically to the results labels, some metric queries might return an error like `maximum of series (50000) reached for a single query`. You can use the [Keep](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#keep-labels-expression) and [Drop](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#drop-labels-expression) stages to filter out labels that you don't need.
For example:

```logql
count_over_time({job="example"} | trace_id="0242ac120002" | keep job  [5m])
```

# Quickstart to run Loki locally

<!-- INTERACTIVE page intro.md START -->

# Quickstart to run Loki locally

If you want to experiment with Loki, you can run Loki locally using the Docker Compose file that ships with Loki. It runs Loki in the [simple scalable deployment](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/#simple-scalable) mode and includes a sample application to generate logs. Simple Scalable Deployment (SSD) mode is deprecated and scheduled for removal in Loki 4.0; for production deployments, see [deployment modes](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/) for currently recommended options.

The Docker Compose configuration runs the following components, each in its own container:

- **flog**: which generates log lines.
  [flog](https://github.com/mingrammer/flog) is a log generator for common log formats.

- **Grafana Alloy**: which scrapes the log lines from flog, and pushes them to Loki through the gateway.
- **Gateway** (nginx) which receives requests and redirects them to the appropriate container based on the request's URL.
- **Loki read component**: which runs a Query Frontend and a Querier.
- **Loki write component**: which runs a Distributor and an Ingester.
- **Loki backend component**: which runs an Index Gateway, Compactor, Ruler, Bloom Planner (deprecated), Bloom Builder (deprecated), and Bloom Gateway (deprecated).
- **Minio**: which Loki uses to store its index and chunks.
- **Grafana**: which provides visualization of the log lines captured within Loki.

{{< figure max-width="75%" src="/media/docs/loki/get-started-flog-v3.png" caption="Getting started sample application" alt="Getting started sample application" >}}

<!-- INTERACTIVE page intro.md END -->

<!-- INTERACTIVE ignore START -->
## Before you begin

Before you start, you need to have the following installed on your local system:
- Install [Docker](https://docs.docker.com/install)
- Install [Docker Compose](https://docs.docker.com/compose/install)

{{< admonition type="tip" >}}
Alternatively, you can try out this example in our interactive learning environment: [Loki Quickstart Sandbox](https://killercoda.com/grafana-labs/course/loki/loki-quickstart).

It's a fully configured environment with all the dependencies already installed.

![Interactive](/media/docs/loki/loki-ile.svg)

Provide feedback, report bugs, and raise issues in the [Grafana Killercoda repository](https://github.com/grafana/killercoda).
{{< /admonition >}}


<!-- INTERACTIVE ignore END -->

<!-- INTERACTIVE page step1.md START -->

## Install Loki and collecting sample logs

<!-- INTERACTIVE ignore START -->

{{< admonition type="note" >}}
This quickstart assumes you are running Linux.
{{< /admonition >}}

<!-- INTERACTIVE ignore END -->

**To install Loki locally, follow these steps:**

1. Create a directory called `evaluate-loki` for the demo environment.
   Make `evaluate-loki` your current working directory:

   ```bash
   mkdir evaluate-loki
   cd evaluate-loki
   ```

2. Download `loki-config.yaml`, `alloy-local-config.yaml`, and `docker-compose.yaml`:

   <!-- INTERACTIVE ignore START -->
   {{< tabs >}}
   {{< tab-content name="wget" >}}
   ```bash
   wget https://raw.githubusercontent.com/grafana/loki/main/examples/getting-started/loki-config.yaml -O loki-config.yaml
   wget https://raw.githubusercontent.com/grafana/loki/main/examples/getting-started/alloy-local-config.yaml -O alloy-local-config.yaml
   wget https://raw.githubusercontent.com/grafana/loki/main/examples/getting-started/docker-compose.yaml -O docker-compose.yaml
   ```
   {{< /tab-content >}}
   {{< tab-content name="curl" >}}
   ```bash
   curl https://raw.githubusercontent.com/grafana/loki/main/examples/getting-started/loki-config.yaml --output loki-config.yaml
   curl https://raw.githubusercontent.com/grafana/loki/main/examples/getting-started/alloy-local-config.yaml --output alloy-local-config.yaml
   curl https://raw.githubusercontent.com/grafana/loki/main/examples/getting-started/docker-compose.yaml --output docker-compose.yaml
   ```
   {{< /tab-content >}}
   {{< /tabs >}}
   <!-- INTERACTIVE ignore END -->

   {{< docs/ignore >}}
   ```bash
   wget https://raw.githubusercontent.com/grafana/loki/main/examples/getting-started/loki-config.yaml -O loki-config.yaml
   wget https://raw.githubusercontent.com/grafana/loki/main/examples/getting-started/alloy-local-config.yaml -O alloy-local-config.yaml
   wget https://raw.githubusercontent.com/grafana/loki/main/examples/getting-started/docker-compose.yaml -O docker-compose.yaml
   ```
   {{< /docs/ignore >}}

3. Deploy the sample Docker image.

   With `evaluate-loki` as the current working directory, start the demo environment using `docker compose`:

   ```bash
   docker compose up -d
   ```

   At the end of the command, you should see something similar to the following:

   ```console
   ✔ Network evaluate-loki_loki          Created      0.1s
   ✔ Container evaluate-loki-minio-1     Started      0.6s
   ✔ Container evaluate-loki-flog-1      Started      0.6s
   ✔ Container evaluate-loki-backend-1   Started      0.8s
   ✔ Container evaluate-loki-write-1     Started      0.8s
   ✔ Container evaluate-loki-read-1      Started      0.8s
   ✔ Container evaluate-loki-gateway-1   Started      1.1s
   ✔ Container evaluate-loki-grafana-1   Started      1.4s
   ✔ Container evaluate-loki-alloy-1     Started      1.4s
   ```


4. (Optional) Verify that the Loki cluster is up and running.

   - The read component returns `ready` when you browse to [http://localhost:3101/ready](http://localhost:3101/ready).
     The message `Query Frontend not ready: not ready: number of schedulers this worker is connected to is 0` shows until the read component is ready.
   - The write component returns `ready` when you browse to [http://localhost:3102/ready](http://localhost:3102/ready).
     The message `Ingester not ready: waiting for 15s after being ready` shows until the write component is ready.

5. (Optional) Verify that Grafana Alloy is running.
   - You can access the Grafana Alloy UI at [http://localhost:12345](http://localhost:12345).

6. (Optional) You can check all the containers are running by running the following command:

   ```bash
   docker ps -a
   ```


<!-- INTERACTIVE page step1.md END -->

<!-- INTERACTIVE page step2.md START -->

## View your logs in Grafana

After you have collected logs, you will want to view them.
You can view your logs using the command line interface, [LogCLI](/docs/loki/<LOKI_VERSION>/query/logcli/), but the easiest way to view your logs is with Grafana.

1. Use Grafana to query the Loki data source.

   The test environment includes [Grafana](https://grafana.com/docs/grafana/latest/), which you can use to query and observe the sample logs generated by the flog application.

   You can access the Grafana cluster by browsing to [http://localhost:3000](http://localhost:3000).

   The Grafana instance in this demonstration has a Loki [data source](https://grafana.com/docs/grafana/latest/datasources/loki/) already configured.

   {{< figure src="/media/docs/loki/grafana-query-builder-v2.png" caption="Grafana Explore" alt="Grafana Explore" >}}

1. From the Grafana main menu, click the **Explore** icon (1) to open the Explore tab.

   To learn more about Explore, refer to the [Explore](https://grafana.com/docs/grafana/latest/explore/) documentation.

1. From the menu in the dashboard header, select the Loki data source (2).

   This displays the Loki query editor.

   In the query editor you use the Loki query language, [LogQL](https://grafana.com/docs/loki/<LOKI_VERSION>/query/), to query your logs.
   To learn more about the query editor, refer to the [query editor documentation](https://grafana.com/docs/grafana/latest/datasources/loki/query-editor/).

1. The Loki query editor has two modes (3):

   - [Builder mode](https://grafana.com/docs/grafana/latest/datasources/loki/query-editor/#builder-mode), which provides a visual query designer.
   - [Code mode](https://grafana.com/docs/grafana/latest/datasources/loki/query-editor/#code-mode), which provides a feature-rich editor for writing LogQL queries.

   Next we’ll walk through a few simple queries using both the builder and code views.

1. Click **Code** (3) to work in Code mode in the query editor.

   Here are some sample queries to get you started using LogQL.
   These queries assume that you followed the instructions to create a directory called `evaluate-loki`.

   If you installed in a different directory, you’ll need to modify these queries to match your installation directory.

   After copying any of these queries into the query editor, click **Run Query** (4) to execute the query.

   1. View all the log lines which have the container label `evaluate-loki-flog-1`:
      <!-- INTERACTIVE copy START -->
      ```bash
      {container="evaluate-loki-flog-1"}
      ```
      <!-- INTERACTIVE copy END -->
      In Loki, this is a log stream.

      Loki uses [labels](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/) as metadata to describe log streams.

      Loki queries always start with a label selector.
      In the previous query, the label selector is `{container="evaluate-loki-flog-1"}`.

   1. To view all the log lines which have the container label `evaluate-loki-grafana-1`:
      <!-- INTERACTIVE copy START -->
      ```bash
      {container="evaluate-loki-grafana-1"}
      ```
      <!-- INTERACTIVE copy END -->
   1. Find all the log lines in the `{container="evaluate-loki-flog-1"}` stream that contain the string `status`:
      <!-- INTERACTIVE copy START -->
      ```bash
      {container="evaluate-loki-flog-1"} |= `status`
      ```
      <!-- INTERACTIVE copy END -->
   1. Find all the log lines in the `{container="evaluate-loki-flog-1"}` stream where the JSON field `status` has the value `404`:
      <!-- INTERACTIVE copy START -->
      ```bash
      {container="evaluate-loki-flog-1"} | json | status=`404`
      ```
      <!-- INTERACTIVE copy END -->
   1. Calculate the number of logs per second where the JSON field `status` has the value `404`:
      <!-- INTERACTIVE copy START -->
      ```bash
      sum by(container) (rate({container="evaluate-loki-flog-1"} | json | status=`404` [$__auto]))
      ```
      <!-- INTERACTIVE copy END -->
   The final query is a metric query which returns a time series.
   This makes Grafana draw a graph of the results.

   You can change the type of graph for a different view of the data.
   Click **Bars** to view a bar graph of the data.

1. Click the **Builder** tab (3) to return to builder mode in the query editor.
   1. In builder mode, click **Kick start your query** (5).
   1. Expand the **Log query starters** section.
   1. Select the first choice, **Parse log lines with logfmt parser**, by clicking **Use this query**.
   1. On the Explore tab, click **Label browser**, in the dialog select a container and click **Show logs**.

For a thorough introduction to LogQL, refer to the [LogQL reference](https://grafana.com/docs/loki/<LOKI_VERSION>/query/).

## Sample queries (code view)

Here are some more sample queries that you can run using the Flog sample data.

To see all the log lines that flog has generated, enter the LogQL query:
<!-- INTERACTIVE copy START -->
```bash
{container="evaluate-loki-flog-1"}
```
<!-- INTERACTIVE copy END -->
The flog app generates log lines for simulated HTTP requests.

To see all `GET` log lines, enter the LogQL query:
<!-- INTERACTIVE copy START -->
```bash
{container="evaluate-loki-flog-1"} |= "GET"
```
<!-- INTERACTIVE copy END -->
To see all `POST` methods, enter the LogQL query:
<!-- INTERACTIVE copy START -->
```bash
{container="evaluate-loki-flog-1"} |= "POST"
```
<!-- INTERACTIVE copy END -->
To see every log line with a 401 status (unauthorized error), enter the LogQL query:
<!-- INTERACTIVE copy START -->
```bash
{container="evaluate-loki-flog-1"} | json | status="401"
```
<!-- INTERACTIVE copy END -->
To see every log line that doesn't contain the text `401`:
<!-- INTERACTIVE copy START -->
```bash
{container="evaluate-loki-flog-1"} != "401"
```
<!-- INTERACTIVE copy END -->
For more examples, refer to the [query documentation](https://grafana.com/docs/loki/<LOKI_VERSION>/query/query_examples/).

## Loki data source in Grafana

In this example, the Loki data source is already configured in Grafana. This can be seen within the `docker-compose.yaml` file:

```yaml
  grafana:
    image: grafana/grafana:latest
    environment:
      - GF_PATHS_PROVISIONING=/etc/grafana/provisioning
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
    depends_on:
      - gateway
    entrypoint:
      - sh
      - -euc
      - |
        mkdir -p /etc/grafana/provisioning/datasources
        cat <<EOF > /etc/grafana/provisioning/datasources/ds.yaml
        apiVersion: 1
        datasources:
          - name: Loki
            type: loki
            access: proxy
            url: http://gateway:3100
            jsonData:
              httpHeaderName1: "X-Scope-OrgID"
            secureJsonData:
              httpHeaderValue1: "tenant1"
        EOF
        /run.sh
```
Within the entrypoint section, the Loki data source is configured with the following details:
- `Name: Loki` (name of the data source)
- `Type: loki` (type of data source)
- `Access: proxy` (access type)
- `URL: http://gateway:3100` (URL of the Loki data source. Loki uses an nginx gateway to direct traffic to the appropriate component)
- `jsonData.httpHeaderName1: "X-Scope-OrgID"` (header name for the organization ID)
- `secureJsonData.httpHeaderValue1: "tenant1"` (header value for the organization ID)

It is important to note when Loki is configured in any other mode other than monolithic deployment, you are required to pass a tenant ID in the header. Without this, queries will return an authorization error.

<!-- INTERACTIVE page step2.md END -->

<!-- INTERACTIVE page finish.md START -->

## Complete metrics, logs, traces, and profiling example

You have completed the Loki Quickstart demo. So where to go next?

{{< docs/ignore >}}
## Back to docs
Head back to where you started from to continue with the Loki documentation: [Loki documentation](https://grafana.com/docs/loki/latest/get-started/quick-start/).
{{< /docs/ignore >}}

If you would like to run a demonstration environment that includes Mimir, Loki, Tempo, and Grafana, you can use [Introduction to Metrics, Logs, Traces, and Profiling in Grafana](https://github.com/grafana/intro-to-mlt).
It's a self-contained environment for learning about Mimir, Loki, Tempo, and Grafana.

The project includes detailed explanations of each component and annotated configurations for a single-instance deployment.
You can also push the data from the environment to [Grafana Cloud](https://grafana.com/cloud/).

<!-- INTERACTIVE page finish.md END -->

# Loki Tutorial

<!-- INTERACTIVE page intro.md START -->
# Loki Tutorial

This quickstart guide will walk you through deploying Loki in single binary mode (also known as [monolithic mode](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/#monolithic-mode)) using Docker Compose. Grafana Loki is only one component of the Grafana observability stack for logs. In this tutorial we will refer to this stack as the **Loki stack**.

{{< figure max-width="100%" src="/media/docs/loki/getting-started-loki-stack-3.png" caption="Loki stack" alt="Loki stack" >}}

The Loki stack consists of the following components:

* **Alloy**: [Grafana Alloy](https://grafana.com/docs/alloy/latest/) is an open source telemetry collector for metrics, logs, traces, and continuous profiles. In this quickstart guide Grafana Alloy has been configured to tail logs from all Docker containers and forward them to Loki.
* **Loki**: A log aggregation system to store the collected logs. For more information on what Loki is, see the [Loki overview](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/overview/).
* **Grafana**: [Grafana](https://grafana.com/docs/grafana/latest/) is an open-source platform for monitoring and observability. Grafana will be used to query and visualize the logs stored in Loki.

<!-- INTERACTIVE ignore START -->
## Before you begin

Before you start, you need to have the following installed on your local system:

- Install [Docker](https://docs.docker.com/install)
- Install [Docker Compose](https://docs.docker.com/compose/install)

{{< admonition type="tip" >}}
Alternatively, you can try out this example in our interactive learning environment: [Loki Quickstart Sandbox](https://killercoda.com/grafana-labs/course/loki/loki-getting-started-tutorial).

It's a fully configured environment with all the dependencies already installed.

![Interactive](/media/docs/loki/loki-ile.svg)

Provide feedback, report bugs, and raise issues in the [Grafana Killercoda repository](https://github.com/grafana/killercoda).
{{< /admonition >}}
<!-- INTERACTIVE ignore END -->

<!-- INTERACTIVE page intro.md END -->

<!-- INTERACTIVE page step1.md START -->

## Deploy the Loki stack

<!-- INTERACTIVE ignore START -->
{{< admonition type="note" >}}
This quickstart assumes you are running Linux or MacOS. Windows users can follow the same steps using [Windows Subsystem for Linux](https://learn.microsoft.com/en-us/windows/wsl/install).
{{< /admonition >}}
<!-- INTERACTIVE ignore END -->

**To deploy the Loki stack locally, follow these steps:**

1. Clone the Loki fundamentals repository and check out the getting-started branch:

     ```bash
     git clone https://github.com/grafana/loki-fundamentals.git -b getting-started
     ```

1. Change to the `loki-fundamentals` directory:

     ```bash
     cd loki-fundamentals
     ```

1. With `loki-fundamentals` as the current working directory deploy Loki, Alloy, and Grafana using Docker Compose:

     ```bash
     docker compose up -d
     ```

      After running the command, you should see a similar output:

      ```console
       ✔ Container loki-fundamentals-grafana-1  Started  0.3s 
       ✔ Container loki-fundamentals-loki-1     Started  0.3s 
       ✔ Container loki-fundamentals-alloy-1    Started  0.4s
      ```

1. With the Loki stack running, you can now verify each component is up and running:

   * **Alloy**: Open a browser and navigate to [http://localhost:12345/graph](http://localhost:12345/graph). You should see the Alloy UI.
   * **Grafana**: Open a browser and navigate to [http://localhost:3000](http://localhost:3000). You should see the Grafana home page.
   * **Loki**: Open a browser and navigate to [http://localhost:3100/metrics](http://localhost:3100/metrics). You should see the Loki metrics page.

<!-- INTERACTIVE page step1.md END -->

<!-- INTERACTIVE page step2.md START -->

Since Grafana Alloy is configured to tail logs from all Docker containers, Loki should already be receiving logs. The best place to verify log collection is using the Grafana Logs Drilldown feature. To do this, navigate to [http://localhost:3000/drilldown](http://localhost:3000/drilldown). Select **Logs**. You should see the Grafana Logs Drilldown page.

{{< figure max-width="100%" src="/media/docs/loki/get-started-drill-down.png" caption="Grafana Logs Drilldown" alt="Grafana Logs Drilldown" >}}

If you have only the getting started demo deployed in your Docker environment, you should see three containers and their logs; `loki-fundamentals-alloy-1`, `loki-fundamentals-grafana-1` and `loki-fundamentals-loki-1`.  In the `loki-fundamentals-loki-1` container, click **Show Logs**  to drill down into the logs for that container.

{{< figure max-width="100%" src="/media/docs/loki/get-started-drill-down-container.png" caption="Grafana Drilldown Service View" alt="Grafana Drilldown Service View" >}}

We will not cover the rest of the Grafana Logs Drilldown features in this quickstart guide. For more information on how to use the Grafana Logs Drilldown feature, refer to [Get started with Grafana Logs Drilldown](https://grafana.com/docs/grafana/latest/explore/simplified-exploration/logs/get-started/).

<!-- INTERACTIVE page step2.md END -->

<!-- INTERACTIVE page step3.md START -->

## Collect logs from a sample application

Currently, the Loki stack is collecting logs about itself. To provide a more realistic example, you can deploy a sample application that generates logs. The sample application is called **The Carnivorous Greenhouse**, a microservices application that allows users to login and simulate a greenhouse with carnivorous plants to monitor. The application consists of seven services:

- **User Service:** Manages user data and authentication for the application. Such as creating users and logging in.
- **Plant Service:** Manages the creation of new plants and updates other services when a new plant is created.
- **Simulation Service:** Generates sensor data for each plant.
- **WebSocket Service:** Manages the websocket connections for the application.
- **Bug Service:** A service that when enabled, randomly causes services to fail and generate additional logs.
- **Main App:** The main application that ties all the services together.
- **Database:** A PostgreSQL database that stores user and plant data.

The architecture of the application is shown below:

{{< figure max-width="100%" src="/media/docs/loki/get-started-architecture.png" caption="Sample Microservice Architecture" alt="Sample Microservice Architecture" >}}

To deploy the sample application, follow these steps:

1. With `loki-fundamentals` as the current working directory, deploy the sample application using Docker Compose:

     ```bash
     docker compose -f greenhouse/docker-compose-micro.yml up -d --build  
     ```

     {{< admonition type="note" >}}
     This may take a few minutes to complete since the images for the sample application need to be built. Go grab a coffee and come back.
     {{< /admonition >}}

     Once the command completes, you should see a similar output:

     ```console
       ✔ Container greenhouse-websocket_service-1   Started   0.7s 
       ✔ Container greenhouse-db-1                  Started   0.7s 
       ✔ Container greenhouse-user_service-1        Started   0.8s 
       ✔ Container greenhouse-bug_service-1         Started   0.8s 
       ✔ Container greenhouse-plant_service-1       Started   0.8s 
       ✔ Container greenhouse-simulation_service-1  Started   0.7s 
       ✔ Container greenhouse-main_app-1            Started   0.7s
     ```

2. To verify the sample application is running, open a browser and navigate to [http://localhost:5005](http://localhost:5005). You should see the login page for the Carnivorous Greenhouse application.

   Now that the sample application is running, run some actions in the application to generate logs. Here is a list of actions:

   1. **Create a user:** Click **Sign Up** and create a new user. Add a username and password and click **Sign Up**.
   2. **Login:** Use the username and password you created to login. Add the username and password and click **Login**.
   3. **Create a plant:** Once logged in, give your plant a name, select a plant type and click **Add Plant**. Do this a few times if you like.

  Your greenhouse should look something like this:

  {{< figure max-width="100%" src="/media/docs/loki/get-started-greenhouse.png" caption="Greenhouse Dashboard" alt="Greenhouse Dashboard" >}}

  Now that you have generated some logs, you can return to the Grafana Logs Drilldown page [http://localhost:3000/drilldown](http://localhost:3000/drilldown). You should see seven new services such as `greenhouse-main_app-1`, `greenhouse-plant_service-1`, `greenhouse-user_service-1`, etc.
<!-- INTERACTIVE page step3.md END -->

<!-- INTERACTIVE page step4.md START -->

## Querying logs

At this point, you have viewed logs using the Grafana Logs Drilldown feature. In many cases this will provide you with all the information you need. However, we can also manually query Loki to ask more advanced questions about the logs. This can be done via **Grafana Explore**.

1. Open a browser and navigate to [http://localhost:3000](http://localhost:3000) to open Grafana.

1. From the Grafana main menu, click the **Explore** icon (1) to open the Explore tab.

   To learn more about Explore, refer to the [Explore](https://grafana.com/docs/grafana/latest/explore/) documentation.

   {{< figure src="/media/docs/loki/grafana-query-builder-v2.png" caption="Grafana Explore" alt="Grafana Explore" >}}

1. From the menu in the dashboard header, select the Loki data source (2).

   This displays the Loki query editor.

   In the query editor you use the Loki query language, [LogQL](https://grafana.com/docs/loki/<LOKI_VERSION>/query/), to query your logs.
   To learn more about the query editor, refer to the [query editor documentation](https://grafana.com/docs/grafana/latest/datasources/loki/query-editor/).

1. The Loki query editor has two modes (3):

   - [Builder mode](https://grafana.com/docs/grafana/latest/datasources/loki/query-editor/#builder-mode), which provides a visual query designer.
   - [Code mode](https://grafana.com/docs/grafana/latest/datasources/loki/query-editor/#code-mode), which provides a feature-rich editor for writing LogQL queries.

   Next we’ll walk through a few queries using the code view.

1. Click **Code** (3) to work in Code mode in the query editor.

   Here are some sample queries to get you started using LogQL. After copying any of these queries into the query editor, click **Run Query** (4) to execute the query.

   1. View all the log lines which have the `container` label value `greenhouse-main_app-1`:
      <!-- INTERACTIVE copy START -->
      ```logql
      {container="greenhouse-main_app-1"}
      ```
      <!-- INTERACTIVE copy END -->
      In Loki, this is a log stream.

      Loki uses [labels](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/) as metadata to describe log streams.

      Loki queries always start with a label selector.
      In the previous query, the label selector is `{container="greenhouse-main_app-1"}`.

      <!-- INTERACTIVE copy END -->
   2. Find all the log lines in the `{container="greenhouse-main_app-1"}` stream that contain the string `POST`:
      <!-- INTERACTIVE copy START -->
      ```logql
      {container="greenhouse-main_app-1"} |= "POST"
      ```
      <!-- INTERACTIVE copy END -->

<!-- INTERACTIVE page step4.md END -->

<!-- INTERACTIVE page step5.md START -->

### Extracting attributes from logs

Loki by design does not force log lines into a specific schema format. Whether you are using JSON, key-value pairs, plain text, Logfmt, or any other format, Loki ingests these logs lines as a stream of characters. The sample application we are using stores logs in [Logfmt](https://brandur.org/logfmt) format:
<!-- INTERACTIVE copy START -->
```bash
ts=2025-02-21 16:09:42,176 level=INFO line=97 msg="192.168.65.1 - - [21/Feb/2025 16:09:42] "GET /static/style.css HTTP/1.1" 304 -"
```

To break this down:

- `ts=2025-02-21 16:09:42,176` is the timestamp of the log line.
- `level=INFO` is the log level.
- `line=97` is the line number in the code.
- `msg="192.168.65.1 - - [21/Feb/2025 16:09:42] "GET /static/style.css HTTP/1.1" 304 -"` is the log message.

<!-- INTERACTIVE copy END -->
When querying Loki, you can pipe the result of the label selector through a parser. This extracts attributes from the log line for further processing. For example, lets pipe `{container="greenhouse-main_app-1"}` through the `logfmt` parser to extract the `level` and `line` attributes:
<!-- INTERACTIVE copy START -->
```logql
{container="greenhouse-main_app-1"} | logfmt
```
<!-- INTERACTIVE copy END -->
When you now expand a log line in the query result, you will see the extracted attributes.

{{< admonition type="tip" >}}
Before we move on to the next section, let's generate some error logs. To do this, enable the bug service in the sample application. This is done by setting the `Toggle Error Mode` to `On` in the Carnivorous Greenhouse application. This will cause the bug service to randomly cause services to fail.
{{< /admonition >}}

### Advanced and Metrics Queries

With Error Mode enabled the bug service will start causing services to fail, in these next few LogQL examples we will track down some of these errors.  Lets start by parsing the logs to extract the `level` attribute and then filter for logs with a `level` of `ERROR`:
<!-- INTERACTIVE copy START -->
```logql
{container="greenhouse-plant_service-1"} | logfmt | level="ERROR"
```
<!-- INTERACTIVE copy END -->
This query will return all the logs from the `greenhouse-plant_service-1` container that have a `level` attribute of `ERROR`. You can further refine this query by filtering for a specific code line:
<!-- INTERACTIVE copy START -->
```logql
{container="greenhouse-plant_service-1"} | logfmt | level="ERROR", line="58"
```
<!-- INTERACTIVE copy END -->
This query will return all the logs from the `greenhouse-plant_service-1` container that have a `level` attribute of `ERROR` and a `line` attribute of `58`.

LogQL also supports metrics queries. Metrics are useful for abstracting the raw log data aggregating attributes into numeric values. This allows you to utilize more visualization options in Grafana as well as generate alerts on your logs.

For example, you can use a metric query to count the number of logs per second that have a specific attribute:
<!-- INTERACTIVE copy START -->
```logql
sum(rate({container="greenhouse-plant_service-1"} | logfmt | level="ERROR" [$__auto]))
```
<!-- INTERACTIVE copy END -->
It worth changing the visualization from `lines` to `bars` to visualize the error rate over time since the error count is quite low.

Another example is to get the top 10 services producing the highest rate of errors:
<!-- INTERACTIVE copy START -->
```logql
topk(10, sum(rate({env="production"} | logfmt | level="ERROR" [5m])) by (service_name))
```
<!-- INTERACTIVE copy END -->
{{< admonition type="note" >}}
`service_name` is a label created by Loki when no service name is provided in the log line. It will use the container name as the service name. A list of all automatically generated labels can be found in [Labels](https://grafana.com/docs/loki/latest/get-started/labels/#default-labels-for-all-users).
{{< /admonition >}}

Finally, lets take a look at the total log throughput of each container in our production environment:
<!-- INTERACTIVE copy START -->
```logql
sum by (service_name) (rate({env="production"} | logfmt [$__auto]))
```
<!-- INTERACTIVE copy END -->
This is made possible by the `service_name` label and the `env` label that we have added to our log lines. Note that `env` is a static label that we added to all log lines as they are processed by Alloy.

<!-- INTERACTIVE page step5.md END -->

<!-- INTERACTIVE page step6.md START -->

## A look under the hood

At this point you will have a running Loki stack and a sample application generating logs. You have also queried Loki using Grafana Logs Drilldown and Grafana Explore.
In this next section we will take a look under the hood to understand how the Loki stack has been configured to collect logs, the Loki configuration file, and how the Loki data source has been configured in Grafana.

### Grafana Alloy configuration

Grafana Alloy is collecting logs from all the Docker containers and forwarding them to Loki.
It needs a configuration file to know which logs to collect and where to forward them to. Within the `loki-fundamentals` directory, you will find a file called `config.alloy`:

```alloy
// This component is responsible for discovering new containers within the Docker environment
discovery.docker "getting_started" {
	host             = "unix:///var/run/docker.sock"
	refresh_interval = "5s"
}

// This component is responsible for relabeling the discovered containers
discovery.relabel "getting_started" {
	targets = []

	rule {
		source_labels = ["__meta_docker_container_name"]
		regex         = "/(.*)"
		target_label  = "container"
	}
}

// This component is responsible for collecting logs from the discovered containers
loki.source.docker "getting_started" {
	host             = "unix:///var/run/docker.sock"
	targets          = discovery.docker.getting_started.targets
	forward_to       = [loki.process.getting_started.receiver]
	relabel_rules    = discovery.relabel.getting_started.rules
	refresh_interval = "5s"
}

// This component is responsible for processing the logs (In this case adding static labels)
loki.process "getting_started" {
    stage.static_labels {
    values = {
      env = "production",
    }
}
    forward_to = [loki.write.getting_started.receiver]
}

// This component is responsible for writing the logs to Loki
loki.write "getting_started" {
	endpoint {
		url  = "http://loki:3100/loki/api/v1/push"
	}
}

// Enables the ability to view logs in the Alloy UI in realtime
livedebugging {
  enabled = true
}
```

This configuration file can be viewed visually via the Alloy UI at [http://localhost:12345/graph](http://localhost:12345/graph).

{{< figure max-width="100%" src="/media/docs/loki/getting-started-alloy-ui.png" caption="Alloy UI" alt="Alloy UI" >}}

In this view you can see the components of the Alloy configuration file and how they are connected:

* **discovery.docker**: This component queries the metadata of the Docker environment via the Docker socket and discovers new containers, as well as providing metadata about the containers.
* **discovery.relabel**: This component converts a metadata (`__meta_docker_container_name`) label into a Loki label (`container`).
* **loki.source.docker**: This component collects logs from the discovered containers and forwards them to the next component. It requests the metadata from the `discovery.docker` component and applies the relabeling rules from the `discovery.relabel` component.
* **loki.process**: This component provides stages for log transformation and extraction. In this case it adds a static label `env=production` to all logs.
* **loki.write**: This component writes the logs to Loki. It forwards the logs to the Loki endpoint `http://loki:3100/loki/api/v1/push`.

### View Logs in realtime

Grafana Alloy provides a built-in real time log viewer. This allows you to view current log entries and how they are being transformed via specific components of the pipeline.
To view live debugging mode open a browser tab and navigate to: [http://localhost:12345/debug/loki.process.getting_started](http://localhost:12345/debug/loki.process.getting_started).
<!-- INTERACTIVE page step6.md END -->
<!-- INTERACTIVE page step7.md START -->

## Loki Configuration

Grafana Loki requires a configuration file to define how it should run. Within the `loki-fundamentals` directory, you will find a file called `loki-config.yaml`:

```yaml
auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9096
  log_level: info
  grpc_server_max_concurrent_streams: 1000

common:
  instance_addr: 127.0.0.1
  path_prefix: /tmp/loki
  storage:
    filesystem:
      chunks_directory: /tmp/loki/chunks
      rules_directory: /tmp/loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory

query_range:
  results_cache:
    cache:
      embedded_cache:
        enabled: true
        max_size_mb: 100

limits_config:
  metric_aggregation_enabled: true
  allow_structured_metadata: true
  volume_enabled: true
  retention_period: 24h   # 24h

schema_config:
  configs:
    - from: 2020-10-24
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

pattern_ingester:
  enabled: true
  metric_aggregation:
    loki_address: localhost:3100

ruler:
  enable_alertmanager_discovery: true
  enable_api: true
  
frontend:
  encoding: protobuf

compactor:
  working_directory: /tmp/loki/retention
  delete_request_store: filesystem
  retention_enabled: true
```

To summarize the configuration file:

* **auth_enabled**: This is set to `false`, meaning Loki does not need a [tenant ID](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/multi-tenancy/) for ingest or query. Note that this is not recommended for production environments. When deploying the Loki Helm chart, this is set to `true` by default.
* **server**: Defines the ports Loki listens on, the log level, and the maximum number of concurrent gRPC streams.
* **common**:  Defines the common configuration for Loki. This includes the instance address, storage configuration, replication factor, and ring configuration.
* **query_range**: This is configured to tell Loki to use inbuilt caching for query results. In production environments of Loki this is handled by a separate cache service such as memcached.
* **limits_config**: Defines the global limits for all Loki tenants. This includes enabling specific features such as metric aggregation and structured metadata. Limits can be defined on a per tenant basis, however this is considered an advanced configuration and for most use cases the global limits are sufficient.
* **schema_config**: Defines the schema configuration for Loki. This includes the schema version, the object store, and the index configuration.
* **pattern_ingester**: Enables pattern ingesters which are used to discover log patterns. Mostly used by Grafana Logs Drilldown.
* **ruler**: Enables the ruler component of Loki. This is used to create alerts based on log queries.
* **frontend**: Defines the encoding format for the frontend. In this case it is set to `protobuf`.
* **compactor**: Defines the compactor configuration. Used to compact the index and manage chunk retention.

The above configuration file is a basic configuration file for Loki. For more advanced configuration options, refer to the [Loki Configuration](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/) documentation.

<!-- INTERACTIVE page step7.md END -->

<!-- INTERACTIVE page step8.md START -->

### Grafana Loki Data source

The final piece of the puzzle is the Grafana Loki data source. This is used by Grafana to connect to Loki and query the logs. Grafana has multiple ways to define a data source:

* **Direct**: This is where you define the data source in the Grafana UI.
* **Provisioning**: This is where you define the data source in a configuration file and have Grafana automatically create the data source.
* **API**: This is where you use the Grafana API to create the data source.

In this case we are using the provisioning method. Instead of mounting the Grafana configuration directory, we have defined the data source in this portion of the `docker-compose.yml` file:

```yaml
  grafana:
    image: grafana/grafana:latest
    environment:
      - GF_FEATURE_TOGGLES_ENABLE=grafanaManagedRecordingRules
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_BASIC_ENABLED=false
    ports:
      - 3000:3000/tcp
    entrypoint:
      - sh
      - -euc
      - |
         mkdir -p /etc/grafana/provisioning/datasources
         cat <<EOF > /etc/grafana/provisioning/datasources/ds.yaml
         apiVersion: 1
         datasources:
         - name: Loki
           type: loki
           access: proxy
           orgId: 1
           url: 'http://loki:3100'
           basicAuth: false
           isDefault: true
           version: 1
           editable: true 
         EOF
         /run.sh
    networks:
      - loki
```

The entrypoint overrides the default startup command to first create the datasource provisioning file `ds.yaml`, which configures Loki as the default datasource. It then calls `/run.sh`, the default Grafana startup script included in the `grafana/grafana` Docker image, which starts the Grafana server. Since Loki is running in the same Docker network as Grafana, the datasource URL uses the service name `loki`.

<!-- INTERACTIVE page step8.md END -->

<!-- INTERACTIVE page finish.md START -->

## What next?

{{< docs/ignore >}}
### Back to docs

Head back to where you started from to continue with the Loki documentation: [Loki documentation](https://grafana.com/docs/loki/latest/get-started/quick-start/).
{{< /docs/ignore >}}

You have completed the Loki Quickstart demo. So where to go next? Here are a few suggestions:

* **Deploy:** Loki can be deployed in multiple ways. For production use cases we recommend deploying Loki via the [Helm chart](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/).
* **Send Logs:** In this example we used Grafana Alloy to collect and send logs to Loki. However there are many other methods you can use depending upon your needs. For more information see [send data](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/).
* **Query Logs:** LogQL is an extensive query language for logs and contains many tools to improve log retrieval and generate insights. For more information see the [Query section](https://grafana.com/docs/loki/<LOKI_VERSION>/query/).
* **Alert:** Lastly you can use the ruler component of Loki to create alerts based on log queries. For more information see [Alerting](https://grafana.com/docs/loki/<LOKI_VERSION>/alert/).

### Complete metrics, logs, traces, and profiling example

If you would like to run a demonstration environment that includes Mimir, Loki, Tempo, and Grafana, you can use [Introduction to Metrics, Logs, Traces, and Profiling in Grafana](https://github.com/grafana/intro-to-mlt).
It's a self-contained environment for learning about Mimir, Loki, Tempo, and Grafana.

The project includes detailed explanations of each component and annotated configurations for a single-instance deployment.
You can also push the data from the environment to [Grafana Cloud](https://grafana.com/cloud/).

<!-- INTERACTIVE page finish.md END -->

# Operations

# Manage authentication

Grafana Loki does not come with any included authentication layer. You must run an authenticating reverse proxy in front of your services.

The simple scalable and microservices [deployment modes](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/) require a reverse proxy to be deployed in front of Loki, to direct client API requests to the various components.

By default the Loki Helm chart includes a default reverse proxy configuration, using an nginx container to handle routing traffic and authorization.

A list of open-source reverse proxies you can use:

- [HAProxy](https://docs.haproxy.org/ )
- [nginx](https://docs.nginx.com/nginx/) using their [guide on restricting access with HTTP basic authentication](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/)
- [OAuth2 proxy](https://oauth2-proxy.github.io/oauth2-proxy/)
- [Pomerium](https://www.pomerium.com/docs), which has a [guide for securing Grafana](https://www.pomerium.com/docs/guides/grafana)

{{< admonition type="note" >}}
Loki runs in multi-tenant mode by default (`auth_enabled: true`). In this mode, Loki requires the HTTP header
`X-Scope-OrgID` to be set to a string identifying the tenant on every request; the responsibility
of populating this value should be handled by the authenticating reverse proxy.
A request that does not include this header fails with an HTTP `401 Unauthorized` error and the message `no org id`.
To learn how to troubleshoot this error, refer to [No org ID](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/troubleshooting/troubleshoot-operations/#error-no-org-id).
For more information, read the [multi-tenancy](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/multi-tenancy/) documentation.{{< /admonition >}}

For information on configuring authentication for your log shipping agent, see the [Grafana Alloy documentation](https://grafana.com/docs/alloy/latest/).

## Mutual TLS

Loki's HTTP and gRPC servers can require clients to present a valid TLS certificate before a connection is accepted. This is a form of client authentication that Loki supports natively, without a reverse proxy.

Mutual TLS (mTLS) authenticates the client at the transport level. It does not populate `X-Scope-OrgID`, so you still need a reverse proxy, your log shipping agent, or your client to set that header when `auth_enabled: true`.

To require client certificates, you must first enable TLS on the server by setting a certificate and a key, then set a client authentication policy and the certificate authority (CA) that signs your client certificates. For example:

```yaml
server:
  http_tls_config:
    cert_file: /etc/loki/certs/server.crt
    key_file: /etc/loki/certs/server.key
    # Reject any client that does not present a certificate signed by this CA.
    client_auth_type: RequireAndVerifyClientCert
    client_ca_file: /etc/loki/certs/ca.crt
```

Use `client_ca_file` for the path to a CA certificate file, or `client_ca` to include the certificate contents directly in the configuration file. The `grpc_tls_config` block accepts the same options for the gRPC server. The equivalent command line flags are `-server.http-tls-client-auth` and `-server.http-tls-ca-path`, plus `-server.grpc-tls-client-auth` and `-server.grpc-tls-ca-path`.

The supported values for `client_auth_type` are:

* `NoClientCert`
* `RequestClientCert`
* `RequireAnyClientCert`
* `VerifyClientCertIfGiven`
* `RequireAndVerifyClientCert`

Use `RequireAndVerifyClientCert` for the strictest setting. It rejects a client that sends no certificate, and it also rejects a certificate that the CA did not sign. The other values check less. For example, `VerifyClientCertIfGiven` allows a client that sends no certificate at all. For the full list of TLS options, refer to the [server block](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#server) of the configuration reference.

## Enable basic authentication for Loki using nginx

This section describes the process of enabling basic authentication for Loki using [nginx](https://docs.nginx.com/nginx/).

### Prerequisites

* A running Loki instance
* A running nginx instance

### Configure nginx

You must create a new nginx configuration file for the Loki instance.

This example assumes the following:

* nginx is running in `/opt/homebrew`
* Loki is running on port 3100 on the local machine
* Your Loki tenant id is `fake`
* The configuration file is named `/opt/homebrew/etc/nginx/loki.conf`

If you used different configuration parameters for Loki, adjust the examples to match your configuration.

`loki.conf` configuration:

```conf
upstream loki {
  server 127.0.0.1:3100;
  keepalive 15;
}

server {
  listen 80;
  server_name loki.localhost;

  auth_basic "loki auth";
  auth_basic_user_file /opt/homebrew/etc/nginx/passwords;

  location / {
    proxy_read_timeout 1800s;
    proxy_connect_timeout 1600s;
    proxy_pass http://loki;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Keep-Alive";
    proxy_set_header Proxy-Connection "Keep-Alive";
    proxy_redirect off;
  }

  location /ready {
    proxy_pass http://loki;
    proxy_http_version 1.1;
    proxy_set_header Connection "Keep-Alive";
    proxy_set_header Proxy-Connection "Keep-Alive";
    proxy_redirect off;
    auth_basic "off";
  }
}
```

{{< admonition type="tip" >}}
This example expects the client to send the `X-Scope-OrgID` header itself, in addition to the basic authentication credentials. If you run Loki with `auth_enabled: true`, you can instead have nginx set the tenant ID from the authenticated username. Add this line to the `location / {}` block:

```conf
proxy_set_header X-Scope-OrgID $remote_user;
```

With this line, nginx overwrites any tenant header sent by the client, so a client cannot read or write the data of another tenant. This requires each username in your password file to match a Loki tenant ID. The Loki Helm chart's gateway uses this same pattern.{{< /admonition >}}

This configuration must be included in your main nginx configuration, for example, by including it in `nginx.conf` like:

```yaml
include /opt/homebrew/etc/nginx/loki.conf;
```

Restart the nginx server to ensure all configuration changes are updated.

### Validate your nginx configuration

To validate the nginx configuration for Loki, you can send a `curl` request to two endpoints:

* The `/ready` endpoint, which is not protected by a basic authentication mechanism.

```curl
% curl -i http://loki.localhost/ready

HTTP/1.1 200 OK
Server: nginx/1.29.2
Date: Thu, 16 Oct 2025 14:28:31 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 6
Connection: keep-alive
X-Content-Type-Options: nosniff

ready
```

* The `/` endpoint, which is protected by a basic authentication mechanism.

```curl
curl -i http://loki.localhost/

HTTP/1.1 401 Unauthorized
Server: nginx/1.29.2
Date: Thu, 16 Oct 2025 14:32:43 GMT
Content-Type: text/html
Content-Length: 179
Connection: keep-alive
WWW-Authenticate: Basic realm="loki auth"

<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.29.2</center>
</body>
</html>
```

### Update passwords

The password file can be seeded using whatever mechanism you may use for other web services.

In this example, `htpasswd` is utilized:

```
% htpasswd -c /opt/homebrew/etc/nginx/passwords loki123

New password:
Re-type new password:
Adding password for user loki123
```

Restart the nginx server to ensure all configuration changes are updated.

### Validate passwords

Enter your password into a temporary file, such as:

```
% vi lokipw
```

Then, store it as an environment variable::

```
% pass=$(cat lokipw)
```

You can validate basic authentication is then working by issuing a curl command to the protected resource:

```curl
curl -i -u loki123:$pass -H "X-Scope-OrgID:fake" "http://loki.localhost/loki/api/v1/labels"

HTTP/1.1 200 OK
Server: nginx/1.29.2
Date: Thu, 16 Oct 2025 14:46:09 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 21
Connection: keep-alive

{"status":"success"}
```

# Manage large volume log streams with automatic stream sharding

Automatic stream sharding can keep streams under a `desired_rate` by adding new labels and values to
existing streams. When properly tuned, this can eliminate issues where log producers are rate limited due to the
per-stream rate limit.

Automatic stream sharding is enabled by default (`shard_streams.enabled` defaults to `true`). To tune it or confirm it's on:

1. Check or set the global [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config) in the Loki configuration file:

   ```yaml
   limits_config:
     shard_streams:
       enabled: true
   ```

1. Optionally lower the `desired_rate` in bytes if you find that the system is still hitting the `per_stream_rate_limit`:

   ```yaml
   limits_config:
     shard_streams:
       enabled: true
       desired_rate: 2097152 #2MiB
   ```

   The `desired_rate` defaults to `1536KB` (1.5MB), which is below the default `per_stream_rate_limit` of `3MB`. This gives Loki some headroom to shard a stream before it hits the per-stream rate limit.

1. Optionally enable `time_sharding_enabled` if you need to ingest old, out-of-order logs, for example during a backfill:

   ```yaml
   limits_config:
     shard_streams:
       enabled: true
       time_sharding_enabled: true
       time_sharding_ignore_recent: 40m
   ```

   Time-based sharding adds a `__time_shard__` label to streams, splitting log entries into buckets based on their timestamp. Log entries with a timestamp newer than `time_sharding_ignore_recent` (40 minutes by default) are still ingested, but Loki does not add the `__time_shard__` label to them. This lets very old logs be ingested without triggering out-of-order errors. Refer to [How automatic stream sharding works](#how-automatic-stream-sharding-works) for more detail.

1. Optionally enable `logging_enabled` for debugging stream sharding.
  {{< admonition type="note" >}}
  This may affect the ingestion performance of Loki.
  {{< /admonition >}}

   ```yaml
   limits_config:
     shard_streams:
       enabled: true
       logging_enabled: true
   ```

## When to use automatic stream sharding

Large log streams present several problems for Loki, namely increased and uneven resource usage on Ingesters and
Distributors. The general recommendation is to explore existing log streams for additional label values that are both
useful for querying and sufficiently low cardinality. There are many cases, however, where no more labels can
be extracted, or cardinality for a label is dangerously large. To protect itself from such volume leading to operational failure, Loki implements per-stream rate limits;
but the result is that some data is lost. The per-stream limit also needs human intervention to change, which is not ideal when log volumes increase and decrease.

Loki uses automatic stream sharding to avoid rate limiting and large streams for any log stream by ensuring it is close
to a configured `desired_rate`.

## How automatic stream sharding works

Loki supports two independent sharding mechanisms, rate-based sharding and time-based sharding. You can enable either one on its own, or enable both together.

### Rate-based sharding

Rate-based sharding works by adding a new label, `__stream_shard__`, to streams and incrementing its value to try
and keep all streams below a configured `desired_rate`.

The feature adds a new API to Ingesters that reports the size of all existing log streams. Once per second, Distributors
query the API to get a picture of all stream rates in the system. Distributors use the existing stream-rate data and a
configured `desired_rate` to determine how many shards a given stream should have. The desired number of new log streams
are created with the label `__stream_shard__` and logs are divided evenly among the streams.

The once-per-second query interval is a Distributor setting, not a `shard_streams` limit. Use the `-distributor.rate-store.stream-rate-update-interval` flag, or the equivalent `rate_store` block in the Distributor configuration, to change how often Distributors refresh their view of stream rates.

Because rate-based sharding is reactive and relies on successive calls to Ingesters, the view of current rates is
always somewhat behind. As a result, the actual size of sharded streams will always be higher than the `desired_rate`.
In practice, this is still sufficient to keep log producers from being rate limited by per-stream rate limits.

### Time-based sharding

Time-based sharding works by adding a different label, `__time_shard__`, to streams. Loki calculates the value of this label from each log entry's timestamp, divided into buckets of `max_chunk_age`/2. This lets Loki accept out-of-order log entries that are older than would normally be allowed, because each time bucket becomes its own stream instead of extending an existing one.

Loki does not apply time-based sharding to log entries with a timestamp newer than the configured `time_sharding_ignore_recent` value. Those entries are still ingested, but they keep their original labels, so that recent logs are not needlessly split into extra streams.

If you enable both rate-based sharding and time-based sharding, Loki applies time-based sharding first, and then applies rate-based sharding to each of the resulting time-sharded streams.

## Automatic stream sharding metrics

Use these metrics to help tune Loki so that it is sharding streams aggressively enough to avoid the per-stream rate
limit:

- `loki_rate_store_refresh_failures_total`: The total number of failed attempts to refresh the distributor's view of
  stream rates.
- `loki_rate_store_streams`: The number of unique streams reported by all Ingesters. Sharded streams are reported as if
  they were unsharded.
- `loki_rate_store_max_stream_shards`: The maximum number of shards for any tenant of the system.
- `loki_rate_store_stream_shards`: A histogram of the distribution of shard counts across all streams.
- `loki_rate_store_max_stream_rate_bytes`: The maximum stream size in bytes/second for any tenant of the system. Sharded
  streams are reported as if they are unsharded.
- `loki_rate_store_max_unique_stream_rate_bytes`: The maximum size of any stream across all tenants. Stream shards are
  individually reported.
- `loki_rate_store_stream_rate_bytes`: A histogram of the distribution of stream sizes across all tenants in
  bytes/second.
- `loki_stream_sharding_count`: The total number of times that streams have been sharded. Useful for calculating the
  sharding rate.

# Manage varying workloads at scale with autoscaling queriers

A microservices deployment of a Loki cluster that runs on Kubernetes typically handles a
workload that varies throughout the day.
To make Loki easier to operate and optimize the cost of running Loki at scale,
we have designed a set of resources to help you autoscale your Loki queriers.

## Prerequisites

You need to run Loki in Kubernetes as a set of microservices. You need to use the query-scheduler.

We recommend using [Kubernetes Event-Driven Autoscaling (KEDA)](https://keda.sh/) to configure autoscaling
based on Prometheus metrics. Refer to [Deploying KEDA](https://keda.sh/docs/latest/deploy) to learn more
about setting up KEDA in your Kubernetes cluster.

## Scaling metric

Because queriers pull queries from the query-scheduler queue and process them on the querier workers, you should scale metrics based on:

- The scheduler queue size.
- The queries running in the queriers.

The query-scheduler exposes the `loki_query_scheduler_inflight_requests` metric.
It tracks the sum of queued queries plus the number of queries currently running in the querier workers.
The following query is useful to scale queriers based on the inflight requests.

```promql
sum(
  max_over_time(
    loki_query_scheduler_inflight_requests{namespace="loki-cluster", quantile="<Q>"}[<R>]
  )
)
```

Use the quantile (Q) and the range (R) parameters to fine-tune the metric.
The higher Q is, the more sensitive the metric is to short-lasting spikes.
As R increases, you can reduce the variation over time in the metric.
A higher R-value helps avoid the autoscaler from modifying the number of replicas too frequently.

In our experience, we have found that a Q of 0.75 and an R of 2 minutes work well.
You can adjust these values according to your workload.

`loki_query_scheduler_inflight_requests` only tracks a fixed set of quantiles: `0.5`, `0.75`, `0.8`, `0.9`, `0.95`, and `0.99`.
If you query the metric with any other quantile value, Prometheus returns no data.
Choose Q from this list.

The metric already smooths its value over a 60 second sliding window before Prometheus scrapes it.
Because of this, an R value below about one minute adds little extra smoothing.
We recommend keeping R at one minute or higher.

## Cluster capacity planning

To scale the Loki queries, you configure the following settings:

- The threshold for scaling up and down
- The scale down stabilization period
- The minimum and the maximum number of queriers

Querier workers process queries from the queue. You can configure each Loki querier to run several workers with the
`-querier.max-concurrent` flag (`max_concurrent` in YAML configuration), which defaults to `4`.
To reserve workforce headroom to address workload spikes, our recommendation is not to use more than 75% of the workers.
For example, if you configure the Loki queriers to run 6 workers, set a threshold of `floor(0.75 * 6) = 4`.

`-querier.max-concurrent` sets the total number of workers for a querier, but the querier splits that total across its connections to query-schedulers (or query-frontends).
If the configured value is lower than the number of query-schedulers a querier connects to, each connection still gets at least one worker, so the real worker count can be higher than the configured value.
Check the actual number with the `loki_querier_worker_concurrency` metric before you calculate the threshold.

To determine the minimum number of queries that you should run, run at least one querier and determine the average
number of inflight requests the system processes 75% of the time over seven days. The target utilization of the queries is 75%.
So if we use 6 workers per querier, we will use the following query:

```promql
clamp_min(ceil(
    avg(
        avg_over_time(loki_query_scheduler_inflight_requests{namespace="loki-cluster", quantile="0.75"}[7d])
    ) / scalar(floor(vector(6 * 0.75)))
), 1)
```

The maximum number of queriers to run is equal to the number of queriers required to process all inflight
requests 50% of the time during a seven-day timespan.
As for the previous example, if each querier runs 6 workers, divide the inflight requests by 6.
The resulting query becomes:

```promql
ceil(
    max(
        max_over_time(loki_query_scheduler_inflight_requests{namespace="loki-cluster", quantile="0.5"}[7d])
    ) / 6
)
```

To minimize the scenario where Loki scales up shortly after scaling down, set
a stabilization window for scaling down.

### KEDA configuration

If you deploy Loki with the Helm chart, you don't need to write your own `KEDA ScaledObject`.
The chart can generate one for you through the `querier.kedaAutoscaling` values, which already default to a Prometheus trigger on `loki_query_scheduler_inflight_requests` with a Q of 0.75 and an R of 2 minutes.
Set `querier.kedaAutoscaling.enabled` to `true`, then set `minReplicas` and `maxReplicas` for your workload.
You also need to set `defaults.kedaAutoscaling.prometheusAddress` to the address of your Prometheus server, because it is empty by default and the trigger cannot query the metric without it.
To replace the default trigger, set `querier.kedaAutoscaling.triggers`.
Refer to [Helm chart values](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/reference/#querier) for the full list of values.

If you don't use the Helm chart, or need a trigger the chart doesn't support, follow the example below to write your own `KEDA ScaledObject`.

This [KEDA ScaledObject](https://keda.sh/docs/latest/concepts/scaling-deployments/) example configures autoscaling
for the querier deployment in the `loki-cluster` namespace.
The example shows the minimum number of replicas set to 10 and the maximum number of replicas set to 50.
Because each querier runs 6 workers, aiming to use 75% of those workers, the threshold is set to 4.
The metric is served at `http://prometheus.default:9090/prometheus`. We configure a stabilization window of 30 minutes.

```yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: querier
  namespace: loki-cluster
spec:
  maxReplicaCount: 50
  minReplicaCount: 10
  scaleTargetRef:
    kind: Deployment
    name: querier
  triggers:
  - metadata:
      metricName: querier_autoscaling_metric
      query: sum(max_over_time(loki_query_scheduler_inflight_requests{namespace="loki-cluster", quantile="0.75"}[2m]))
      serverAddress: http://prometheus.default:9090/prometheus
      threshold: "4"
    type: prometheus
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleDown:
          stabilizationWindowSeconds: 1800
```

## Prometheus alerting when at capacity

Because the configured maximum might not be sufficient, a Prometheus alert can identify
when the quantity of queriers has been at its configured maximum for an extended time. The following example specifies three hours (`3h`) as the extended time:

```yaml
name: LokiAutoscalerMaxedOut
expr: kube_horizontalpodautoscaler_status_current_replicas{namespace=~"loki-cluster"} == kube_horizontalpodautoscaler_spec_max_replicas{namespace=~"loki-cluster"}
for: 3h
labels:
  severity: warning
annotations:
  description: HPA {{ $labels.namespace }}/{{ $labels.horizontalpodautoscaler }} has been running at max replicas for longer than 3h; this can indicate underprovisioning.
  summary: HPA has been running at max replicas for an extended time
```

# Block unwanted queries

In certain situations, you may not be able to control the queries being sent to your Loki installation. These queries
may be intentionally or unintentionally expensive to run, and they may affect the overall stability or cost of running
your service.

You can block queries using [per-tenant overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#runtime-configuration-file), like so:

```yaml
overrides:
  "tenant-id":
    blocked_queries:
      # block this query exactly
      - pattern: 'sum(rate({env="prod"}[1m]))'

      # block any query matching this regex pattern 
      - pattern: '.*prod.*'
        regex: true

      # block all metric queries
      - types: metric

      # block any filter or limited queries matching this regex pattern 
      - pattern: '.*prod.*'
        regex: true
        types: filter,limited

      # block any query that matches this query hash
      - hash: 2943214005          # hash of {stream="stdout",pod="loki-canary-9w49x"}
        types: filter,limited

      # block queries originating from specific sources via X-Query-Tags
      # Keys and values are matched case-insensitively.
      - pattern: '.*'             # optional; if pattern and regex are omitted they will default to '.*' and true
        regex: true
        query_tags:
          source: grafana
          feature: beta
```

{{< admonition type="note" >}}
Changes to these configurations **do not require a restart**; they are defined in the [runtime configuration file](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#runtime-configuration-file).
{{< /admonition >}}

The available query types are:

- `metric`: a query with an aggregation, e.g. `sum(rate({env="prod"}[1m]))`
- `filter`: a query with a log filter, e.g. `{env="prod"} |= "error"`
- `limited`: a query without a filter or a metric aggregation

The `hash` option uses a [32-bit FNV-1](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) hash of the query string, represented as a 32-bit unsigned integer.
This can often be easier to use than query strings that are long or require lots of string escaping. A `query_hash` field
is logged with every query request in the `query-frontend` and `querier` logs, for easy reference. Here's an example log line:

```logfmt
level=info ts=2023-03-30T09:08:15.2614555Z caller=metrics.go:152 component=frontend org_id=29 latency=fast 
query="{stream=\"stdout\",pod=\"loki-canary-9w49x\"}" query_hash=2943214005 query_type=limited range_type=range ...
```

{{< admonition type="note" >}}
The order of patterns is preserved, and Loki stops at the first rule whose `pattern` or `hash` matches the query text.
If that rule's `types` or `query_tags` constraints then don't match, the query is **not** blocked, and Loki does not continue checking any later rules, even if a later rule would have matched and blocked the query.

For example, with this configuration:

```yaml
overrides:
  "tenant-id":
    blocked_queries:
      - pattern: '{env="prod"}'
        types: metric
      - pattern: '{env="prod"}'
```

A query for `{env="prod"}` (a `limited` query) matches the first rule's pattern, but not its `types: metric` constraint, so it is not blocked. Loki does not fall through to the second rule, which has no `types` restriction and would otherwise have blocked it. To block a query with more than one rule that share a pattern, combine the constraints into a single rule instead of relying on a later rule as a fallback.
{{< /admonition >}}

## Observing blocked queries

Blocked queries are logged, as well as counted in the `loki_blocked_queries` metric on a per-tenant basis.

When a policy matches by pattern/hash/regex, Loki logs whether the query type and request tags matched that policy:

```logfmt
level=warn msg="query blocker matched with regex policy" user=29 type=metric pattern=".*rate\\(.*\\).*" query="sum(rate({app=\"foo\"}[5m]))" typesMatched=true tagsMatched=false blocked=false
```

If tag constraints fail to match, Loki emits a debug log showing the missing key and the raw header value that was received:

```logfmt
level=debug msg="query blocker tags mismatch: missing or mismatched key" key=feature tagsRaw="Source=grafana,Feature=alpha"
```

## Scope

Query blocking is enforced by the LogQL query engine when it evaluates a query. This covers:

- Range and instant queries sent to the `/loki/api/v1/query` and `/loki/api/v1/query_range` API endpoints.
- [Alerting and recording rules](https://grafana.com/docs/loki/<LOKI_VERSION>/alert/), in both local and remote rule evaluation modes. Remote evaluation sends the rule's query back through the query frontend and querier, so it is blocked the same way as an API query.

Query blocking does **not** apply to:

- Log tailing (the `/loki/api/v1/tail` endpoint). Tailing reads from the ingesters and the store without using the query engine, so no block policy is applied.
- Metadata endpoints, such as `labels`, `series`, `index/stats`, `index/volume`, and `detected_fields`. These endpoints don't run a LogQL expression through the query engine, so there is nothing for the query blocker to match against.

## Tag-based blocking

You can scope a blocked query rule to requests that include specific key=value pairs in the `X-Query-Tags` header.

- Header format: `key=value` pairs separated by commas, for example: `Source=grafana,Feature=beta`.
- Allowed characters are alphanumeric plus space, comma, equals, '@', '.', and '-'. Any other characters are replaced with `_`.
- Parsing keeps only canonical `key=value` tokens; malformed tokens are ignored.
- Matching rules:
  - Keys are matched case-insensitively (the server lowercases keys).
  - Values are matched case-insensitively.
  - All specified `tags:` pairs in the rule must be present in the request to apply the block.

Examples:

```yaml
overrides:
  tenant-a:
    blocked_queries:
      # Block only metric queries from a beta feature flag
      - types: metric
        query_tags:
          feature: beta

      # Combine with regex to narrow scope further
      - pattern: '.*rate\\(.*\\).*'
        regex: true
        query_tags:
          source: grafana
```

### Ruler query tags

When the ruler evaluates alerting and recording rules in remote evaluation mode (`-ruler.evaluation.mode=remote`), it automatically sets `X-Query-Tags` on the query it sends to `source=ruler,rule_name=<rule name>,rule_type=<rule type>`.
You can match on these tags to block or scope a policy to rule evaluations specifically, for example:

```yaml
overrides:
  "tenant-id":
    blocked_queries:
      # block all queries that come from rule evaluation
      - query_tags:
          source: ruler
```

{{< admonition type="note" >}}
The ruler doesn't set `X-Query-Tags` in local evaluation mode, so you can't match on these tags there.

Be careful when you match on `rule_name`, because a rule name that contains a comma or an equals sign doesn't survive tag parsing. A comma ends the value early, so `rule_name=my,rule` is read as `rule_name=my`. An equals sign makes the pair invalid, so `rule_name=a=b` is dropped completely. Refer to [Tag-based blocking](#tag-based-blocking) for the full parsing rules.
{{< /admonition >}}

# Manage bloom filter building and querying (Deprecated)

{{< admonition type="warning" >}}
In Loki and Grafana Enterprise Logs (GEL), Query acceleration using blooms is deprecated and will be removed in a future release. Engineering and on-call support is not available. No SLA is provided. Note that this feature is intended for users who are ingesting more than 75TB of logs a month, as it is designed to accelerate queries against large volumes of logs.
{{< /admonition >}}

Loki leverages [bloom filters](https://en.wikipedia.org/wiki/Bloom_filter) to speed up queries by reducing the amount of data Loki needs to load from the store and iterate through.
Loki is often used to run "needle in a haystack" queries; these are queries where a large number of log lines are searched, but only a few log lines match the query.
Some common use cases are searching all logs tied to a specific trace ID or customer ID.

An example of such queries would be looking for a trace ID on a whole cluster for the past 24 hours:

```logql
{cluster="prod"} | traceID="3c0e3dcd33e7"
```

Without accelerated filtering, Loki downloads all the chunks for all the streams matching `{cluster="prod"}` for the last 24 hours and iterates through each log line in the chunks, checking if the [structured metadata](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/structured-metadata) key `traceID` with value `3c0e3dcd33e7` is present.

With accelerated filtering, Loki is able to skip most of the chunks and only process the ones where we have a statistical confidence that the structured metadata pair might be present.

To learn how to write queries to use bloom filters, refer to [Query acceleration](https://grafana.com/docs/loki/<LOKI_VERSION>/query/query_acceleration).

## Enable bloom filters

{{< admonition type="warning" >}}
Building and querying bloom filters are by design not supported in single binary deployment, it can only be used with distributed microservice mode.
The reason is that bloom filters also come with a relatively high cost for both building and querying the bloom filters that only pays off at large scale deployments.
{{< /admonition >}}

To start building and using blooms you need to:

- Deploy the [Bloom Planner and Builder](#bloom-planner-and-builder) components (as [microservices](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/#microservices-mode) or via the [SSD](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/#simple-scalable) `backend` target) and enable the components in the [Bloom Build config](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#bloom_build).
- Deploy the [Bloom Gateway](#bloom-gateway) component (as a [microservice](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/#microservices-mode) or via the [SSD](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/#simple-scalable) `backend` target) and enable the component in the [Bloom Gateway config](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#bloom_gateway).
- Enable blooms building and filtering for each tenant individually, or for all of them by default.

```yaml
# Configuration block for the bloom creation.
bloom_build:
  enabled: true
  planner:
    planning_interval: 8h
  builder:
    planner_address: bloom-planner.<namespace>.svc.cluster.local:9095

# Configuration block for bloom filtering.
bloom_gateway:
  enabled: true
  client:
    addresses: dnssrvnoa+_bloom-gateway-grpc._tcp.bloom-gateway-headless.<namespace>.svc.cluster.local

# Enable blooms creation and filtering for all tenants by default
# or do it on a per-tenant basis.
limits_config:
  bloom_creation_enabled: true
  bloom_split_series_keyspace_by: 1024
  bloom_gateway_enable_filtering: true
```

For more configuration options refer to the [Bloom Gateway](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#bloom_gateway), [Bloom Build](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#bloom_build) and [per tenant-limits](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config) configuration docs.
We strongly recommend reading the whole documentation for this deprecated feature before using it.

## Bloom Planner and Builder

Building bloom filters from the chunks in the object storage is done by two components: the Bloom Planner and the Bloom
Builder, where the planner creates tasks for bloom building, and sends the tasks to the builders to process and upload the resulting blocks.
Bloom filters are grouped in bloom blocks spanning multiple streams (also known as series) and chunks from a given day.
To learn more about how blocks and metadata files are organized, refer to the [Building blooms](#building-blooms) section below.

The Bloom Planner runs as a single instance and calculates the gaps in fingerprint ranges for a certain time period for a tenant for which bloom filters need to be built.
It dispatches these tasks to the available builders. The planner also applies the [blooms retention](#retention).

{{< admonition type="warning" >}}
Do not run more than one instance of the Bloom Planner.
{{< /admonition >}}

The Bloom Builder is a stateless horizontally scalable component and can be scaled independently of the planner to fulfill the processing demand of the created tasks.

You can find all the configuration options for these components in the [Configure section for the Bloom Builder](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#bloom_build).
Refer to the [Enable bloom filters](#enable-bloom-filters) section above for a configuration snippet enabling this feature.

### Retention

The Bloom Planner applies bloom block retention on object storage. Retention is disabled by default.
When enabled, retention is applied to all tenants. The retention for each tenant is the longest of its [configured](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config) general retention (`retention_period`) and the streams retention (`retention_stream`).

For example, in the following example, tenant A has a bloom retention of 30 days, and tenant B a bloom retention of 40 days for the `{namespace="prod"}` stream.

```yaml
overrides:
    "A":
        retention_period: 30d
    "B":
        retention_period: 30d
        retention_stream:
            - selector: '{namespace="prod"}'
              priority: 1
              period: 40d
```

### Planner and Builder sizing and configuration

The single planner instance runs the planning phase for bloom blocks for each tenant in the given interval and puts the created tasks to an internal task queue.
Builders process tasks sequentially by pulling them from the queue. The amount of builder replicas required to complete all pending tasks before the next planning iteration depends on the value of `-bloom-build.split-keyspace-by`, the number of tenants, and the log volume of the streams.

The maximum block size is configured per tenant via `-bloom-build.max-block-size`.
The actual block size might exceed this limit given that we append streams blooms to the block until the block is larger than the configured maximum size.
The maximum size of a single stream's bloom is configured per tenant via `-bloom-build.max-bloom-size`. A stream whose bloom exceeds this size is discarded rather than added to a block.
Blocks are created in memory and as soon as they are written to the object store they are freed. Chunks and TSDB files are downloaded from the object store to the file system.
We estimate that builders are able to process 4MB worth of data per second per core.

### Planning strategies

The Bloom Planner supports two strategies for splitting the bloom-building work into tasks, configured per tenant via `bloom_planning_strategy`:

- `split_keyspace_by_factor` (default): splits the series keyspace into a fixed number of parts, controlled by `bloom_split_series_keyspace_by`. This is the strategy described in the sizing guidance above.
- `split_by_series_chunks_size`: sizes each task by a target amount of chunk data instead of a fixed split count, controlled by `bloom_task_target_series_chunk_size`.

A few additional per-tenant limits let you tune task scheduling, block encoding, and gateway prefetching:

- `bloom_build_max_builders` limits how many builders can work on a tenant's tasks concurrently (`0` allows unlimited builders).
- `bloom_build_builder_response_timeout` sets how long the planner waits for a builder to finish a task before requeuing it (`0` disables the timeout).
- `bloom_build_task_max_retries` limits how many times a failed task is retried before it is abandoned.
- `bloom_block_encoding` sets the compression algorithm used for bloom block pages.
- `bloom_prefetch_blocks` prefetches blocks on Bloom Gateways as soon as they are built.

Refer to the [per-tenant limits](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config) configuration docs for the full list of flags, defaults, and descriptions.

## Bloom Gateway

Bloom Gateways handle chunks filtering requests from the [index gateway](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/components/#index-gateway).
The service takes a list of chunks and a filtering expression and matches them against the blooms, filtering out those chunks not matching the given filter expression.

This component is horizontally scalable and every instance only owns a subset of the stream fingerprint range for which it performs the filtering.
The sharding of the data is performed on the client side using DNS discovery of the server instances and the [jumphash](https://arxiv.org/abs/1406.2294) algorithm for consistent hashing and even distribution of the stream fingerprints across Bloom Gateway instances.

You can find all the configuration options for this component in the Configure section for the [Bloom Gateways](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#bloom_gateway).
Refer to the [Enable bloom filters](#enable-bloom-filters) section above for a configuration snippet enabling this feature.

### Gateway sizing and configuration

Bloom Gateways use their local file system as a Least Recently Used (LRU) cache for blooms that are downloaded from object storage.
The size of the blooms depend on the ingest volume and number of unique structured metadata key-value pairs, as well as on build settings of the blooms, namely false-positive-rate.
With default settings, bloom filters make up <1% of the raw structured metadata size.

Since reading blooms depends heavily on disk IOPS, Bloom Gateways should make use of multiple, locally attached SSD disks (NVMe) to increase I/O throughput.
Multiple directories on different disk mounts can be specified using the `-bloom.shipper.working-directory` [setting](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#storage_config) when using a comma separated list of mount points, for example:

```yaml
-bloom.shipper.working-directory="/mnt/data0,/mnt/data1,/mnt/data2,/mnt/data3"
```

Bloom Gateways need to deal with relatively large files: the bloom filter blocks.
Even though the binary format of the bloom blocks allows for reading them into memory in smaller pages, the memory consumption depends on the number of pages that are concurrently loaded into memory for processing.
The product of three settings control the maximum amount of bloom data in memory at any given time: `-bloom-gateway.worker-concurrency`, `-bloom-gateway.block-query-concurrency`, and `-bloom.max-query-page-size`.

Example, assuming 4 CPU cores:

```yaml
-bloom-gateway.worker-concurrency=4      // 1x NUM_CORES
-bloom-gateway.block-query-concurrency=8 // 2x NUM_CORES
-bloom.max-query-page-size=64MiB

4 x 8 x 64MiB = 2048MiB
```

Here, the memory requirement for block processing is 2GiB.
To get the minimum requirements for the Bloom Gateways, you need to double the value.

## Building blooms

Bloom filters are built per stream and aggregated together into block files.
Streams are assigned to blocks by their fingerprint, following the same ordering scheme as Loki’s TSDB and sharding calculation.
This gives a data locality benefit when querying as streams in the same shard are likely to be in the same block.

In addition to blocks, builders maintain a list of metadata files containing references to bloom blocks and the
TSDB index files they were built from. Gateways and the planner use these metadata files to discover existing blocks.

Every `-bloom-build.planner.interval`, the planner will load the latest TSDB files for all tenants for which bloom building is enabled, and compares the TSDB files with the latest bloom metadata files.
If there are new TSDB files or any of them have changed, the planner will create a task for the streams and chunks referenced by the TSDB file.

The builder pulls a task from the planner's queue and processes the containing streams and chunks.
For a given stream, the builder will iterate through all the log lines inside its new chunks and build a bloom for the stream.
In case of changes for a previously processed TSDB file, builders will try to reuse blooms from existing blocks instead of building new ones from scratch.
The builder converts structured metadata from each log line of each chunk of a stream and appends the hash of each key, and key-value pair to the bloom, followed by the hashes combined with the chunk identifier.
The first set of hashes allows gateways to skip whole streams, while the latter is for skipping individual chunks.

For example, given structured metadata `foo=bar` in the chunk `c6dj8g`, we append to the stream bloom the following hashes: `hash("foo")`, `hash("foo=bar")`, `hash("c6dj8g" + "foo")` and `hash("c6dj8g" + "foo=bar")`.

## Query sharding

Query acceleration does not just happen while processing chunks, but also happens from the query planning phase where the query frontend applies [query sharding](https://lokidex.com/posts/tsdb/#sharding).
Loki 3.0 introduces a new [per-tenant configuration](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config) flag `tsdb_sharding_strategy` which defaults to computing shards as in previous versions of Loki by using the index stats to come up with the closest power of two that would optimistically divide the data to process in shards of roughly the same size.
Unfortunately, the amount of data each stream has is often unbalanced with the rest, therefore, some shards end up processing more data than others.

Query acceleration introduces a new sharding strategy: `bounded`, which uses blooms to reduce the chunks to be processed right away during the planning phase in the query frontend, as well as evenly distributes the amount of chunks each sharded query will need to process.

# Configure caches to speed up queries

Loki supports caching for query results, chunks, and index lookups to speed up query performance and reduce calls to the storage layer. Memcached is included in the Loki Helm chart and enabled by default for the `chunksCache` and `resultsCache`.
This section describes the recommended Memcached configuration to enable caching for chunks and query results.

## Cache backends

Every cache in Loki (`chunk_cache_config`, `results_cache`, and so on) can use one of three interchangeable backends: Memcached, Redis, or an embedded (in-process) cache. Memcached is the backend used throughout this page and is the recommended choice for production, multi-replica deployments, because it is shared across all instances of a component. Redis is supported as an alternative external cache. The embedded cache stores data in the memory of the Loki process itself, so it isn't shared between replicas; it is best suited to single-binary or small deployments. Configure only one backend per cache. If you configure both Memcached and Redis for the same cache, Loki fails to start.

If you don't configure Memcached or Redis for a cache, Loki automatically enables the embedded cache for it, so that caching still works out of the box. This applies to the chunks cache and to the results caches. It does not apply to the index queries cache or the write dedupe cache, which stay disabled unless you configure them. Because the embedded cache is per-process, this automatic fallback is not a substitute for Memcached or Redis in a deployment with more than one replica.

For more information about which caching backend to use for each cache, refer to [Configure caches](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#cache_config) and [Components](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/components). For help diagnosing cache configuration problems, refer to [Troubleshoot operations](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/troubleshooting/troubleshoot-operations/).

### Results cache

The results cache stores results so that Loki can reuse them on subsequent queries, and it supports negative caching for log queries. It is sometimes called the frontend cache in some configurations. The results cache is consulted by query-frontends. If the cached results are incomplete, the query frontend calculates the required sub-queries and sends them further along to be executed in queriers, then also caches those results.
To orchestrate all of the above, the results cache uses a query hash as the key that is computed and stored in the headers.

The results cache is really a set of six independently configurable caches under `query_range`, one per query type:

| Query type | Enable with | Cache config |
| --- | --- | --- |
| Metric and log queries | `cache_results` | `results_cache` |
| Index-stats queries | `cache_index_stats_results` | `index_stats_results_cache` |
| Volume queries | `cache_volume_results` | `volume_results_cache` |
| Instant metric queries | `cache_instant_metric_results` | `instant_metric_results_cache` |
| Series queries | `cache_series_results` | `series_results_cache` |
| Label queries | `cache_label_results` | `label_results_cache` |

If a per-query-type cache is enabled but its cache config is left empty, Loki falls back to the `results_cache` configuration for that query type. This means you only need to configure `results_cache` to get caching for all query types, and you only need to give a query type its own cache config if you want it to use a different Memcached, Redis, or embedded cache instance. For details of each supported request type, refer to the [Components section](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/components).

The index lookup cache only supports the legacy BoltDB index storage and is configured to be in-memory by default.
Since moving to the TSDB indexes the attached disks/persistent volumes are utilized as cache and in-memory index lookup cache is obsolete.

### Chunks cache

The chunks are cached using the `chunkRef` as the cache key, which is the unique reference to a chunk when it's cut in the Loki ingesters.
The chunk cache is consulted by queriers each time a set of `chunkRef`s are calculated to serve the query, before going to the storage layer.

Query results are significantly smaller compared to chunks. As the Loki cluster gets bigger in ingested volume, the results cache can continue to perform, whereas the chunks cache will need to grow in proportion to demand more memory.
To be able to support the growing needs of a cluster, in 2023 we introduced support for memcached-extstore. Extstore is an additional feature on Memcached which supports attaching SSD disks to memcached pods to maximize their capacity.

Please see this [blog post](https://grafana.com/blog/2023/08/23/how-we-scaled-grafana-cloud-logs-memcached-cluster-to-50tb-and-improved-reliability/) on Loki's experience with memcached-extstore for our SaaS offering, Grafana Cloud.
For more information on how to tune memcached-extstore please consult the open source [memcached documentation](https://docs.memcached.org/advisories/grafanaloki/).

### L2 chunks cache

As an alternative, or a complement, to memcached-extstore, Loki supports a second, "L2" chunk cache tier configured with `chunk_cache_config_l2` and `l2_chunk_cache_handoff`. The `l2_chunk_cache_handoff` setting is an age threshold. Chunks younger than this age are written to and read from the primary (L1) cache that you configure in `chunk_cache_config`. Chunks older than this age are written to and read from the L2 cache instead. A value of `0` disables the L2 cache.

Loki does not copy or move entries between the two tiers. Each chunk is routed to one tier or the other based on its age at the time of the request. On the read path, Loki widens the threshold by 10% so that chunks near the boundary are still looked up in the L1 cache.

This lets you keep a small, fast cache of recent chunks while routing older, less frequently accessed chunks to a larger and cheaper second tier, such as a disk-backed Memcached instance.

```yaml
chunk_store_config:
  l2_chunk_cache_handoff: 24h
  chunk_cache_config_l2:
    memcached:
      batch_size: 256
      parallelism: 10
    memcached_client:
      host: <l2 chunk cache memcached host>
      service: <port name of memcached service>
```

If the Helm chart is used, the L2 cache is configured under `chunksCache.l2`, which is disabled by default (`chunksCache.l2.enabled: false`) and defaults to a four day handoff (`chunksCache.l2.l2ChunkCacheHandoff: 345600s`). The Helm chart's `chunksCache.l2.persistence` settings can attach a persistent volume to the L2 cache pods, which is a chart-native alternative to memcached-extstore for adding disk-backed capacity.

## Before you begin

- It is recommended to deploy separate Memcached type as separate components (`memcached_frontend` and `memcached_chunks`).
- Use the Memcached image version shipped with the current release of the [Loki Helm chart](https://github.com/grafana-community/helm-charts/blob/main/charts/loki/values.yaml) (`memcached.image.tag`) rather than pinning to a specific version here, since the recommended version changes over time.
- Consult the Loki ksonnet [memcached](https://github.com/grafana/loki/blob/main/production/ksonnet/loki/memcached.libsonnet) deployment and the ksonnet [memcached library](https://github.com/grafana/jsonnet-libs/tree/master/memcached).
- Index caching is not required for the [TSDB](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/tsdb/#index-caching-not-required) index format.
- For recommendations on scaling the cache, refer to the [Size the cluster](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/size/) page.

## Steps

To enable and configure Memcached:

1. Deploy each Memcached service with at least three replicas and configure
   each as follows:
    1. Chunk cache

       ```yaml
       --memory-limit=4096 --max-item-size=2m --conn-limit=1024
       ```

    1. Query result cache

       ```yaml
       --memory-limit=1024 --max-item-size=5m --conn-limit=1024
       ```

1. Configure Loki to use the cache.
    1. If the Helm chart is used

       Set `chunksCache.addresses` to the Memcached address for the chunk cache and `resultsCache.addresses` to the Memcached address for the query result cache, then set `chunksCache.enabled=true` and `resultsCache.enabled=true`.

       Ensure that the connection limit of Memcached is at least `number_of_clients * max_idle_conns`.

       By default, the chart also deploys and manages the Memcached servers themselves. To use your own Memcached instances instead, set `memcached.enabled` to `false` to stop the chart from deploying its built-in Memcached server, keep `chunksCache.enabled` and `resultsCache.enabled` set to `true` so that Loki still uses a Memcached-based cache, and point the `addresses` values at your own service. Use the same comma-separated DNS Service Discovery format that the chart uses by default:

       ```yaml
       memcached:
         enabled: false
       chunksCache:
         enabled: true
         addresses: "dnssrvnoa+_memcached-client._tcp.chunk-cache-memcached.loki.svc"
       resultsCache:
         enabled: true
         addresses: "dnssrvnoa+_memcached-client._tcp.results-cache-memcached.loki.svc"
       ```

       The Helm chart's `chunksCache` and `resultsCache` values only manage the chunk cache and the main query-range results cache. The other five results caches (`index_stats_results_cache`, `volume_results_cache`, `instant_metric_results_cache`, `series_results_cache`, and `label_results_cache`) don't have dedicated Helm values. You can still enable and configure them by adding the settings to the chart's `loki.query_range` value, which the chart copies into the Loki configuration file as it is written. For example:

       ```yaml
       loki:
         query_range:
           cache_index_stats_results: true
           cache_volume_results: true
       ```

    1. If the Loki configuration is used, modify the following two sections in
       the Loki configuration file.
        1. Configure the chunk cache

           ```yaml
           chunk_store_config:
             chunk_cache_config:
               memcached:
                 batch_size: 256
                 parallelism: 10
               memcached_client:
                 host: <chunk cache memcached host>
                 service: <port name of memcached service>
           ```

        1. Configure the query result cache

           ```yaml
           query_range:
             cache_results: true
             results_cache:
               cache:
                 memcached_client:
                   consistent_hash: true
                   host: <memcached host>
                   service: <port name of memcached service>
                   max_idle_conns: 16
                   timeout: 200ms
                   update_interval: 1m
           ```

# Kafka-based ingestion (Experimental)

{{< admonition type="warning" >}}
Kafka-based ingestion is an [experimental feature](https://grafana.com/docs/release-life-cycle/). Engineering and on-call support is not available. No SLA is provided. Configuration options and metric names may change in a future release.
{{< /admonition >}}

Grafana Loki can use Apache Kafka, or a Kafka-protocol-compatible system such as [WarpStream](https://www.warpstream.com/), as an optional durable buffer on the write path. This topic explains what Kafka-based ingestion is, when to use it, how it works, and how to configure and monitor it.

## What it is

By default, the [distributor](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/components/#distributor) sends log streams directly to [ingesters](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/components/#ingester) over gRPC. When you enable Kafka-based ingestion, the distributor instead (or also) writes log streams as records to a Kafka topic. Each ingester then reads, or *consumes*, records from one partition of that topic and processes them the same way it processes directly pushed data.

Kafka sits between the distributor and the ingester as a durable, replicated buffer. This decouples the two components so that a slow or restarting ingester doesn't block or fail writes.

Loki uses the [franz-go](https://github.com/twmb/franz-go) Kafka client library. If you need to debug Kafka client behavior in your logs, look for the component name `kafka_client`. Kafka client metrics use the `loki_kafka_client_` prefix.

## When and why to use it

Consider enabling Kafka-based ingestion if you want to:

- Decouple ingestion durability from ingester availability.
- Absorb ingester restarts and rolling updates without blocking or failing writes.
- Automatically replay buffered data from the last committed offset when an ingester restarts, instead of relying only on the ingester's write-ahead log (WAL).
- Scale write throughput independently of ingester replica count, by adding Kafka partitions.

Kafka-based ingestion also underpins the ingest-limits service and the experimental data object builders used for columnar storage. These features require Kafka even if you don't otherwise need the durability benefits above.

If you don't have an existing Kafka deployment and don't need these benefits, you can continue to use the default gRPC write path. Enabling Kafka adds an operational dependency: you must run, or pay for, a Kafka cluster (or a compatible managed service) and monitor it.

## Architecture summary

{{< mermaid >}}
flowchart LR
    A[Distributor] -->|produces records| B[(Kafka topic, partitioned)]
    B -->|partition 0| C1[Ingester A]
    B -->|partition 1| C2[Ingester B]
    B -->|partition N| C3[Ingester N]
    D[Partition ring] -.assigns partition ownership.-> C1
    D -.-> C2
    D -.-> C3
{{< /mermaid >}}

The distributor writes each stream as one or more Kafka records to a topic. The topic has multiple partitions. Each ingester owns exactly one partition and consumes records only from that partition. Ingesters use the [partition ring](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/components/#kafka-based-ingestion-experimental) to coordinate which ingester owns which partition.

Every ingester process always registers in the classic hash ring it uses for gRPC writes, whether or not Kafka-based ingestion is enabled. When you also enable `ingester.kafka_ingestion.enabled`, that same process additionally joins the partition ring. Loki supports two ways to deploy this:

- **A single, shared ingester pool**: the same ingester replicas serve both the classic hash ring and the partition ring at once. This is the simplest setup, and works well if you're experimenting with Kafka-based ingestion or plan to cut over quickly.
- **Two separate ingester pools**: a new, dedicated pool of Kafka-consuming ingesters runs alongside your existing ingesters, isolated from the classic hash ring by giving it a distinct `ingester.ring.kvstore.prefix`. This is the only path Grafana Labs has tested in production, and is the approach documented in [Migrate to Kafka-based ingestion](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/migrate/migrate-to-kafka/).

Because the two rings assign ownership of a tenant's streams differently (token-hash with replication, compared to shuffle-sharded partitions with a single owner), the querier has to be told explicitly which ring to use when it looks up ingesters. See [Read path](#read-path) and `querier.query_partition_ingesters` under [Configuration overview](#configuration-overview).

{{< admonition type="note" >}}
You can use any system that implements the Kafka wire protocol, such as WarpStream, instead of running Apache Kafka yourself. Loki only needs the broker address and, optionally, SASL credentials. No code changes are required.
{{< /admonition >}}

## Read path

Enabling Kafka-based ingestion primarily changes the **write path**. [LogQL](https://grafana.com/docs/loki/<LOKI_VERSION>/query/) queries still read recent data from ingester memory and older data from long-term object storage, the same way they do when Kafka-based ingestion is disabled.

However, the querier must be told which ring to use to find the ingesters that hold recent data: the classic hash ring (the default), or the partition ring that Kafka-based ingestion uses. Set `querier.query_partition_ingesters: true` to switch the querier, and any locally evaluating rulers, over to the partition ring. Until you set this, the querier keeps using the classic ring even if the distributor and ingesters are fully configured for Kafka-based ingestion. For the full migration sequence and when to flip this setting, refer to [Migrate to Kafka-based ingestion](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/migrate/migrate-to-kafka/).

## Configuration overview

This section groups the relevant configuration blocks at a high level. For the full list of fields and their defaults, refer to the [Configuration reference](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-config-ref/).

- **`kafka_config`**: the Kafka client configuration shared by every Loki component that reads or writes Kafka records. It includes the broker addresses (`reader_config.address` and `writer_config.address`), the topic name, SASL credentials, producer tuning (`producer_linger`, `producer_max_inflight_requests_per_broker`, `producer_max_record_size_bytes`), and consumer tuning (`max_consumer_lag_at_startup`, `consumer_group_offset_commit_interval`, `max_consumer_workers`).
- **`distributor.kafka_writes_enabled`** and **`distributor.ingester_writes_enabled`**: control whether the distributor writes to Kafka, to ingesters over gRPC, or both. At least one must be `true`.
- **`ingester.kafka_ingestion`**: controls whether ingesters consume from Kafka, and configures the partition ring key-value store that ingesters, distributors, queriers, and rulers use to coordinate partition ownership.
- **`querier.query_partition_ingesters`**: controls whether the querier, and any ruler evaluating rules locally, looks up ingesters using the partition ring instead of the classic hash ring. Set this to `true` only after the Kafka-consuming ingesters have caught up and held data for at least `querier.query_ingesters_within` (default: 3 hours); otherwise recent queries can return incomplete results. Defaults to `false`.
- **`limits_config.ingestion_partitions_tenant_shard_size`**: the number of Kafka partitions a single tenant's data is shuffle-sharded across. The default, `0`, means a tenant's data uses all partitions.
- **`ingest_limits`**: configuration for the optional ingest-limits service, which uses its own Kafka topic, partition count, and consumer group to track stream metadata.

## Authentication limitations

The only Kafka authentication mechanism Loki supports today is **SASL PLAIN**, configured with `kafka_config.sasl_username` and `kafka_config.sasl_password`. Both fields must be set together, or both left empty.

Loki has no built-in Transport Layer Security (TLS) or mutual TLS (mTLS) configuration for the Kafka connection. If your organization requires an encrypted connection to Kafka, you must terminate TLS at the network layer, for example with a sidecar proxy or a load balancer in front of your Kafka brokers.

## Partition sizing guidance

The number of Kafka partitions in your topic limits how many ingesters can actively consume from it, because each ingester owns exactly one partition.

Keep the following in mind when you choose a partition count:

- `kafka_config.auto_create_topic_default_partitions` defaults to `1000`. This is a cluster-wide Kafka broker setting (equivalent to Kafka's `num.partitions`), not a per-topic setting, and it only takes effect if you let Loki auto-create the topic.
- The partition count sets the maximum number of ingesters that can actively consume from the topic at once. If you have more ingesters than partitions, the extra ingesters have no partition to own and stay idle for Kafka-based ingestion.
- `limits_config.ingestion_partitions_tenant_shard_size` controls how many partitions a single tenant's streams are shuffle-sharded across. The default, `0`, disables shuffle sharding, so a tenant's data spreads across all partitions.
- Start with more partitions than you expect to need. Partitions are inexpensive to create in Kafka, but reducing the partition count of an existing topic later is difficult and usually requires creating a new topic.

## Offset management and restart behavior

When Kafka-based ingestion is enabled, each ingester follows this sequence to safely resume consuming after a restart:

1. On startup, the ingester fetches the last offset it committed for its partition.
1. If no committed offset exists, for example on first startup, the ingester starts from the earliest available offset.
1. The ingester replays Kafka records from that offset until its consumption lag is within `kafka_config.max_consumer_lag_at_startup` (default: 15 seconds).
1. Only after catching up does the ingester mark itself `ACTIVE` in the partition ring and pass its readiness check.
1. During normal operation, the ingester commits its current offset to Kafka every `kafka_config.consumer_group_offset_commit_interval` (default: 1 second).

Because of this sequence, ingester restarts are safe: any data produced to Kafka while an ingester was down is still in the topic, and the ingester automatically replays it after it comes back up.

## Monitoring Kafka ingestion

This is the full list of Kafka-related metrics, organized by component. For the highest-signal subset of these metrics and example alerting queries, refer to [Key metrics for monitoring Loki](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/metrics/#kafka-based-ingestion-experimental).

**Distributor (producer side):**

- `loki_distributor_kafka_appends_total` (labels: `partition`, `status`): total number of Kafka produce attempts.
- `loki_distributor_kafka_latency_seconds`: end-to-end produce latency.
- `loki_distributor_kafka_sent_bytes_total`: bytes written to Kafka.
- `loki_distributor_kafka_records_per_write_request`: how many Kafka records each incoming push request was split into.

**Ingester (consumer side):**

- `loki_ingester_partition_records_batch_process_duration_seconds`: time to process a batch of Kafka records.
- `loki_ingester_partition_current_offset`: the offset the ingester has most recently consumed. Useful for lag alerting.
- `loki_ingester_partition_push_latency_seconds`: latency of the internal push after consuming a record from Kafka.
- `loki_ingester_partition_consume_workers_count`: number of consumer worker goroutines.

**Kafka client internals (reader and writer):**

- `loki_kafka_client_partition_reader_consumption_lag_seconds` (labels: `phase`, `partition_state`): estimated time the reader is behind the latest produced record. Alert if this grows over time.
- `loki_kafka_client_partition_reader_fetch_errors_total`: errors fetching records from Kafka brokers.
- `loki_kafka_client_partition_reader_fetches_total` and `loki_kafka_client_partition_reader_records_per_fetch`: fetch throughput.
- `loki_kafka_client_partition_reader_offset_commit_requests_total` and `loki_kafka_client_partition_reader_offset_commit_failures_total`: health of offset commits to Kafka.
- `loki_kafka_client_produce_write_wait_seconds`, `loki_kafka_client_produce_write_time_seconds`, `loki_kafka_client_produce_read_wait_seconds` and `loki_kafka_client_produce_read_time_seconds` (labels: `node_id`, `host`): native histograms of how long each stage of a Produce request to each broker takes: waiting to be written, writing, waiting for the response, and reading the response.

## Configuration examples

### Dual-write for a safe rollout

Write to both Kafka and ingesters at the same time so you can validate Kafka-based ingestion before relying on it. Queries still use the classic hash ring at this stage (`querier.query_partition_ingesters` stays `false`).

```yaml
distributor:
  kafka_writes_enabled: true
  ingester_writes_enabled: true

ingester:
  kafka_ingestion:
    enabled: true

kafka_config:
  topic: loki-logs
  reader_config:
    address: kafka:9092
  writer_config:
    address: kafka:9092
```

### Kafka-only write path

Once you've validated dual-write and switched queriers to the partition ring, you can disable the direct gRPC writes to ingesters. Refer to [Migrate to Kafka-based ingestion](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/migrate/migrate-to-kafka/) for the full, safely ordered sequence.

```yaml
distributor:
  kafka_writes_enabled: true
  ingester_writes_enabled: false

ingester:
  kafka_ingestion:
    enabled: true

querier:
  query_partition_ingesters: true

kafka_config:
  topic: loki-logs
  reader_config:
    address: kafka:9092
  writer_config:
    address: kafka:9092
```

### Managed Kafka with SASL authentication

This example connects to a managed Kafka-protocol-compatible service, such as WarpStream, using SASL PLAIN authentication and separate reader and writer broker addresses.

```yaml
distributor:
  kafka_writes_enabled: true
  ingester_writes_enabled: true

ingester:
  kafka_ingestion:
    enabled: true

kafka_config:
  topic: loki-logs
  reader_config:
    address: reader.kafka.example.com:9092
  writer_config:
    address: writer.kafka.example.com:9092
  sasl_username: ${KAFKA_SASL_USERNAME}
  sasl_password: ${KAFKA_SASL_PASSWORD}
```

## Links

- [Migrate to Kafka-based ingestion](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/migrate/migrate-to-kafka/)
- [Configuration reference](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-config-ref/)
- [Troubleshoot Kafka integration errors](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/troubleshooting/troubleshoot-operations/#kafka-integration-errors)

# Use label-based access control with Loki

{{< admonition type="note" >}}
This feature is generally available from v4.0. For the latest releases, refer to the [Release notes](https://grafana.com/docs/loki/<LOKI_VERSION>/release-notes/).
{{< /admonition >}}

Label-based access control (LBAC) restricts the logs a tenant can query to those that match one or more [Prometheus label selectors](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors). You associate a set of selectors with a tenant, and queries from that tenant only return data from log streams that match at least one of the selectors. Because selectors can be combined into "OR" groups, where each group is a list of things joined by "AND" you can express any required policy. For example, `(A and B and C) or (D and E) or (F)`.

LBAC builds on Loki's multi-tenancy: policies are scoped per tenant, so you must run Loki in multi-tenant mode. For more information, refer to the [multi-tenancy](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/multi-tenancy/) documentation.

## Why use LBAC

LBAC lets you share a single Loki tenant across multiple teams or users while still limiting what each of them can see. Common reasons to use LBAC include:

- **Restrict a tenant to a subset of its own data.** For example, one team should only see logs from its own Kubernetes namespace, even though the tenant contains logs from many namespaces.
- **Exclude sensitive log streams from some users.** For example, you can hide streams labeled `secret="true"` from most users, while still letting a smaller group of users query them.
- **Enforce access boundaries per tenant without creating a separate Loki tenant for every team.** Running one tenant per team adds operational overhead. LBAC lets you keep a single tenant and instead scope access with label policies.

## Security model

Loki does not include its own authentication layer. Both the tenant (`X-Scope-OrgID`) and the label policy (`X-Prom-Label-Policy`) are read from trusted HTTP headers on incoming requests.

{{< admonition type="warning" >}}
Never expose the Loki API directly to clients. A client that can reach Loki can set the `X-Scope-OrgID` and `X-Prom-Label-Policy` headers itself and bypass label-based access control entirely. You must run an authentication gateway in front of Loki that authenticates the request, sets the tenant, and attaches the correct label policy.
{{< /admonition >}}

The gateway is responsible for stripping any user supplied `X-Scope-OrgID` and `X-Prom-Label-Policy` and replacing them with values it derives from the authenticated identity. One open-source gateway that sets these headers is [db-auth-gateway](https://github.com/grafana/db-auth-gateway/). For general guidance on putting an authenticating reverse proxy in front of Loki, refer to the [authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/) documentation.

## How LBAC works

At a high level, your gateway attaches a label policy to each request, and Loki carries that policy through the read path so that every component filters data to match it.

1. Your authentication gateway authenticates the request and sets the `X-Prom-Label-Policy` HTTP header. Each value in the header has the form `<TENANT>:<URL-ENCODED SELECTOR>`, for example `tenant1:%7Benv%3D%22dev%22%7D` for the selector `{env="dev"}`.
2. An HTTP middleware inside Loki parses this header on every incoming request and stores the resulting policies in the request context.
3. As the request moves between Loki's internal components, for example from the query frontend to the queriers, ingesters, and index gateway, the policies travel with it as gRPC metadata. This means the same policy applies no matter which component ultimately reads the data.
4. Loki enforces the policy at several points in the read path:
   - It filters chunks and streams while running the query, for both the original query engine and the newer query engine.
   - It filters the results of label-values queries against the store and the ingesters.
   - In the query frontend, it filters the response of volume queries, and it rewrites aggregated-metrics queries so they only return permitted data.

The following diagram shows the request flow for a query from a client through the gateway and into Loki:

```mermaid
sequenceDiagram
    participant Client
    participant Gateway as Authentication gateway
    participant QF as Query frontend
    participant Q as Querier / Ingester

    Client->>Gateway: Query request
    Gateway->>Gateway: Authenticate request
    Gateway->>QF: Query request + X-Scope-OrgID + X-Prom-Label-Policy
    QF->>Q: Query request (policy propagated over gRPC)
    Q->>Q: Filter streams and chunks by policy
    Q->>QF: Filtered results
    QF->>Gateway: Filtered response
    Gateway->>Client: Filtered response
```

### LBAC and ingestion

Label-based access control is not enforced on write (push) requests. It only restricts what a tenant can read. A tenant that is allowed to write can push log lines with any labels, regardless of the label policy that applies to its queries.

### LBAC and alertmanager and ruler

Label policies are not enforced by the Alertmanager or ruler HTTP endpoints. This means that the requests they serve contain everything for a particular tenant without applying label-based access control. For example, listing all rule groups in the ruler returns all rule groups for the tenant, even if a label selector in the policy would exclude some of the labels on the rules. However it does apply when you query the metrics those components generate, such as `ALERTS`.

### LBAC and querying

LogQL queries use the following format:

```logql
{ log stream selector } | log pipeline
```

LBAC only applies to the log stream selector portion of a query, that is the part of the query that comes before the pipe (`|`). It does not apply to label filter expressions or other parts of the log pipeline.

For more information, refer to the [Log stream selector](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#log-stream-selector) and [Label filter expression](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#label-filter-expression) sections in the documentation about [Log queries](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/).


## Enable label-based access control

LBAC requires multi-tenant mode, which is the default. Ensure authentication is enabled so that requests carry a tenant:

```yaml
auth_enabled: true
```

Then enable LBAC:

```yaml
lbac:
  enabled: true
```

This can also be set with the `-lbac.enabled` command-line flag. When enabled, Loki parses the `X-Prom-Label-Policy` header on incoming requests and enforces the policies it contains.

## Configuration reference

LBAC currently has a single configuration setting. There are no per-tenant settings in Loki's YAML configuration: policies for each tenant arrive dynamically in the `X-Prom-Label-Policy` header, set by your authentication gateway, not through Loki's configuration file.

```yaml
lbac:
  # Enables label based access control through the X-Prom-Label-Policy header.
  [enabled: <boolean> | default = false]
```

| Setting | CLI flag | Default | Description |
| --- | --- | --- | --- |
| `lbac.enabled` | `-lbac.enabled` | `false` | Enables label-based access control through the `X-Prom-Label-Policy` header. This setting is [experimental](https://grafana.com/docs/release-life-cycle/). |

## Setting up a label policy

A label policy is conveyed to Loki in the `X-Prom-Label-Policy` request header. This header is set by your authentication gateway, not by clients. Each header value has the form:

```http
<TENANT>:<URL-ENCODED SELECTOR>
```

The selector is a standard label matcher set such as `{env="dev"}`, URL-encoded so that it is safe to carry in a header. For example, the policy `{env="dev"}` for tenant `tenant1` is encoded as:

```http
X-Prom-Label-Policy: tenant1:%7Benv%3D%22dev%22%7D
```

To associate multiple selectors with a tenant, set multiple values. You can either repeat the header or separate the encoded policies with a comma (`,`). The selectors are combined with `OR` into groups where each group is a list of things joined by `AND`: a stream is returned if any of the streams matches any one of the selector groups.  For example, a policy written `{a='true', b='true, c='true'} OR {x='true', y='true'}` reads as `{a='true' AND b='true'AND c='true'} OR {x='true'AND y='true'}`.

## Exclude a label

One common use case for an LBAC policy is to exclude logs that have a specific label. For example, to exclude all log lines with the label `secret=true`, use a selector with `secret!="true"`:

```logql
{secret!="true"}
```

## Use multiple selectors

To allow access to both the production and development environments while excluding logs with the label `secret=true` in the production environment, use multiple selectors:

```logql
{secret!="true", env="prod"}
{env="dev"}
```

These selectors enforce the policy as follows:

* `{secret!="true", env="prod"}` matches and returns log lines from the production environment that do not have the `secret: true` label.
* `{env="dev"}` matches and returns log lines from the development environment, even if they have the `secret: true` label.

## Real-world examples

The following examples use labels that Loki assigns by default when logs are collected through OpenTelemetry, such as `service_name`, `k8s_namespace_name`, and `deployment_environment_name`. For the full list of default OpenTelemetry-derived labels, refer to [Default labels](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/#default-labels-for-opentelemetry). Each example shows the LogQL selector and the resulting `X-Prom-Label-Policy` header value that your gateway would set, following the same format as [Setting up a label policy](#setting-up-a-label-policy).

### Restrict a team to one namespace

Scope a tenant so that it can only see logs from the `checkout` Kubernetes namespace. This is useful when a platform team gives a single application team access to a shared tenant, but the application team should only see its own namespace.

Selector:

```logql
{k8s_namespace_name="checkout"}
```

Header:

```http
X-Prom-Label-Policy: team-checkout:%7Bk8s_namespace_name%3D%22checkout%22%7D
```

### Restrict by environment

Give separate policies to a production support team and a staging support team that share the same tenant.

Production team selector:

```logql
{deployment_environment_name="production"}
```

Header:

```http
X-Prom-Label-Policy: team-prod:%7Bdeployment_environment_name%3D%22production%22%7D
```

Staging team selector:

```logql
{deployment_environment_name="staging"}
```

Header:

```http
X-Prom-Label-Policy: team-staging:%7Bdeployment_environment_name%3D%22staging%22%7D
```

### Restrict by service, with an exclusion

Combine a service restriction with an exclusion, similar to [Use multiple selectors](#use-multiple-selectors), but scoped to a specific service.

Allow access to the `payments-api` service, but exclude any of its streams labeled `secret="true"`, and separately allow access to every other service:

```logql
{service_name="payments-api", secret!="true"}
{service_name!="payments-api"}
```

Header:

```http
X-Prom-Label-Policy: team-payments:%7Bservice_name%3D%22payments-api%22%2C%20secret%21%3D%22true%22%7D,team-payments:%7Bservice_name%21%3D%22payments-api%22%7D
```

### Restrict by region for a distributed team

Scope a regional on-call team so that it only sees logs from its own region.

Selector:

```logql
{cloud_region="us-west-1"}
```

Header:

```http
X-Prom-Label-Policy: team-uswest:%7Bcloud_region%3D%22us-west-1%22%7D
```

### Combine selectors across multiple namespaces

Grant a tenant access to logs from two Kubernetes namespaces by giving it two separate selectors for the same tenant:

```logql
{k8s_namespace_name="frontend"}
{k8s_namespace_name="checkout"}
```

Because a tenant's selectors are combined with OR, a stream is returned if it matches either one: the `frontend` namespace or the `checkout` namespace.

Header:

```http
X-Prom-Label-Policy: team-multi:%7Bk8s_namespace_name%3D%22frontend%22%7D,team-multi:%7Bk8s_namespace_name%3D%22checkout%22%7D
```

# Manage tenant isolation

Grafana Loki is a multi-tenant system; requests and data for tenant A are isolated from
tenant B. Requests to the Loki API should include an HTTP header
(`X-Scope-OrgID`) that identifies the tenant for the request.

Tenant IDs must not be longer than 150 bytes and can only include a specific
set of characters; see [Restrictions](#restrictions) below for the full rules.
Operators are recommended to use a reasonable limit for uniquely identifying
tenants; 20 bytes is usually enough.

Loki defaults to running in multi-tenant mode.
Multi-tenant mode is set in the configuration with `auth_enabled: true`, or with the equivalent command line flag `-auth.enabled` (default `true`).

When configured with `auth_enabled: false`, Loki uses a single tenant.
The `X-Scope-OrgID` header is not required in Loki API requests.
The single tenant ID defaults to `fake` and can be changed via the `no_auth_tenant` configuration option.
On a fresh cluster with an empty bucket this is safe to change at any time; on a cluster that has already
written data, changing the tenant ID requires migrating existing data to the new tenant path (see [Loki Migrate Tool](https://github.com/grafana/loki/tree/main/cmd/migrate)).

When `auth_enabled: true`, a request that does not include the `X-Scope-OrgID` header fails with an HTTP `401 Unauthorized` error and the message `no org id`.
For more information about setting this header, including examples of configuring a reverse proxy to add it, refer to [Manage authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/).
To troubleshoot this error, refer to [No org ID](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/troubleshooting/troubleshoot-operations/#error-no-org-id).

## Multi-tenant Queries

In multi-tenant mode, queries may gather results from multiple tenants.
Set the querier configuration option `multi_tenant_queries_enabled: true`, or the equivalent command line flag `-querier.multi-tenant-queries-enabled` (default `false`), to enable queries across tenants.
The query API request defines the tenants.
Specify multiple tenants
in the query request HTTP header `X-Scope-OrgID` by separating the tenant IDs with the pipe character (`|`).
For example, a query for tenants `A` and `B` requires the header `X-Scope-OrgID: A|B`.

Only query endpoints support multi-tenant calls.
Calls to `GET /loki/api/v1/tail`, `POST /loki/api/v1/push`, and `POST /otlp/v1/logs` will return an HTTP 400 error if more than one tenant is defined in the HTTP header.

Instant and range queries support label filtering using tenant IDs.
For example, the query

```logql
{app="foo", __tenant_id__=~"a.+"} | logfmt
```

will return results for all tenants
that have a tenant ID that begins with the character `a`.

The `__tenant_id__` label is not stored with the log data.
Loki adds it to the results only when a query spans more than one tenant.
If the request names a single tenant, the results do not include this label.

If the label `__tenant_id__` is already present in a log stream, it is prepended with the string `original_`.

Tenant ID filtering in stages is not supported.
An example of a query that will _not_ work:

```logql
{app="foo"} | __tenant_id__="1" | logfmt
```

## Restrictions

Tenant IDs must not be longer than 150 bytes and can only include the following characters:

- Alphanumeric characters
  - `0-9`
  - `a-z`
  - `A-Z`
- Special characters
  - Exclamation point (`!`)
  - Hyphen (`-`)
  - Underscore (`_`)
  - Single period (`.`)
  - Asterisk (`*`)
  - Single quote (`'`)
  - Open parenthesis (`(`)
  - Close parenthesis (`)`)

{{< admonition type="note" >}}
For security reasons, `.` and `..` aren't valid tenant IDs.
{{< /admonition >}}

{{< admonition type="warning" >}}
Do not include a colon (`:`) in the `X-Scope-OrgID` header.
A colon is not a valid tenant ID character.
Most Loki components stop reading the tenant ID at the first colon and ignore the rest of the value, and the ignored part is not checked against the rules on this page.
Other components read the whole header value instead, so a header that contains a colon can produce inconsistent results.
{{< /admonition >}}

# Monitor tenant limits using the Overrides Exporter

Loki is a multi-tenant system that supports applying limits to each tenant as a mechanism for resource management. The `overrides-exporter` module exposes these limits as Prometheus metrics in order to help operators better understand tenant behavior.

## Context

Configuration updates to tenant limits can be applied to Loki without restart via the [`runtime_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#runtime_config) feature.

## Example

The `overrides-exporter` module is disabled by default. We recommend running a single instance per cluster to avoid issues with metric cardinality. The `overrides-exporter` creates one metric for every scalar field in the limits configuration under the metric `loki_overrides_defaults` with the default value for that field after loading the Loki configuration. It also exposes another metric for _every_ differing field for _every_ tenant.

Using an example `runtime.yaml`:

```yaml
overrides:
  "tenant_1":
    ingestion_rate_mb: 10
    max_streams_per_user: 100000
    max_chunks_per_query: 100000
```

Launch an instance of the `overrides-exporter`:

```shell
loki -target=overrides-exporter -runtime-config.file=runtime.yaml -config.file=basic_schema_config.yaml -server.http-listen-port=8080
```

To inspect the tenant limit overrides:

```shell
$ curl -sq localhost:8080/metrics | grep override
# HELP loki_overrides Resource limit overrides applied to tenants
# TYPE loki_overrides gauge
loki_overrides{limit_name="ingestion_rate_mb",user="tenant_1"} 10
loki_overrides{limit_name="max_chunks_per_query",user="tenant_1"} 100000
loki_overrides{limit_name="max_streams_per_user",user="tenant_1"} 100000
# HELP loki_overrides_defaults Default values for resource limit overrides applied to tenants
# TYPE loki_overrides_defaults gauge
loki_overrides_defaults{limit_name="block_ingestion_status_code"} 260
loki_overrides_defaults{limit_name="cardinality_limit"} 100000
loki_overrides_defaults{limit_name="creation_grace_period"} 6e+11
loki_overrides_defaults{limit_name="ingestion_burst_size_mb"} 6
loki_overrides_defaults{limit_name="ingestion_rate_mb"} 4
loki_overrides_defaults{limit_name="max_cache_freshness_per_query"} 6e+11
loki_overrides_defaults{limit_name="max_chunks_per_query"} 2e+06
loki_overrides_defaults{limit_name="max_concurrent_tail_requests"} 10
loki_overrides_defaults{limit_name="max_entries_limit_per_query"} 5000
loki_overrides_defaults{limit_name="max_global_streams_per_user"} 5000
loki_overrides_defaults{limit_name="max_label_name_length"} 1024
loki_overrides_defaults{limit_name="max_label_names_per_series"} 15
loki_overrides_defaults{limit_name="max_label_value_length"} 2048
loki_overrides_defaults{limit_name="max_line_size"} 262144
loki_overrides_defaults{limit_name="max_metadata_cache_freshness"} 8.64e+13
loki_overrides_defaults{limit_name="max_queriers_per_tenant"} 0
loki_overrides_defaults{limit_name="max_query_capacity"} 0
loki_overrides_defaults{limit_name="max_query_length"} 2.5956e+15
loki_overrides_defaults{limit_name="max_query_lookback"} 0
loki_overrides_defaults{limit_name="max_query_parallelism"} 32
loki_overrides_defaults{limit_name="max_query_series"} 500
loki_overrides_defaults{limit_name="max_streams_matchers_per_query"} 1000
loki_overrides_defaults{limit_name="max_streams_per_user"} 0
loki_overrides_defaults{limit_name="max_structured_metadata_size"} 65536
loki_overrides_defaults{limit_name="min_sharding_lookback"} 0
loki_overrides_defaults{limit_name="per_stream_rate_limit"} 3.145728e+06
loki_overrides_defaults{limit_name="per_stream_rate_limit_burst"} 1.572864e+07
loki_overrides_defaults{limit_name="per_tenant_override_period"} 1e+10
loki_overrides_defaults{limit_name="query_timeout"} 6e+10
loki_overrides_defaults{limit_name="reject_old_samples_max_age"} 6.048e+14
loki_overrides_defaults{limit_name="retention_period"} 0
loki_overrides_defaults{limit_name="ruler_max_rule_groups_per_tenant"} 0
loki_overrides_defaults{limit_name="ruler_max_rules_per_rule_group"} 0
loki_overrides_defaults{limit_name="ruler_remote_write_queue_batch_send_deadline"} 0
loki_overrides_defaults{limit_name="ruler_remote_write_queue_capacity"} 0
loki_overrides_defaults{limit_name="ruler_remote_write_queue_max_backoff"} 0
loki_overrides_defaults{limit_name="ruler_remote_write_queue_max_samples_per_send"} 0
loki_overrides_defaults{limit_name="ruler_remote_write_queue_max_shards"} 0
loki_overrides_defaults{limit_name="ruler_remote_write_queue_min_backoff"} 0
loki_overrides_defaults{limit_name="ruler_remote_write_queue_min_shards"} 0
loki_overrides_defaults{limit_name="ruler_remote_write_timeout"} 0
loki_overrides_defaults{limit_name="split_queries_by_interval"} 3.6e+12
loki_overrides_defaults{limit_name="tsdb_max_bytes_per_shard"} 6.291456e+08
loki_overrides_defaults{limit_name="tsdb_max_query_parallelism"} 128
loki_overrides_defaults{limit_name="volume_enabled"} 1
loki_overrides_defaults{limit_name="volume_max_series"} 1000
```

{{< admonition type="note" >}}
This output is a partial example. The exact set of metrics depends on your Loki version, since `loki_overrides_defaults` includes one series for every scalar field in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config) block, and that block grows with each release. Query your own instance's `/metrics` endpoint for the authoritative, current list rather than relying on a fixed example.
{{< /admonition >}}

Alerts can be created based on these metrics to inform operators when tenants are close to hitting their limits allowing for increases to be applied before the tenant limits are exceeded.

# Manage recording rules

Recording rules are queries that run in an interval and produce metrics from logs that can be pushed to a Prometheus compatible backend.

Recording rules are evaluated by the `ruler` component. Each `ruler` acts as its own `querier`, in the sense that it
executes queries against the store without using the `query-frontend` or `querier` components. It will respect all query
[limits](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config) put in place for the `querier`.

The Loki implementation of recording rules largely reuses Prometheus' code.

Samples generated by recording rules are sent to Prometheus using Prometheus' **remote-write** feature.

## Write-Ahead Log (WAL)

All samples generated by recording rules are written to a WAL. The WALs main benefit is that it persists the samples
generated by recording rules to disk, which means that if your `ruler` crashes, you won't lose any data.
We are trading off extra memory usage and slower start-up times for this functionality.

A WAL is created per tenant; this is done to prevent cross-tenant interactions. If all samples were to be written
to a single WAL, this would increase the chances that one tenant could cause data-loss for others. A typical scenario here
is that Prometheus will, for example, reject a remote-write request with 100 samples if just 1 of those samples is invalid in some way.

### Start-up

When the `ruler` starts up, it deletes (wipes) its remote-write WAL directory and starts each tenant with a fresh, empty
WAL. WAL data left over from a previous run is not replayed, so any samples that had not yet been flushed to remote storage
before the restart are discarded.

### Truncation

WAL files are regularly truncated to reduce their size on disk.
[This guide](https://ganeshvernekar.com/blog/prometheus-tsdb-wal-and-checkpoint/#wal-truncation-and-checkpointing)
from one of the Prometheus maintainers (Ganesh Vernekar) gives an excellent overview of the truncation, checkpointing,
and replaying of the WAL.

### Cleaner

The WAL Cleaner watches for abandoned WALs (tenants who no longer have recording rules associated) and deletes them.
Enable this feature only if you are running into storage concerns with WALs that are too large. WALs should not grow
excessively large due to truncation.

The WAL Cleaner is disabled by default. Enable it by setting `-ruler.wal-cleaner.period` to a non-zero value.

## Scaling

See Mimir's guide for [configuring Grafana Mimir hash rings](https://grafana.com/docs/mimir/latest/configure/configure-hash-rings/) for scaling the ruler using a ring.

{{< admonition type="note" >}}
By default, the `ruler` shards by rule _group_, not by individual rules. This is an artifact of the fact that Prometheus
recording rules need to run in order since one recording rule can reuse another, but this is not possible in Loki.
To shard by individual rule instead, set `-ruler.sharding-algo="by-rule"`. For details, refer to [Tuning](#tuning).
{{< /admonition >}}

## Deployment

The `ruler` needs to persist its WAL files to disk, and it incurs a bit of a start-up cost by reading these WALs into memory.
As such, it is recommended that you try to minimize churn of individual `ruler` instances, since recording rule samples
produced while a tenant's WAL is being read from disk are dropped rather than persisted (see [Start-up](#start-up)).

### Kubernetes

It is recommended that you run the `rulers` using `StatefulSets`. The `ruler` will write its WAL files to persistent storage,
so a `Persistent Volume` should be utilized.

## Remote-Write

### Client configuration

Remote-write client configuration is fully compatible with [prometheus configuration format](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write).

```yaml
remote_write:
  clients:
    mimir:
      url: http://mimir/api/v1/push
      write_relabel_configs:
      - action: replace
        target_label: job
        replacement: loki-recording-rules
```

### Per-Tenant Limits

Remote-write can be configured at a global level in the base configuration, and certain parameters tuned specifically on
a per-tenant basis. Most of the configuration options [defined here](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#ruler)
have [override options](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config) (which can be also applied at runtime!).

### Tuning

Remote-write can be tuned if the default configuration is insufficient (see [Failure Modes](#failure-modes) below).

There is a [guide](https://prometheus.io/docs/practices/remote_write/) on the Prometheus website, all of which applies to Loki, too.

Rules can be evenly distributed across available rulers by using `-ruler.enable-sharding=true` and `-ruler.sharding-algo="by-rule"`.

`-ruler.enable-sharding` and `-ruler.sharding-strategy` control which `ruler` instances own which tenants (ring-based
sharding, with optional shuffle sharding across rulers). For details, refer to
[Shuffle sharding](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/shuffle-sharding/#shuffle-sharding-in-the-ruler).

`-ruler.sharding-algo` controls how a single tenant's rule groups are split up for evaluation once sharding is
enabled. Rule groups execute in order; this is a feature inherited from Prometheus' rule engine (which Loki uses), but
Loki has no need for this constraint because rules cannot depend on each other. The default algorithm, `by-group`,
shards by rule group, but this may be undesirable, since some rule groups could contain more expensive rules, which
can lead to subsequent rules missing evaluations. The `by-rule` algorithm creates one rule group for each rule the
ruler instance "owns" (based on its hash ring), and these groups are all executed concurrently.

## Observability

Since Loki reuses the Prometheus code for recording rules and WALs, it also gains all of Prometheus' observability.

Prometheus exposes a number of metrics for its WAL implementation, and these have all been prefixed with `loki_ruler_wal_`.

For example: `prometheus_remote_storage_bytes_total` → `loki_ruler_wal_prometheus_remote_storage_bytes_total`

Additional metrics are exposed, also with the prefix `loki_ruler_wal_`. All per-tenant metrics contain a `tenant`
label, so be aware that cardinality could begin to be a concern if the number of tenants grows sufficiently large.

Some key metrics to note are:

- `loki_ruler_wal_appender_ready`: whether a WAL appender is ready to accept samples (1) or not (0)
- `loki_ruler_wal_prometheus_remote_storage_samples_total`: number of samples sent per tenant to remote storage
  - `loki_ruler_wal_prometheus_remote_storage_samples_pending`: samples currently buffered in memory, waiting to be sent to remote storage. This is a gauge, not a counter, so don't use `rate()` or `increase()` with it.
  - `loki_ruler_wal_prometheus_remote_storage_samples_failed_total`: samples that failed when sent to remote storage
  - `loki_ruler_wal_prometheus_remote_storage_samples_dropped_total`: samples dropped by relabel configurations
  - `loki_ruler_wal_prometheus_remote_storage_samples_retried_total`: samples re-resent to remote storage
- `loki_ruler_wal_prometheus_remote_storage_highest_timestamp_in_seconds`: highest timestamp of sample appended to WAL
- `loki_ruler_wal_prometheus_remote_storage_queue_highest_sent_timestamp_seconds`: highest timestamp of sample sent to remote storage.

We've created a basic [dashboard in our loki-mixin](https://github.com/grafana/loki/tree/main/production/loki-mixin/dashboards/recording-rules.libsonnet)
which you can use to administer recording rules.

## Failure Modes

### Remote-Write Lagging

Remote-write can lag behind for many reasons:

1. Remote-write storage (Prometheus) is temporarily unavailable
2. A tenant is producing samples too quickly from a recording rule
3. Remote-write is tuned too low, creating backpressure

It can be determined by subtracting
`loki_ruler_wal_prometheus_remote_storage_queue_highest_sent_timestamp_seconds` from
`loki_ruler_wal_prometheus_remote_storage_highest_timestamp_in_seconds`.

In case 1, the `ruler` will continue to retry sending these samples until the remote storage becomes available again. Be
aware that if the remote storage is down for longer than `ruler.wal.max-age`, data loss may occur after truncation occurs.

In cases 2 and 3, you should consider [tuning](#tuning) remote-write appropriately.

Further reading: see [this blog post](https://grafana.com/blog/how-to-troubleshoot-remote-write-issues-in-prometheus/)
by Prometheus maintainer Callum Styan.

### Appender Not Ready

Each tenant's WAL has an "appender" internally; this appender is used to _append_ samples to the WAL. The appender is marked
as _not ready_ until the WAL storage has been initialized upon startup. You can alert on `loki_ruler_wal_appender_ready < 1`
to detect tenants whose WAL is not yet ready to accept samples.

### Corrupt WAL

If a disk fails or the `ruler` does not terminate correctly, there's a chance one or more tenant WALs can become corrupted.
The `ruler` wipes its WAL directory on startup, so any WAL left over from a previous run — corrupt or not — is discarded and
recreated fresh rather than replayed.

### Found another failure mode?

Open an [issue](https://github.com/grafana/loki/issues) and tell us about it!

# Enforce rate limits and push request validation

Loki will reject requests if they exceed a usage threshold (rate limit error) or if they are invalid (validation error).

All occurrences of these errors can be observed using the `loki_discarded_samples_total` and `loki_discarded_bytes_total` metrics. The sections below describe the various possible reasons specified in the `reason` label of these metrics.

It is recommended that Loki operators set up alerts or dashboards with these metrics to detect when rate limits or validation errors occur.

## Terminology

- **sample**: a log line with [structured metadata](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/structured-metadata/)
- **stream**: samples with a unique combination of labels
- **active stream**: streams that are present in the ingesters - these have recently received log lines within the `chunk_idle_period` period (default: 30m)

## Rate-Limit Errors

Rate-limits are enforced when Loki cannot handle more requests from a tenant.

Rate-limit errors reject the whole request before Loki stores any of it. The tables below list `Sample discarded: No` for these errors because Loki never accepts the samples in the first place, but the request still increments `loki_discarded_samples_total` and `loki_discarded_bytes_total`. Because these errors are retryable, a client can resend the same data after the rate limit condition clears.

### `rate_limited`

This rate limit is enforced when a tenant has exceeded their configured log ingestion rate limit.

One solution if you're seeing samples dropped due to `rate_limited` is simply to increase the rate limits on your Loki cluster. These limits can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. The config options to use are `ingestion_rate_mb` and `ingestion_burst_size_mb`.

Note that you'll want to make sure your Loki cluster has sufficient resources provisioned to be able to accommodate these higher limits. Otherwise your cluster may experience performance degradation as it tries to handle this higher volume of log lines to ingest.

 Another option to address samples being dropped due to `rate_limits` is simply to decrease the rate of log lines being sent to your Loki cluster. Consider collecting logs from fewer targets or setting up [drop stages](https://grafana.com/docs/alloy/latest/reference/components/loki/loki.process/#stagedrop-block) in Alloy to filter out certain log lines. You can also use Alloy's [rate limiting](https://grafana.com/docs/alloy/latest/reference/components/loki/loki.process/#stagelimit-block) to control the volume of logs sent to your Loki cluster.  

| Property                | Value                   |
|-------------------------|-------------------------|
| Enforced by             | `distributor`           |
| Outcome                 | Request rejected        |
| Retryable               | Yes                     |
| Sample discarded        | No                      |
| Configurable per tenant | Yes                     |
| HTTP status code        | `429 Too Many Requests` |

### `per_stream_rate_limit`

This limit is enforced when a single stream reaches its rate limit.

Each stream has a rate limit applied to it to prevent individual streams from overwhelming the set of ingesters it is distributed to (the size of that set is equal to the `replication_factor` value).

This value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. The config options to adjust are `per_stream_rate_limit` and `per_stream_rate_limit_burst`. The default values are `3MB` for `per_stream_rate_limit` and `15MB` for `per_stream_rate_limit_burst`.

Another option you could consider to decrease the rate of samples dropped due to `per_stream_rate_limit` is to split the stream that is getting rate limited into several smaller streams. A third option is to use the Alloy [`stage.limit` block](https://grafana.com/docs/alloy/latest/reference/components/loki/loki.process/#stagelimit-block) to limit the rate of samples sent to the stream hitting the `per_stream_rate_limit`.

We typically recommend setting `per_stream_rate_limit` no higher than 5MB, and `per_stream_rate_limit_burst` no higher than 20MB.

| Property                | Value                   |
|-------------------------|-------------------------|
| Enforced by             | `ingester`              |
| Outcome                 | Request rejected        |
| Retryable               | Yes                     |
| Sample discarded        | No                      |
| Configurable per tenant | Yes                     |
| HTTP status code        | `429 Too Many Requests` |

### `stream_limit`

This limit is enforced when a tenant reaches their maximum number of active streams.

Active streams are held in memory buffers in the ingesters, and if this value becomes sufficiently large then it will cause the ingesters to run out of memory.

This value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file.  To increase the allowable active streams, adjust `max_global_streams_per_user`. Alternatively, the number of active streams can be reduced by removing extraneous labels or removing excessive unique label values.

{{< admonition type="note" >}}
Under normal conditions, this limit only blocks the creation of genuinely new streams. Log lines for streams that are already active continue to be accepted.

However, during ingester scaling (autoscaling, rollouts, or ring changes such as a partition-ingester migration), streams can be rebalanced to ingesters that were not previously handling them. The new ingester has no record of that stream in memory, so it treats the next write for that stream as a stream creation and checks it against its local share of the limit. If that share is already used up, for example because the same tenant is also adding new high-cardinality streams during the rebalance, writes to the already-active stream can be rejected with `stream_limit`, even though the tenant has not added new label cardinality for it.

This effect is temporary. It is bounded by the number of ingesters added or restarted, and it typically resolves once the ring is stable and stream ownership is recalculated.
{{< /admonition >}}

| Property                | Value                   |
|-------------------------|-------------------------|
| Enforced by             | `ingester`              |
| Outcome                 | Request rejected        |
| Retryable               | Yes                     |
| Sample discarded        | No                      |
| Configurable per tenant | Yes                     |
| HTTP status code        | `429 Too Many Requests` |

## Validation Errors

Validation errors occur when a request violates a validation rule defined by Loki.

### `line_too_long`

This error occurs when a log line exceeds the maximum allowable length in bytes. The HTTP response will include the stream to which the offending log line belongs as well as its size in bytes.

This value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. To increase the maximum line size, adjust `max_line_size`.  We recommend that you do not increase this value above 256kb for performance reasons. Alternatively, Loki can be configured to ingest truncated versions of log lines over the length limit by using the `max_line_size_truncate` option. When truncation is enabled, you can use `max_line_size_truncate_identifier` to append a marker string to each truncated log line, so that truncated lines are easy to identify later.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | Yes               |
| HTTP status code        | `400 Bad Request` |

### `invalid_labels`

This error occurs when one or more labels in the submitted streams fail validation.

Loki uses the [same validation rules as Prometheus](https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels) for validating labels.

> Label names may contain ASCII letters, numbers, as well as underscores. They must match the regex `[a-zA-Z_][a-zA-Z0-9_]*`. Label names beginning with __ are reserved for internal use.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | No                |
| HTTP status code        | `400 Bad Request` |

### `missing_labels`

This validation error is returned when a stream is submitted without any labels.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | No                |
| HTTP status code        | `400 Bad Request` |

### `too_far_behind` and `out_of_order`

The `too_far_behind` validation error is returned when lines in question are older than half of `-ingester.max-chunk-age` compared to the newest line in the stream.

{{< admonition type="note" >}}
`unordered_writes` and its related flag `-ingester.unordered-writes` are deprecated and will be removed in a future major release. Once removed, out-of-order writes will be accepted unconditionally, and this setting will no longer be configurable.
{{< /admonition >}}

More details can be found [here](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#accept-out-of-order-writes) about the Loki ordering constraints.

The `unordered_writes` config value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. `max_chunk_age`, on the other hand, is a global-only configuration and cannot be set per tenant.

This problem can be solved by ensuring that log delivery is configured correctly, or by increasing the `max_chunk_age` value.

It is recommended to resist modifying the default value of `max_chunk_age` as this has other implications, and to instead try track down the cause for delayed logged delivery. It should also be noted that this a per-stream error, so by simply splitting streams (adding more labels) this problem can be circumvented, especially if multiple hosts are sending samples for a single stream.

| Property                | Value               |
|-------------------------|---------------------|
| Enforced by             | `ingester`          |
| Outcome                 | Request rejected    |
| Retryable               | **No**              |
| Sample discarded        | **Yes**             |
| Configurable per tenant | Yes, for `unordered_writes`. No, for `max_chunk_age`, which is global only. |
| HTTP status code        | `400 Bad Request`   |

### `greater_than_max_sample_age`

If the `reject_old_samples` config option is set to `true` (it is by default), then samples will be rejected with `reason=greater_than_max_sample_age` if they are older than the `reject_old_samples_max_age` value. You should not see samples rejected for `reason=greater_than_max_sample_age` if `reject_old_samples=false`.

This value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. This error can be solved by increasing the `reject_old_samples_max_age` value, or investigating why log delivery is delayed for this particular stream. The stream in question will be returned in the body of the HTTP response.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | Yes               |
| HTTP status code        | `400 Bad Request` |

### `too_far_in_future`

If a sample's timestamp is greater than the current timestamp, Loki allows for a certain grace period during which samples will be accepted. If the grace period is exceeded, the error will occur.

This value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. This error can be solved by increasing the `creation_grace_period` value, or investigating why this particular stream has a timestamp too far into the future. The stream in question will be returned in the body of the HTTP response.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | Yes               |
| HTTP status code        | `400 Bad Request` |

### `max_label_names_per_series`

If a sample is submitted with more labels than Loki has been configured to allow, it will be rejected with the `max_label_names_per_series` reason. Note that 'series' is the same thing as a 'stream' in Loki - the 'series' term is a legacy name.

This value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. This error can be solved by increasing the `max_label_names_per_series` value. The stream to which the offending sample (i.e. the one with too many label names) belongs will be returned in the body of the HTTP response.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | Yes               |
| HTTP status code        | `400 Bad Request` |

### `label_name_too_long`

If a sample is sent with a label name that has a length in bytes greater than Loki has been configured to allow, it will be rejected with the `label_name_too_long` reason.

This value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. This error can be solved by increasing the `max_label_name_length` value, though we do not recommend raising it significantly above the default value of `1024` for performance reasons. The offending stream will be returned in the body of the HTTP response.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | Yes               |
| HTTP status code        | `400 Bad Request` |

### `label_value_too_long`

If a sample has a label value with a length in bytes greater than Loki has been configured to allow, it will be rejected for the `label_value_too_long` reason.

This value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. This error can be solved by increasing the `max_label_value_length` value. The offending stream will be returned in the body of the HTTP response.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | Yes               |
| HTTP status code        | `400 Bad Request` |

### `duplicate_label_names`

If a sample is sent with two or more identical labels, it will be rejected for the `duplicate_label_names` reason.

The offending stream will be returned in the body of the HTTP response.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | No                |
| HTTP status code        | `400 Bad Request` |

### `request_body_too_large`

This error occurs when the raw push request exceeds a size limit before Loki can parse individual streams and log lines out of it. Unlike the other validation errors on this page, `request_body_too_large` can be triggered in two different ways:

- The decompressed request body exceeds the `max_recv_msg_size` or `max_decompressed_size` settings in the `distributor` configuration block. These are cluster-wide settings, not `limits_config` values, so they cannot be set per tenant. `max_recv_msg_size` defaults to 100MB, and `max_decompressed_size` defaults to 5000MB (50 times the default `max_recv_msg_size`).
- A single stream's label string exceeds 16MB. This limit is hardcoded and cannot be changed through any configuration option. It exists because the Prometheus label parser cannot handle labels above this size.

To resolve this error, reduce the size of the push request (for example by sending fewer streams per request, or by reducing the number or size of labels on a stream), or increase `max_recv_msg_size` and `max_decompressed_size` if your cluster has the resources to handle larger requests.

{{< admonition type="note" >}}
This reason only appears in the `loki_discarded_bytes_total` metric, not in `loki_discarded_samples_total`. Loki rejects the request before it can parse the body, so it cannot count how many log lines the request contained.
{{< /admonition >}}

| Property                | Value                          |
|-------------------------|--------------------------------|
| Enforced by             | `distributor`                  |
| Outcome                 | Request rejected               |
| Retryable               | **No**                         |
| Sample discarded        | **Yes**                        |
| Configurable per tenant | No                             |
| HTTP status code        | `413 Request Entity Too Large` |

### `disallowed_structured_metadata`

This error occurs when a stream includes [structured metadata](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/structured-metadata/) but the tenant is not allowed to send it.

This value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. To allow structured metadata, set `allow_structured_metadata` to `true` (this is the default). The offending stream will be returned in the body of the HTTP response.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | Yes               |
| HTTP status code        | `400 Bad Request` |

### `structured_metadata_too_large`

This error occurs when the [structured metadata](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/structured-metadata/) attached to a single log line exceeds the maximum allowable size in bytes.

This value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. To increase the limit, adjust `max_structured_metadata_size` (default: `64kb`). The offending stream will be returned in the body of the HTTP response.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | Yes               |
| HTTP status code        | `400 Bad Request` |

### `structured_metadata_too_many`

This error occurs when a single log line includes more [structured metadata](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/structured-metadata/) entries than Loki has been configured to allow.

This value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. To increase the limit, adjust `max_structured_metadata_entries_count` (default: `128`). The offending stream will be returned in the body of the HTTP response.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | Yes               |
| HTTP status code        | `400 Bad Request` |

### `blocked_ingestion` and `blocked_ingestion_policy`

{{< admonition type="note" >}}
This is an experimental feature.
{{< /admonition >}}

These reasons occur when an operator has deliberately blocked ingestion for a tenant (`blocked_ingestion`) or for a specific policy (`blocked_ingestion_policy`), rather than because a client sent invalid or excessive data. Policies are defined using the `policy_stream_mapping` setting in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block.

This is configured using `block_ingestion_until` (for a tenant) or `block_ingestion_policy_until` (for a policy), both of which take a date in RFC3339 format. Ingestion is blocked until that date. These values can be set globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, but they are normally set for a single tenant in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file, because a global value blocks ingestion for every tenant.

The `block_ingestion_status_code` option controls what HTTP status code Loki returns while ingestion is blocked. The default is a custom status code, `260`, so that blocked requests can be distinguished from ordinary errors. If `block_ingestion_status_code` is set to `200`, Loki silently drops the blocked data without returning an error to the client, although the data is still counted in `loki_discarded_samples_total` and `loki_discarded_bytes_total`.

| Property                | Value                  |
|-------------------------|------------------------|
| Enforced by             | `distributor`          |
| Outcome                 | Request rejected, or silently dropped if `block_ingestion_status_code` is `200` |
| Retryable               | **No**                 |
| Sample discarded        | **Yes**                |
| Configurable per tenant | Yes                    |
| HTTP status code        | `260` by default, configurable with `block_ingestion_status_code` |

### `missing_enforced_labels`

{{< admonition type="note" >}}
This is an experimental feature.
{{< /admonition >}}

This error occurs when a tenant has been configured to require certain labels on every stream, and a stream is submitted that is missing one or more of them.

This value can be modified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) block, or on a per-tenant basis in the [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#runtime-configuration-file) file. Use `enforced_labels` to require labels on all streams, or `policy_enforced_labels` to require them only on streams matching a specific policy defined by `policy_stream_mapping`. The stream and the missing labels will be returned in the body of the HTTP response.

| Property                | Value             |
|-------------------------|-------------------|
| Enforced by             | `distributor`     |
| Outcome                 | Request rejected  |
| Retryable               | **No**            |
| Sample discarded        | **Yes**           |
| Configurable per tenant | Yes               |
| HTTP status code        | `400 Bad Request` |

# Manage larger production deployments

When needing to scale Loki due to increased log volume, operators should consider running several Loki processes
partitioned by role (ingester, distributor, querier, and so on) rather than a single Loki
process. Grafana Labs' [production setup](https://github.com/grafana/loki/blob/main/production/ksonnet/loki)
contains `.libsonnet` files that demonstrates configuring separate components
and scaling for resource usage.

## Separate Query Scheduler

The Query frontend has an in-memory queue that can be moved out into a separate process similar to the
[Grafana Mimir query-scheduler](https://grafana.com/docs/mimir/latest/operators-guide/architecture/components/query-scheduler/). This allows running multiple query frontends.

To run with the Query Scheduler, configure both the frontend and the querier worker with the scheduler's address.

Using CLI flags:

- Frontend: `-frontend.scheduler-address=<scheduler-host:port>`
- Querier: `-querier.scheduler-address=<scheduler-host:port>`

Using the configuration file:

```yaml
frontend:
  scheduler_address: <scheduler-host:port>

frontend_worker:
  scheduler_address: <scheduler-host:port>
```

{{< admonition type="note" >}}
The querier's `scheduler_address` is configured under `frontend_worker`, not under `querier`. The [`frontend_worker`](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#frontend_worker) block configures the worker running within the querier that pulls and executes queries.
{{< /admonition >}}

It is not valid to start the querier with both a configured frontend address and a scheduler address.

The query scheduler process itself can be started via the `-target=query-scheduler` option of the Loki Docker image. For instance, `docker run grafana/loki:latest -config.file=/etc/loki/config.yaml -target=query-scheduler -server.http-listen-port=8009 -server.grpc-listen-port=9009` starts the query scheduler listening on ports `8009` and `9009`.

### Scheduler discovery using a ring

Instead of configuring a static `scheduler_address` on the frontend and the querier worker, you can have query schedulers register themselves in a [hash ring](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/hash-rings/#about-the-query-scheduler-ring). Queriers and query frontends then discover query schedulers through the ring instead of using a fixed address.

To enable this, set `use_scheduler_ring: true`, or use the `-query-scheduler.use-scheduler-ring` CLI flag:

```yaml
query_scheduler:
  use_scheduler_ring: true
```

{{< admonition type="note" >}}
Set this option in the configuration used by your query schedulers, your queriers, and your query frontends. Queriers and query frontends only read the ring if `use_scheduler_ring` is true in their own configuration. If all of your components share one configuration file, you only need to set it once.
{{< /admonition >}}

The ring needs a key-value store. In most deployments you do not need to configure one for the query scheduler specifically, because the scheduler ring inherits the store from the [`common.ring`](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#common) block, and Loki uses `memberlist` for all rings when you configure a `memberlist` section. To set the store for the scheduler ring alone, use the `scheduler_ring` block:

```yaml
query_scheduler:
  use_scheduler_ring: true
  scheduler_ring:
    kvstore:
      store: memberlist
```

If you do not configure a frontend address, a scheduler address, or a downstream URL anywhere in your configuration, Loki automatically enables the scheduler ring for you.

Each component that takes part in the ring exposes the ring state at the `/scheduler/ring` endpoint, which you can use to check that all query schedulers registered as expected.

## Memory ballast

In compute-constrained environments, garbage collection can become a significant performance factor. Frequently-run garbage collection interferes with running the application by using CPU resources. The use of memory ballast can mitigate the issue. Memory ballast allocates extra, but unused virtual memory in order to inflate the quantity of live heap space. Garbage collection is triggered by the growth of heap space usage. The inflated quantity of heap space reduces the perceived growth, so garbage collection occurs less frequently.

Configure memory ballast using the ballast_bytes configuration option.

## Remote rule evaluation

_This feature was first proposed in [`LID-0002`](https://github.com/grafana/loki/pull/8129); it contains the design decisions
which informed the implementation._

By default, the `ruler` component embeds a query engine to evaluate rules. This generally works fine, except when rules
are complex or have to process a large amount of data regularly. Poor performance of the `ruler` manifests as recording rules metrics
with gaps or missed alerts. This situation can be detected by alerting on the `loki_prometheus_rule_group_iterations_missed_total` metric
when it has a non-zero value.

A solution to this problem is to externalize rule evaluation from the `ruler` process. The `ruler` embedded query engine
is single-threaded, meaning that rules are not split, sharded, or otherwise accelerated like regular Loki queries. The `query-frontend`
component exists explicitly for this purpose and, when combined with a number of `querier` instances, can massively
improve rule evaluation performance and lead to fewer missed iterations.

It is generally recommended to create a separate `query-frontend` deployment and `querier` pool from your existing one - which handles adhoc
queries via Grafana, `logcli`, or the API. Rules should be given priority over adhoc queries because they are used to produce
metrics or alerts which may be crucial to the reliable operation of your service; if you use the same `query-frontend` and `querier` pool
for both, your rules will be executed with the same priority as adhoc queries which could lead to unpredictable performance.

To enable remote rule evaluation, set the following configuration options:

```yaml
ruler:
  evaluation:
    mode: remote
    query_frontend:
      address: dns:///<query-frontend-service>:<grpc-port>
```

If the `query-frontend` connection requires TLS, set `tls_enabled: true` under `query_frontend` and configure the accompanying TLS options.

To reduce contention when many rules evaluate at the same time, set `max_jitter` to add a bounded, random delay before each rule evaluation. This option applies to both local and remote evaluation modes:

```yaml
ruler:
  evaluation:
    max_jitter: 5s
```

Refer to the [`ruler` configuration reference](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#ruler) for further configuration options.

When you enable remote rule evaluation, the `ruler` component becomes a gRPC client to the `query-frontend` service;
this will result in far lower `ruler` resource usage because the majority of the work has been externalized.
The LogQL queries coming from the `ruler` will be executed against the given `query-frontend` service.
Requests will be load-balanced across all `query-frontend` IPs if the `dns:///` prefix is used.

{{< admonition type="note" >}}
Queries that fail to execute are _not_ retried.
{{< /admonition >}}

### Limits and Observability

Remote rule evaluation can be tuned with the following options:

- `ruler_remote_evaluation_timeout`: maximum allowable execution time for rule evaluations
- `ruler_remote_evaluation_max_response_size`: maximum allowable response size over gRPC connection from `query-frontend` to `ruler`

Both of these can be specified globally in the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config) section
or on a [per-tenant basis](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#runtime-configuration-file).

`max_jitter`, described earlier in this topic, is a global-only setting under `ruler.evaluation` rather than a per-tenant limit.

Remote rule evaluation exposes a number of metrics:

- `loki_ruler_remote_eval_request_duration_seconds`: time taken for rule evaluation (histogram)
- `loki_ruler_remote_eval_response_bytes`: number of bytes in rule evaluation response (histogram)
- `loki_ruler_remote_eval_response_samples`: number of samples in rule evaluation response (histogram)
- `loki_ruler_remote_eval_success_total`: successful rule evaluations (counter)
- `loki_ruler_remote_eval_failure_total`: unsuccessful rule evaluations with reasons (counter)

Each of these metrics are per-tenant, so cardinality must be taken into consideration.

# Manage version upgrades

- [Upgrade](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/) from one Loki version to a newer version.

- [Upgrade Helm](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/) from Helm v2.x to Helm v3.x.

# Speed up ingester rollout using zone awareness

The Loki zone aware ingesters are used by Grafana Labs in order to allow for easier rollouts of large Loki deployments. You can think of them as three logical zones, however with some extra Kubernetes configuration you could deploy them in separate zones.

By default, an incoming log stream's logs are replicated to 3 random ingesters. Except in the case of some replica scaling up or down, a given stream will always be replicated to the same 3 ingesters. This means that if one of those ingesters is restarted no data is lost. However two or more ingesters restarting can result in data loss and also impacts the systems ability to ingest logs because of an unhealthy ring status.

With zone awareness enabled, an incoming log line will be replicated to one ingester in each zone. This means that we're not only concerned about ingesters in multiple zones restarting at the same time, we can now rollout or lose an entire zone at once without impacting writes. This allows deployments with a large number of ingesters to be deployed much more quickly.

At Grafana Labs, we also make use of [rollout-operator](https://github.com/grafana/rollout-operator) to manage rollouts to the 3 StatefulSets gracefully. The rollout-operator looks for labels on StatefulSets to know which ones are part of a certain rollout group, and coordinates rollouts of pods only from a single StatefulSet in the group at a time. See the README in the rollout-operator repo for a more in depth explanation.

{{< admonition type="note" >}}
This guide describes the [jsonnet](https://github.com/grafana/loki/blob/main/production/ksonnet/loki/multi-zone.libsonnet) deployment method used internally at Grafana Labs. New jsonnet deployments run zone aware ingesters by default, so this guide is only needed if you have an existing deployment that still uses a single ingester StatefulSet.

If you deploy Loki with the [Helm chart](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/) instead, refer to [Zone-aware replication](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/concepts/#zone-aware-replication) in the Helm chart components documentation, which uses `ingester.zoneAwareReplication` Helm values rather than the jsonnet configuration described here.

The number of zones is fixed at 3 in the jsonnet library described in this guide. There is no configuration option to run a different number of zones.
{{< /admonition >}}

## Migration

Migrating from a single ingester StatefulSet to 3 zone aware ingester StatefulSets. The migration follows a few general steps, regardless of deployment method.

1. Configure your existing ingesters to be part of a zone, for example `zone-default`, this will allow us to later exclude them from the write path while still allowing for graceful shutdowns.
1. Prep for the increase in active streams (due to the way streams are split between ingesters) by increasing the number of active streams allowed for your tenants.
1. Add and scale up your new zone-aware ingester StatefulSets such that each has 1/3rd of the total number of replicas you want to run.
1. Enable zone awareness on the write path by setting `distributor.zone-awareness-enabled` to `true` for distributors and rulers.
1. Wait some time to ensure that the new zone-aware ingesters have data for the time period they are queried for (`query_ingesters_within`).
1. Enable zone awareness on the read path by setting `distributor.zone-awareness-enabled` to true for queriers.

    {{< admonition type="note" >}}
    `distributor.zone-awareness-enabled` and `distributor.excluded-zones` are CLI flag names. Despite the `distributor.` prefix, they configure the ingester ring, so each process that uses that ring (distributor, ruler, and querier) reads the same flag. In YAML, the equivalent keys are `zone_awareness_enabled` and `excluded_zones` under `ingester.lifecycler.ring`, not under a top-level `distributor:` block.
    {{< /admonition >}}

1. Configure distributors and rulers to exclude ingesters in the `zone-default` so those ingesters no longer receive write traffic using `distributor.excluded-zones`.
1. Use the shutdown endpoint to flush data from the default ingesters, then scale down and remove the associated StatefulSet.
1. Clean up any config remaining from the migration.

### Detailed Migration Steps

The following are steps to live migrate (no downtime) an existing Loki deployment from a single ingester StatefulSet to 3 zone aware ingester StatefulSets.

These instructions assume you are using the zone aware ingester jsonnet deployment code from this repo, see [here](https://github.com/grafana/loki/blob/main/production/ksonnet/loki/multi-zone.libsonnet). **If you are not using jsonnet see the relevant annotations in some steps that describe how to perform that step manually.**

1. Configure the zone for the existing ingester StatefulSet as `zone-default` by setting `multi_zone_default_ingester_zone: true`, this allows us to later filter out that zone from the write path.

    If you're not using jsonnet, this is done by setting the `-ingester.availability-zone=zone-default` flag (or the equivalent `availability_zone` field under the ingester's `lifecycler` YAML configuration) on the existing ingesters.

1. Configure ingester-pdb with `maxUnavailable` as 0 and deploy 3x zone-aware StatefulSets with 0 replicas by setting

    ```jsonnet
    _config+:: {
        multi_zone_ingester_enabled: true,
        multi_zone_ingester_migration_enabled: true,
        multi_zone_ingester_replicas: 0,
        // These last two lines are necessary now that we enable zone aware ingester by default
        // so that newly created cells will not be migrated later on. If you miss them you will
        // break writes in the cell.
        multi_zone_ingester_replication_write_path_enabled: false,
        multi_zone_ingester_replication_read_path_enabled: false,
    },
    ```

    Setting `multi_zone_ingester_migration_enabled: true` already configures the existing `ingester` PodDisruptionBudget (PDB) with `maxUnavailable: 0` for you, so no manual PDB change is needed when using jsonnet.

    If you're not using jsonnet, manually set `maxUnavailable` to `0` on the existing ingester-pdb. The new ingester StatefulSets should have a label with `rollout-group: ingester`, annotation `rollout-max-unavailable: x` (put a placeholder value in, later you should set the value of this to be some portion of the StatefulSets total replicas, for example in jsonnet we template this so that each StatefulSet runs 1/3 of the total replicas and the max unavailable is 1/3 of each StatefulSets replicas), set the update strategy to `OnDelete`, and set `-ingester.availability-zone=zone-<a|b|c>` for each of the three new StatefulSets.

1. Diff ingester and ingester-zone-a StatefulSets and make sure all config matches

    ```bash
    kubectl get statefulset -n loki-dev-008 ingester -o yaml > ingester.yaml
    kubectl get statefulset -n loki-dev-008 ingester-zone-a -o yaml > ingester-zone-a.yaml
    diff ingester.yaml ingester-zone-a.yaml
    ```

    Expected in diffs are values like: creation time and revision number, the zone, fields used by rollout operator, number of replicas, anything related to kustomize/flux, and PVC for the WAL since the containers don't exist yet.
1. Temporarily double max series limits for users that are using more than 50% of their current limit, the queries are as follows (add label selectors as appropriate):

    ```logql
    sum by (tenant)(sum (loki_ingester_memory_streams) by (cluster, namespace, tenant) / on (namespace) group_left max by(namespace) (loki_distributor_replication_factor))
    >
    on (tenant) (
    max by (tenant) (label_replace(loki_overrides{limit_name="max_global_streams_per_user"} / 2.5, "tenant", "$1", "user", "(.+)"))
    )
    ```

    ```logql
    (sum (loki_ingester_memory_streams) by (cluster, namespace, tenant) / on (namespace) group_left max by(namespace) (loki_distributor_replication_factor)
    ) / ignoring(tenant) group_left max by (cluster, namespace)(loki_overrides_defaults{limit_name="max_global_streams_per_user"}) > 0.4)
    unless on (tenant) (
    (label_replace(loki_overrides{limit_name="max_global_streams_per_user"},"tenant", "$1", "user", "(.+)")))
    ```

1. Scale up zone-aware StatefulSets until they have 1/3rd of replicas each. In smaller cells you can do this all at once, in larger cells it is safer to do it in chunks. The config value you need to change is `multi_zone_ingester_replicas: 6`, the value will be split across the three StatefulSets. In this case, each StatefulSet would run 2 replicas.

    If you're not using jsonnet, this is the step where you would also set the annotation `rollout-max-unavailable` to some value that is less than or equal to the number of replicas each StatefulSet is running.

    By default, each zone's StatefulSet uses pod anti-affinity so that ingesters from two different zones are never scheduled on the same node. Two ingesters from the same zone are still allowed to share a node. This anti-affinity rule is controlled by `ingester_allow_multiple_replicas_on_same_node`, which defaults to `false`.

    To also spread the ingesters within a single zone evenly across nodes, set `multi_zone_ingester_use_topology_spread: true`. This adds a [topology spread constraint](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) to each zone's StatefulSet, which you can tune with `multi_zone_ingester_topology_spread_max_skew` and `multi_zone_ingester_topology_spread_when_unsatisfiable`.

1. Enable zone awareness on the write path by setting `multi_zone_ingester_replication_write_path_enabled: true`, this causes distributors and rulers to reshuffle series to distributors in each zone.  Be sure to check that all the distributors and rulers have restarted properly.

    If you're not using jsonnet, enable zone awareness on the write path by setting `distributor.zone-awareness-enabled` to true for distributors and rulers.

1. Wait for `query_ingesters_within` configured hours. The default is `3h`. This ensures that no data will be missing if we query a new ingester. However, because we cut chunks at least every 30m due to `chunk_idle_period` we can likely reduce this amount of time.

1. Check that rule evaluations are still correct on the migration, look for increases in the rate for metrics with names with the following suffixes:

    ```
    rule_evaluations_total
    rule_evaluation_failures_total
    rule_group_iterations_missed_total
    ```

1. Enable zone-aware replication on the read path `multi_zone_ingester_replication_read_path_enabled: true`. If you're not using jsonnet, set `distributor.zone-awareness-enabled` to true for queriers.

1. Check that queries are still executing correctly, for example look at `loki_logql_querystats_latency_seconds_count` to see that you don't have a big increase in latency or error count for a specific query type.

1. Configure distributor / ruler to exclude ingesters in the `zone-default` so those ingesters no longer receive write traffic by setting `multi_zone_ingester_exclude_default: true`. If you're not using jsonnet set `distributor.excluded-zones` on distributors and rulers.

    It is a good idea to check rules evaluations again at this point, and also that the zone aware ingester StatefulSet is now receiving all the write traffic, you can compare `sum(loki_ingester_memory_streams{cluster="<cluster>",job=~"(<namespace>)/ingester"})` to `sum(loki_ingester_memory_streams{cluster="<cluster>",job=~"(<namespace>)/ingester-zone.*"})`

1. If you're using an automated reconciliation or deployment system like flux, disable it now (for example using flux ignore) if possible for just the default ingester StatefulSet.

1. Shutdown flush the default ingesters, unregistering them from the ring, you can do this by port-forwarding each ingester Pod and using the endpoint: `"http://url:PORT/ingester/shutdown?flush=true&delete_ring_tokens=true&terminate=false"`

    See [Flush in-memory chunks and shut down](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-http-api/#flush-in-memory-chunks-and-shut-down) for details on this endpoint. For Kubernetes-native scale-down automation, you can instead use [`POST /ingester/prepare_shutdown`](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-http-api/#prepare-ingester-shutdown), which arms the ingester to flush and unregister on its next `SIGTERM` rather than shutting it down immediately.

1. manually scale down the default ingester StatefulSet to 0 replicas, we do this via `tk apply` but you could do it via modifying the yaml.

1. merge a PR to your central config repo to keep the StatefulSet 0'd, and then remove the flux ignore.

1. clean up any remaining temporary config from the migration, for example `multi_zone_ingester_migration_enabled: true` is no longer needed.

1. ensure that all the old default ingester PVC/PV are removed.

# Deploy Loki meta-monitoring with chart version 3

The primary method for collecting and monitoring a Loki cluster is to use the [Kubernetes Monitoring Helm](https://github.com/grafana/k8s-monitoring-helm/) chart. This chart provides a comprehensive monitoring solution for Kubernetes clusters and includes direct integrations for monitoring the full LGTM (Loki, Grafana, Tempo, and Mimir) stack. This procedure will walk you through deploying version 3.8.x of the Kubernetes Monitoring Helm chart to monitor your Loki cluster.

{{< admonition type="note" >}}
This page covers chart version 3.8.x, which is a maintained stable release line. If you do not already have a reason to stay on chart 3.8.x, use [Deploy Loki meta-monitoring](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/deploy/) instead, which covers the current chart 4.x line. For help moving from chart 3.x to 4.x, refer to the [Kubernetes Monitoring Helm chart migration guide](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/kubernetes-monitoring/configuration/helm-chart-config/helm-chart/migrate-helm-chart/).
{{< /admonition >}}

{{< admonition type="note" >}}
We recommend running a production cluster of Loki in distributed mode using Kubernetes. This procedure assumes you have a running Kubernetes cluster and a running Loki deployment. There are other methods for deploying Loki, such as using Docker or VM installations. meta-monitoring is still possible when using these deployment methods but not covered in this procedure.
{{< /admonition >}}

## Prerequisites

- [kubectl](https://kubernetes.io/docs/reference/kubectl/)
- Helm 3 or above. See [Installing Helm](https://helm.sh/docs/intro/install/).
- A running Kubernetes cluster with a running Loki deployment.
- A Grafana Cloud account or a separate LGTM stack for monitoring.

## Preparing your environment

Before deploying the Kubernetes Monitoring Helm chart, you need to set up several components in your environment.

1. Add the Grafana Helm repository:

   ```bash
   helm repo add grafana https://grafana.github.io/helm-charts
   ```

1. Update your Helm repositories:

   ```bash
   helm repo update
   ```

1. Create a namespace for the monitoring stack:

   ```bash
   kubectl create namespace meta
   ```

### Authentication

The Kubernetes Monitoring Helm chart requires a Grafana Cloud account or a separate LGTM stack for monitoring. You will need to provide the necessary credentials to the Helm chart to authenticate with your Grafana Cloud account or LGTM stack. In this procedure, we will use Grafana Cloud as an example.

1. Create a new Cloud Access Policy in Grafana Cloud.
    1. Sign into [Grafana Cloud](https://grafana.com/auth/sign-in/).
    1. In the main menu, select **Security > Access Policies**.
    1. Click **Create access policy**.
    1. Give the policy a **Name** and select the following permissions:
       - Metrics: Write
       - Logs: Write
    1. Click **Create**.
    1. Click **Add Token**. Give the token a name and click **Create**.
   Save the token for later use.
1. Collect `url` and `username` for Prometheus and Loki.
   1. Navigate to the Grafana Cloud Portal **Overview** page.
   1. Click the **Details** button for your Prometheus instance.
        1. From the **Sending metrics using Grafana Alloy** section, collect the instance **username** and **url**.
        1. Navigate back to the **Overview** page.
   1. Click the **Details** button for your Loki instance.
        1. From the **Sending Logs to Grafana Cloud using Grafana Alloy** section, collect the instance **username** and **url**.
        2. Navigate back to the **Overview** page.

1. Create the Kubernetes Secrets with the collected credentials from Grafana Cloud.

   ```bash
   kubectl create secret generic metrics -n meta \
    --from-literal=username=<PROMETHEUS-USER> \
    --from-literal=password=<CLOUD-TOKEN>

   kubectl create secret generic logs -n meta \
    --from-literal=username=<LOKI-USER> \
    --from-literal=password=<CLOUD-TOKEN>
   ```

Note that the Kubernetes Monitoring Helm supports many different authentication methods based upon your requirements including:

- Bearer Tokens
- OAuth2
- SigV4
- External Secrets
  
For further information on how to configure these methods, see the [Kubernetes Monitoring Helm examples](https://github.com/grafana/k8s-monitoring-helm/tree/main/charts/k8s-monitoring/docs/examples/auth).

## Deploy the Kubernetes Monitoring Helm chart

Now that you have prepared your environment and collected the necessary credentials, you can deploy the Kubernetes Monitoring Helm chart to monitor your Loki cluster. To do this we need to copy a `values.yaml` file to our local machine and modify it to include the necessary configuration.

1. Download the `values-chart-v3.yaml` file from the Kubernetes Monitoring Helm chart repository:

   ```bash
   curl -O https://raw.githubusercontent.com/grafana/loki/main/production/helm/meta-monitoring/values-chart-v3.yaml
   ```

1. Open the `values-chart-v3.yaml` file in a text editor of your choosing and add the Prometheus and Loki endpoints.

   ```yaml
   destinations:
     - name: prometheus
       type: prometheus
       url: https://<PROMETHEUS-ENDPOINT>/api/prom/push
       auth:
         type: basic
         usernameKey: username
         passwordKey: password
       secret:
         create: false
         name: metrics
         namespace: meta

     - name: loki
       type: loki
       url: https://<LOKI-ENDPOINT>/loki/api/v1/push
       auth:
         type: basic
         usernameKey: username
         passwordKey: password
       secret:
         create: false
         name: logs
         namespace: meta

   ```

1. (Optional) Update the cluster name to a human-readable name to identify your cluster in Grafana Cloud.

   ```yaml
   # Global Label to be added to all telemetry data. Should reflect a recognizable name for the cluster.
   cluster:
     name: loki-meta-monitoring-cluster
   ```

1. The default values file assumes that you have deployed Loki in the `loki` namespace and will deploy the Kubernetes monitoring stack in the `meta` namespace. If you have deployed Loki in a different namespace, or the monitoring stack in a namespace other than `meta`, you will need to update several keys in `values-chart-v3.yaml`. There is no single `namespaces` key; namespace scoping is set independently for each feature:

   | Key | Format | Purpose |
   | --- | --- | --- |
   | `integrations.loki.instances[0].namespaces` | list | Namespace(s) to discover Loki instances in. |
   | `integrations.alloy.instances[0].namespaces` | list | Namespace(s) to discover the monitoring stack's own Alloy collectors in. |
   | `clusterEvents.namespaces` | list | Namespace(s) to capture Kubernetes events from. |
   | `clusterMetrics.cadvisor.metricsTuning.includeNamespaces` | list | Namespace(s) to keep cadvisor metrics for. This is a metric filter, not a discovery scope. |
   | `clusterMetrics.kube-state-metrics.namespaces` | list, or comma-separated string | Namespace(s) to collect Kubernetes resource state from. |
   | `podLogs.namespaces` | list | Namespace(s) to collect pod logs from. |

   For example, to add a namespace to the Loki discovery scope:

   ```yaml
   integrations:
     loki:
       instances:
         - name: loki
           namespaces:
             - loki
   ```

   Each of these keys defaults to an empty list, which means "collect from all namespaces". Removing a key restores that default for that feature only; it does not affect the other keys in this table.

   Also note that `destinations[].secret.namespace` (used for both the `metrics` and `logs` destinations) must match the namespace where you created the `metrics` and `logs` secrets, which is `meta` by default. Update it if you installed the monitoring stack into a different namespace.

1. Deploy the Kubernetes Monitoring Helm chart using the modified `values-chart-v3.yaml` file, pinning the chart to the 3.x line:

   ```bash
   helm install meta-loki grafana/k8s-monitoring \
    --namespace meta \
    --version "^3" \
    -f values-chart-v3.yaml
   ```

1. Verify that the Kubernetes Monitoring Helm chart has been deployed successfully:

   ```bash
    kubectl get pods -n meta
    ```

    You should see a list of pods running in the `meta` namespace.

    ```console
    NAME                                           READY   STATUS    RESTARTS ...        
    meta-loki-alloy-singleton-6d7f8d8b86-sg4wx     2/2     Running   0        ...       
    meta-loki-kube-state-metrics-64bdcfcbd-5snqz   1/1     Running   0        ...       
    meta-loki-node-exporter-855l5                  1/1     Running   0        ...       
    meta-loki-node-exporter-b976b                  1/1     Running   0        ...       
    meta-loki-node-exporter-vsm4s                  1/1     Running   0        ...
    ```

## Next Steps

You have successfully deployed the Kubernetes Monitoring Helm chart to monitor your Loki cluster. You can now move onto the next step of deploying the Loki mixin to visualize the metrics and logs from your Loki cluster. For more information, see [Install the Loki Mixin](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/mixins).

# Deploy Loki meta-monitoring

The primary method for collecting and monitoring a Loki cluster is to use the [Kubernetes Monitoring Helm](https://github.com/grafana/k8s-monitoring-helm/) chart. This chart provides a comprehensive monitoring solution for Kubernetes clusters and includes direct integrations for monitoring the full LGTM (Loki, Grafana, Tempo, and Mimir) stack. This procedure will walk you through deploying the Kubernetes Monitoring Helm chart to monitor your Loki cluster.

{{< admonition type="note" >}}
We recommend running a production cluster of Loki in distributed mode using Kubernetes. This procedure assumes you have a running Kubernetes cluster and a running Loki deployment. There are other methods for deploying Loki, such as using Docker or VM installations. meta-monitoring is still possible when using these deployment methods but not covered in this procedure. If you run Loki in monolithic mode outside of this Helm chart, refer to [Single binary meta-monitoring](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/single-binary/) instead.
{{< /admonition >}}

{{< admonition type="note" >}}
This page covers chart version 4.x, the current line. If you need to stay on the maintained 3.8.x stable line instead, use [Deploy Loki meta-monitoring with chart version 3](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/deploy-chart-v3/).
{{< /admonition >}}

## Prerequisites

- [kubectl](https://kubernetes.io/docs/reference/kubectl/)
- Helm 3 or above. See [Installing Helm](https://helm.sh/docs/intro/install/).
- A running Kubernetes cluster with a running Loki deployment.
- A Grafana Cloud account or a separate LGTM stack for monitoring.

## Preparing your environment

Before deploying the Kubernetes Monitoring Helm chart, you need to set up several components in your environment.

1. Add the Grafana Helm repository:

   ```bash
   helm repo add grafana https://grafana.github.io/helm-charts
   ```

1. Update your Helm repositories:

   ```bash
   helm repo update
   ```

1. Create a namespace for the monitoring stack:

   ```bash
   kubectl create namespace meta
   ```

### Authentication

The Kubernetes Monitoring Helm chart requires a Grafana Cloud account or a separate LGTM stack for monitoring. You will need to provide the necessary credentials to the Helm chart to authenticate with your Grafana Cloud account or LGTM stack. In this procedure, we will use Grafana Cloud as an example.

1. Create a new Cloud Access Policy in Grafana Cloud.
    1. Sign into [Grafana Cloud](https://grafana.com/auth/sign-in/).
    1. In the main menu, select **Security > Access Policies**.
    1. Click **Create access policy**.
    1. Give the policy a **Name** and select the following permissions:
       - Metrics: Write
       - Logs: Write
    1. Click **Create**.
    1. Click **Add Token**. Give the token a name and click **Create**.
   Save the token for later use.
1. Collect `url` and `username` for Prometheus and Loki.
   1. Navigate to the Grafana Cloud Portal **Overview** page.
   1. Click the **Details** button for your Prometheus instance.
        1. From the **Sending metrics using Grafana Alloy** section, collect the instance **username** and **url**.
        1. Navigate back to the **Overview** page.
   1. Click the **Details** button for your Loki instance.
        1. From the **Sending Logs to Grafana Cloud using Grafana Alloy** section, collect the instance **username** and **url**.
        2. Navigate back to the **Overview** page.

1. Create the Kubernetes Secrets with the collected credentials from Grafana Cloud.

   ```bash
   kubectl create secret generic metrics -n meta \
    --from-literal=username=<PROMETHEUS-USER> \
    --from-literal=password=<CLOUD-TOKEN>

   kubectl create secret generic logs -n meta \
    --from-literal=username=<LOKI-USER> \
    --from-literal=password=<CLOUD-TOKEN>
   ```

Note that the Kubernetes Monitoring Helm supports many different authentication methods based upon your requirements including:

- Bearer Tokens
- OAuth2
- SigV4
- External Secrets
  
For further information on how to configure these methods, see the [Kubernetes Monitoring Helm examples](https://github.com/grafana/k8s-monitoring-helm/tree/main/charts/k8s-monitoring/docs/examples/auth).

## Deploy the Kubernetes Monitoring Helm chart

Now that you have prepared your environment and collected the necessary credentials, you can deploy the Kubernetes Monitoring Helm chart to monitor your Loki cluster. To do this we need to copy a `values.yaml` file to our local machine and modify it to include the necessary configuration.

1. Download the `values.yaml` file from the Kubernetes Monitoring Helm chart repository:

   ```bash
   curl -O https://raw.githubusercontent.com/grafana/loki/main/production/helm/meta-monitoring/values.yaml
   ```

1. Open the `values.yaml` file in a text editor of your choosing and add the Prometheus and Loki endpoints. Chart 4.x configures destinations as a map keyed by name, rather than a list:

   ```yaml
   destinations:
     prometheus:
       type: prometheus
       url: https://<PROMETHEUS-ENDPOINT>/api/prom/push
       auth:
         type: basic
         usernameKey: username
         passwordKey: password
       secret:
         create: false
         name: metrics
         namespace: meta

     loki:
       type: loki
       url: https://<LOKI-ENDPOINT>/loki/api/v1/push
       auth:
         type: basic
         usernameKey: username
         passwordKey: password
       secret:
         create: false
         name: logs
         namespace: meta

   ```

1. (Optional) Update the cluster name to a human-readable name to identify your cluster in Grafana Cloud.

   ```yaml
   # Global Label to be added to all telemetry data. Should reflect a recognizable name for the cluster.
   cluster:
     name: loki-meta-monitoring-cluster
   ```

1. Confirm that at least one collector is defined under `collectors:`. Chart 4.x requires this; the install fails if `collectors:` is empty or missing. The default `values.yaml` already defines the `alloy-singleton` collector that every feature in this file is assigned to:

   ```yaml
   collectors:
     alloy-singleton:
       presets: [singleton]
   ```

   If you add or rename a collector, update every `collector: alloy-singleton` reference in the file to match, or the install fails with an error naming the missing collector.

1. The default values file assumes that you have deployed Loki in the `loki` namespace and will deploy the Kubernetes monitoring stack in the `meta` namespace. If you have deployed Loki in a different namespace, or the monitoring stack in a namespace other than `meta`, you will need to update several keys in `values.yaml`. There is no single `namespaces` key; namespace scoping is set independently for each feature:

   | Key | Format | Purpose |
   | --- | --- | --- |
   | `integrations.loki.instances[0].namespaces` | list | Namespace(s) to discover Loki instances in. |
   | `integrations.alloy.instances[0].namespaces` | list | Namespace(s) to discover the monitoring stack's own Alloy collectors in. |
   | `clusterEvents.namespaces` | list | Namespace(s) to capture Kubernetes events from. |
   | `clusterMetrics.cadvisor.metricsTuning.includeNamespaces` | list | Namespace(s) to keep cadvisor metrics for. This is a metric filter, not a discovery scope. |
   | `telemetryServices.kube-state-metrics.namespaces` | list, or comma-separated string | Namespace(s) to collect Kubernetes resource state from. In chart 4.x this key must be set under `telemetryServices`, not under `clusterMetrics`. |
   | `podLogsViaKubernetesApi.namespaces` | list | Namespace(s) to collect pod logs from. |

   For example, to add a namespace to the Loki discovery scope:

   ```yaml
   integrations:
     loki:
       instances:
         - name: loki
           namespaces:
             - loki
   ```

   Each of these keys defaults to an empty list, which means "collect from all namespaces". Removing a key restores that default for that feature only; it does not affect the other keys in this table.

   Also note that `destinations.prometheus.secret.namespace` and `destinations.loki.secret.namespace` must match the namespace where you created the `metrics` and `logs` secrets, which is `meta` by default. Update them if you installed the monitoring stack into a different namespace.

1. Deploy the Kubernetes Monitoring Helm chart using the modified `values.yaml` file, pinning the chart to the 4.x line:

   ```bash
   helm install meta-loki grafana/k8s-monitoring \
    --namespace meta \
    --version "^4" \
    -f values.yaml
   ```

1. Verify that the Kubernetes Monitoring Helm chart has been deployed successfully:

   ```bash
    kubectl get pods -n meta
    ```

    You should see a list of pods running in the `meta` namespace, similar to the following. Exact pod names and counts depend on your cluster size and which features you have enabled:

    ```console
    NAME                                           READY   STATUS    RESTARTS ...
    meta-loki-alloy-operator-7d8f9c6b5d-x2k9p      1/1     Running   0        ...
    meta-loki-alloy-singleton-6d7f8d8b86-sg4wx     2/2     Running   0        ...
    meta-loki-kube-state-metrics-64bdcfcbd-5snqz   1/1     Running   0        ...
    meta-loki-node-exporter-855l5                  1/1     Running   0        ...
    meta-loki-node-exporter-b976b                  1/1     Running   0        ...
    meta-loki-node-exporter-vsm4s                  1/1     Running   0        ...
    ```

## Next Steps

You have successfully deployed the Kubernetes Monitoring Helm chart to monitor your Loki cluster. You can now move onto the next step of deploying the Loki mixin to visualize the metrics and logs from your Loki cluster. For more information, see [Install the Loki Mixin](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/mixins).

# Key metrics for monitoring Loki

Loki exposes many metrics, and each component behaves differently under load. This page focuses on the highest-signal metrics for detecting negative trends early.

{{< admonition type="note" >}}
The example queries on this page are PromQL. Run them against the Prometheus-compatible data source where your Loki metrics are stored (for example, Prometheus, Mimir, or Grafana Cloud Metrics).
{{< /admonition >}}

For setup and prebuilt dashboards and alerts, refer to:

- [Deploy Loki meta-monitoring](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/deploy/)
- [Install dashboards, alerts, and recording rules](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/mixins/)

## Request error rate

Watch request failures first. A sustained increase in 5xx responses is usually the earliest sign of user-visible impact.

Key metric:

- `loki_request_duration_seconds`, a histogram with the labels `method`, `route`, `status_code`, and `ws`. Use the `_count` and `_bucket` suffixes to derive request rate and latency percentiles.

{{< admonition type="note" >}}
`cluster`, `namespace`, and `job` in the example queries on this page come from your Prometheus scrape configuration, not from Loki itself. They are not labels on the metric.
{{< /admonition >}}

Example query:

```promql
100 * sum(rate(loki_request_duration_seconds_count{status_code=~"5.."}[2m])) by (cluster, namespace, job, route)
/
sum(rate(loki_request_duration_seconds_count[2m])) by (cluster, namespace, job, route)
```

Abnormal behavior:

- Any sustained increase in 5xx ratio.
- The Loki mixin alert `LokiRequestErrors` fires when this ratio is greater than 10% for 15 minutes.

## Request latency (p99)

Latency degradation can appear before hard failures. Track p99 for read and write routes.

Key metric:

- `loki_request_duration_seconds_bucket` (histogram buckets of the same metric described in [Request error rate](#request-error-rate))

Example query:

```promql
histogram_quantile(0.99, sum(rate(loki_request_duration_seconds_bucket[1m])) by (le, cluster, namespace, job, route))
```

Abnormal behavior:

- Rising p99 over time, especially in query-frontend and distributor paths.
- The Loki mixin alert `LokiRequestLatency` fires when p99 exceeds 1 second for 15 minutes. This alert excludes tail and scheduler routes, so the example query above can show a higher p99 than the alert reacts to.

## Panics

Panics are high-severity faults and should stay at zero.

Key metric:

- `loki_panic_total`

Example query:

```promql
sum(increase(loki_panic_total[10m])) by (cluster, namespace, job)
```

Abnormal behavior:

- Any value above zero. The Loki mixin alert `LokiRequestPanics` treats this as critical.

## Discarded samples

Discarded samples indicate data that Loki rejected or dropped. This is one of the most important ingestion-quality signals.

Key metric:

- `loki_discarded_samples_total` (counter with labels including `tenant` and `reason`, plus `retention_hours`, `policy`, and `format`)

Example query:

```promql
topk(10, sum by (tenant, reason) (rate(loki_discarded_samples_total{cluster="$cluster", namespace="$namespace"}[$__rate_interval])))
```

Abnormal behavior:

- Increasing discard rate.
- New or growing `reason` values (for example, tenant limits or stream limits).

## Compaction health

Compaction issues can silently degrade read performance and retention behavior over time.

{{< admonition type="note" >}}
Prior to Loki 4.0 the compaction and retention metrics used a `loki_boltdb_shipper_` prefix for historical reasons.
The compactor emitted these metrics regardless of which index type you use, including TSDB.
After removing the legacy BoltDB Shipper index, the prefix has been renamed to `loki_tsdb_shipper_`.
{{< /admonition >}}

Key metrics:

- `loki_tsdb_shipper_compactor_running`
- `loki_tsdb_shipper_compact_tables_operation_last_successful_run_timestamp_seconds`
- `loki_tsdb_shipper_compact_tables_operation_total`
- `loki_tsdb_shipper_compact_tables_operation_duration_seconds`

Example queries:

```promql
sum(loki_tsdb_shipper_compactor_running) by (cluster, namespace)
```

```promql
time() - (loki_tsdb_shipper_compact_tables_operation_last_successful_run_timestamp_seconds > 0)
```

Abnormal behavior:

- More than one compactor running. Only one compactor instance should run at a time. Running more than one can lead to data loss. The Loki mixin alert `LokiTooManyCompactorsRunning` fires when more than one compactor runs for 5 minutes.
- No successful compaction for multiple hours. The mixin alert `LokiCompactorHasNotSuccessfullyRunCompaction` triggers when the last successful run is more than 3 hours old, and fires after that condition holds for 1 additional hour, so it takes about 4 hours in total to alert.

## Ingester health and flush behavior

Ingester pressure often appears as memory growth, poor chunk utilization, or flush backlog.

Key metrics:

- `loki_ingester_memory_streams`
- `loki_ingester_memory_stream_shards`
- `loki_ingester_memory_chunks`
- `loki_ingester_flush_queue_length`
- `loki_ingester_chunk_utilization`
- `loki_ingester_chunks_flushed_total`

Example queries:

```promql
sum(loki_ingester_memory_streams{cluster="$cluster", namespace="$namespace"})
```

The share of in-memory streams that were created by stream sharding in the distributors:

```promql
sum(loki_ingester_memory_stream_shards{cluster="$cluster", namespace="$namespace"})
/
sum(loki_ingester_memory_streams{cluster="$cluster", namespace="$namespace"})
```

```promql
sum(loki_ingester_flush_queue_length{cluster="$cluster", namespace="$namespace"})
```

Abnormal behavior:

- Persistent growth in in-memory streams or chunks.
- Increasing flush queue length.
- Low chunk utilization for long periods.

## Distributor throughput

Throughput changes help identify upstream sender issues, sudden traffic shifts, or ingestion bottlenecks.

Key metrics:

- `loki_distributor_bytes_received_total`
- `loki_distributor_lines_received_total`

Example queries:

```promql
sum(rate(loki_distributor_bytes_received_total{cluster="$cluster", namespace="$namespace"}[$__rate_interval]))
```

```promql
sum(rate(loki_distributor_lines_received_total{cluster="$cluster", namespace="$namespace"}[$__rate_interval]))
```

Abnormal behavior:

- Sharp drops (possible data path interruption).
- Unexpected spikes (possible overload or noisy tenants).

## Kafka-based ingestion (experimental)

{{< admonition type="note" >}}
These metrics only apply if you've enabled [Kafka-based ingestion](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/kafka/), an experimental write-path feature.
{{< /admonition >}}

**Key metrics:**

- `loki_kafka_client_partition_reader_consumption_lag_seconds`: the single most important Kafka health signal. Growing lag means ingesters can't keep up with the rate at which the distributor produces records.
- `loki_distributor_kafka_appends_total` (label `status`): produce success and failure rate. A rising failure rate means writes aren't reaching Kafka.
- `loki_distributor_kafka_latency_seconds`: produce latency. A sustained increase points to Kafka broker pressure or network issues.
- `loki_kafka_client_partition_reader_fetch_errors_total`: errors fetching records from Kafka brokers. This should stay near zero.

**Example queries:**

```promql
# Consumption lag p99 by partition state
histogram_quantile(0.99, sum(rate(loki_kafka_client_partition_reader_consumption_lag_seconds_bucket{phase="running", cluster="$cluster", namespace="$namespace"}[$__rate_interval])) by (le, cluster, namespace))
```

```promql
# Kafka produce error rate
sum(rate(loki_distributor_kafka_appends_total{status="fail", cluster="$cluster", namespace="$namespace"}[$__rate_interval])) by (cluster, namespace)
```

**Abnormal behavior:**

- Consumption lag growing over time (ingesters can't keep up with the produce rate).
- Any sustained produce failures.
- Rising produce latency (broker pressure, network issues, or too few partitions).
- Fetch errors (broker unavailability or a protocol version mismatch).

For a complete list of Kafka metrics, refer to [Kafka-based ingestion](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/kafka/#monitoring-kafka-ingestion).

## Object store operations

Object store latency and failures directly impact query and retention workflows.

Key metrics:

- `loki_objstore_bucket_operations_total`
- `loki_objstore_bucket_operation_failures_total`
- `loki_objstore_bucket_operation_duration_seconds`

Example queries:

```promql
sum by (operation) (rate(loki_objstore_bucket_operation_failures_total{cluster="$cluster", namespace="$namespace"}[$__rate_interval]))
```

```promql
histogram_quantile(0.99, sum(rate(loki_objstore_bucket_operation_duration_seconds_bucket{cluster="$cluster", namespace="$namespace"}[$__rate_interval])) by (le, operation))
```

Abnormal behavior:

- Growing failure rates by operation.
- Increasing p99 latency for `get`, `get_range`, or `upload`.

Connection metrics just for S3:

- `net_conntrack_dialer_conn_attempted_total`
- `net_conntrack_dialer_conn_established_total`
- `net_conntrack_dialer_conn_failed_total`
- `net_conntrack_dialer_conn_closed_total`

Abnormal behavior:

- Non-zero rate of failed connections.
- High rate of closed connections under ordinary load.

## Resource and runtime health

Resource pressure can explain or predict service degradation before alert thresholds are crossed.

Common signals to track:

- Container CPU usage
- Container memory working set
- Go heap in use
- Disk read and write rates
- Container restarts

Abnormal behavior:

- Repeated restart spikes.
- Sustained CPU saturation.
- Memory growth without recovery.

## Loki Canary (end-to-end data verification)

If you run Loki Canary, use it as an end-to-end correctness signal, not only a performance signal.

Key metrics:

- `loki_canary_missing_entries_total`
- `loki_canary_spot_check_missing_entries_total`
- `loki_canary_response_latency_seconds_bucket`

Example query:

```promql
sum(increase(loki_canary_missing_entries_total{cluster=~"$cluster", namespace=~"$namespace"}[$__range]))
/
sum(increase(loki_canary_entries_total{cluster=~"$cluster", namespace=~"$namespace"}[$__range]))
* 100
```

Abnormal behavior:

- Any non-zero missing rate sustained over time.

## Internal error log rate

Internal logs provide fast context when metrics indicate degradation.

Key metric:

- `loki_internal_log_messages_total`

Use this metric with component logs to correlate where failures begin.

## Retention and sweeper progress

Retention and sweeper lag can cause storage growth and delayed data lifecycle actions.

Key metrics:

- `loki_compactor_apply_retention_last_successful_run_timestamp_seconds`
- `loki_tsdb_shipper_retention_sweeper_marker_file_processing_current_time`
- `loki_tsdb_shipper_retention_sweeper_chunk_deleted_duration_seconds_count`

Example query:

```promql
time() - (loki_tsdb_shipper_retention_sweeper_marker_file_processing_current_time{cluster="$cluster", namespace="$namespace"} > 0)
```

Abnormal behavior:

- Increasing sweeper lag.
- Falling delete throughput or sustained delete failures.

## Next steps

- Install and keep the latest Loki mixin dashboards and alerts.
- Build component-specific alerts on top of these baseline signals.
- Pair metrics with Loki component logs, including `metrics.go`, during incident response.

# Install dashboards, alerts, and recording rules

Loki is instrumented to expose metrics about itself via the `/metrics` endpoint, designed to be scraped by Prometheus. Each Loki release includes a mixin. The Loki mixin provides a set of Grafana dashboards, Prometheus recording rules and alerts for monitoring Loki.

To set up monitoring using the mixin, you need to:

1. Deploy the Kubernetes Monitoring Helm chart. Follow the instructions in the [Deploy Loki Meta-monitoring](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/deploy) documentation.
1. Be actively storing metrics from your Loki cluster in Grafana Cloud or a separate LGTM stack.

This procedure assumes that you have set up Loki using the Helm chart.

{{< admonition type="note" >}}
Be sure to update the commands and configuration to match your own deployment.
{{< /admonition >}}

## Install Loki dashboards in Grafana

After Loki metrics are scraped by the Kubernetes Monitoring Helm chart and stored in a Prometheus compatible time-series database, you can monitor Loki’s operation using the Loki mixin.

Each Loki release includes a mixin that includes:

- Relevant dashboards for overseeing the health of Loki as a whole, as well as its individual Loki components
- [Recording rules](https://grafana.com/docs/loki/<LOKI_VERSION>/alert/#recording-rules) that compute metrics that are used in the dashboards
- Alerts that trigger when Loki generates metrics that are outside of normal parameters

To install the mixins in Grafana and a Prometheus-compatible rules store, the general steps are as follows:

1. Download the mixin dashboards from the Loki repository.

1. Import the dashboards in your Grafana instance.

1. Upload `alerts.yaml` and `rules.yaml` files to Prometheus or Mimir with `mimirtool`.

### Download the `loki-mixin` dashboards

1. First, clone the Loki repository from Github:

   ```bash
   git clone https://github.com/grafana/loki
   cd loki
   ```

1. Check out the tag that matches the version of Loki you are running, so that the dashboards, alerts, and recording rules match the metrics your Loki cluster exposes. For example, if you are running Loki 3.7.6:

   ```bash
   git checkout v3.7.6
   ```

1. Once you have a local copy of the repository at the matching version, navigate to the `production/loki-mixin-compiled` directory.

   ```bash
   cd production/loki-mixin-compiled
   ```

This directory contains a compiled version of the alert and recording rules, as well as the dashboards.

{{< admonition type="note" >}}
If you want to change any of the mixins, make your updates in the `production/loki-mixin` directory.
Use the instructions in the [README](https://github.com/grafana/loki/tree/main/production/loki-mixin) in that directory to regenerate the files.
{{< /admonition >}}

### Import the dashboards to Grafana

The `dashboards` directory includes the monitoring dashboards that can be installed into your Grafana instance.
Refer to [Import a dashboard](https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/import-dashboards/) in the Grafana documentation.

{{< admonition type="note" >}}
The dashboards and alerts expect your metrics to carry these labels: `cluster`, `namespace`, `job`, `container`, `pod`, and `instance`. The Kubernetes Monitoring Helm chart provides them, because it adds the cluster name as a global label and collects container and node metrics using cAdvisor, kube-state-metrics, and Node Exporter.

If your setup uses different label names, or does not collect container and node metrics, some panels and alerts show no data until you relabel your metrics to match.
{{< /admonition >}}

{{< admonition type="tip" >}}
Install all dashboards.
You can only import one dashboard at a time.
Create a new folder in the Dashboards area, for example “Loki Monitoring”, as an easy location to save the imported dashboards.
{{< /admonition >}}

To create a folder:

1. Open your Grafana instance and select **Dashboards**.
1. Click the **New** button.
1. Select **New folder** from the **New** menu.
1. Name your folder, for example, “Loki Monitoring”.
1. Click **Create**.

To import a dashboard:

1. Open your Grafana instance and select **Dashboards**.
1. Click the **New** button.
1. Select **Import** from the **New** menu.
1. On the **Import dashboard** screen, select **Upload dashboard JSON file.**
1. Browse to `production/loki-mixin-compiled/dashboards` and select the dashboard to import. Or, drag the dashboard file, for example, `loki-operational.json`, onto the **Upload** area of the **Import dashboard** screen.
1. Select a folder in the **Folder** menu where you want to save the imported dashboard. For example, select "Loki Monitoring" created in the earlier steps.
1. Click **Import**.

The imported files are listed in the Loki Monitoring dashboard folder.

To view the dashboards in Grafana:

1. Select **Dashboards** in your Grafana instance.
1. Select **Loki Monitoring**, or the folder where you uploaded the imported dashboards.
1. Select any file in the folder to view the dashboard.

### Add alerts and recording rules to Prometheus or Mimir

The rules and alerts need to be installed into a Prometheus instance, Mimir or a Grafana Enterprise Metrics cluster.

You can find the YAML files for alerts and rules in the following directories in the Loki repo:

* `production/loki-mixin-compiled/alerts.yaml` (Optional)
* `production/loki-mixin-compiled/rules.yaml` (Required for some dashboard panels to show data)

You use `mimirtool` to load the mixin alerts and rules definitions into a Prometheus instance, Mimir, or a Grafana Enterprise Metrics cluster. The following examples show how to load the mixin alerts and rules into a Grafana Cloud instance.

1. Download [mimirtool](https://github.com/grafana/mimir/releases).

1. Export the authentication credentials for connecting to your Grafana Cloud Mimir instance.

   ```bash
   export MIMIR_ADDRESS=<CLOUD-MIMIR-URL>
   export MIMIR_API_USER=<CLOUD-MIMIR-USER>
   export MIMIR_API_KEY=<CLOUD-MIMIR-API-KEY>
   export MIMIR_TENANT_ID=<CLOUD-MIMIR-USER> # Same as MIMIR_API_USER when using Grafana Cloud
   ```

   The best place to locate these credentials is to:
   1. Sign into [Grafana Cloud](https://grafana.com/auth/sign-in/) and create a new access policy.
       1. In the main menu, select **Security > Access Policies**.
       1. Click **Create access policy**.
       1. Give the policy a **Name** and select the following permissions:
          - Alerts: Write & Read
          - Rules: Write & Read
       1. Click **Create**.
       1. Click **Add Token**. Give the token a name and click **Create**.
   1. Collect `URL` and `user` for Prometheus
       1. Navigate to the Grafana Cloud Portal **Overview** page.
       1. Click the **Details** button for your Alerts instance.
       1. From the **Configuring your Alerting Stacks** section, collect the instance **User** and **URL** for **Metrics Authentication Settings**.

1. Using the same terminal we exported the Mimir environment variables into earlier, run the following command to load the recording rules:

    ```bash
    mimirtool rules load rules.yaml
    ```

1. (Optional) Load the alert rules:

    ```bash
    mimirtool rules load alerts.yaml
    ```

Refer to the [mimirtool](https://grafana.com/docs/mimir/latest/manage/tools/mimirtool/) documentation for more information.

## Next steps

After you have installed the Loki mixin dashboards, alerts, and recording rules, you can now monitor your production Loki cluster using Grafana. Make sure you review the mixins when you upgrade Loki to make sure you are using the latest version of the mixin.

You can now move onto:

* **Send Logs:** Ready to start sending your own logs to Loki, there a several methods you can use. For more information, refer to [send data](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/).
* **Query Logs:** LogQL is an extensive query language for logs and contains many tools to improve log retrieval and generate insights. For more information see the [Query section](https://grafana.com/docs/loki/<LOKI_VERSION>/query/).
* **Alert:** Lastly you can use the ruler component of Loki to create alerts based on log queries. For more information refer to [Alerting](https://grafana.com/docs/loki/<LOKI_VERSION>/alert/).

# Single binary meta-monitoring

Meta monitoring for [monolithic mode](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/#monolithic-mode) deployments involves some additional configuration. This approach does not use the Kubernetes Monitoring Helm chart.

## Metrics

Configure Alloy or Prometheus to scrape the Loki metrics endpoint, adding the additional labels that are expected by the [mixin](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/mixins) dashboards, alerts, and recording rules:

### Alloy

```alloy
prometheus.scrape "loki" {
  targets = [{
    __address__ = "localhost:3100",
    cluster     = "prod",
    namespace   = "default",
    job         = "default/loki-single-binary",
    pod         = "loki-single-binary",
    container   = "loki",
  }]
  forward_to = [prometheus.remote_write.default.receiver]
}

prometheus.remote_write "default" {
  endpoint {
    url = "<PROMETHEUS_REMOTE_WRITE_URL>"
  }
}
```

### Prometheus

```yaml
scrape_configs:
  - job_name: loki
    static_configs:
      - targets: ['localhost:3100']
        labels:
          cluster: prod
          namespace: default
          job: default/loki-single-binary
          pod: loki-single-binary
          container: loki
```

## Mixins

To generate the [mixins](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/mixins):

1. Install [jb](https://github.com/jsonnet-bundler/jsonnet-bundler).
1. Install [mixtool](https://github.com/monitoring-mixins/mixtool).
1. Clone the Loki repository from Github.

    ```bash
    git clone https://github.com/grafana/loki
    cd loki
    ```

1. Check out the tag that matches the version of Loki you are running, so that the dashboards, alerts, and recording rules match the metrics your Loki cluster exposes. For example, if you are running Loki 3.7.6:

    ```bash
    git checkout v3.7.6
    ```

1. Create the output directory.

    ```bash
    mkdir production/loki-mixin-compiled-single-binary
    ```

1. Navigate to the mixin directory.

    ```bash
    cd production/loki-mixin
    ```

1. Use `jb` to install mixin dependencies and generate the `vendor` directory.

    ```bash
    jb install
    ```

1. Create the mixin configuration file `single-binary.libsonnet`.

    ```bash
    cat <<EOF > single-binary.libsonnet
    local loki = import 'mixin.libsonnet';

    loki + {
      _config+:: {
        meta_monitoring+: {
          enabled: true,
        },
        canary+: {
          // Whether or not to include the loki-canary dashboard.
          // Set to false if you don't run loki-canary.
          enabled: true,
        },
        promtail+: {
          // Whether or not to include promtail specific dashboards
          enabled: false,
        },
        // Tunes histogram recording rules to aggregate over this interval.
        // Set to at least twice the scrape interval; otherwise, recording rules will output no data.
        // Set to four times the scrape interval to account for edge cases: https://www.robustperception.io/what-range-should-i-use-with-rate/
        recording_rules_range_interval: '5m',
      },
    }
    EOF
    ```

1. Generate dashboards, alerts, and recording rules.

    ```bash
    mixtool generate all \
      --output-alerts ../loki-mixin-compiled-single-binary/alerts.yaml \
      --output-rules ../loki-mixin-compiled-single-binary/rules.yaml \
      --directory ../loki-mixin-compiled-single-binary/dashboards \
      single-binary.libsonnet
    ```

1. See the generated dashboards, alerts, and recording rules in the `production/loki-mixin-compiled-single-binary` directory.
1. Follow the instructions in the [Install Mixins](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/mixins) documentation to set up the dashboards, alerts, and recordings.

# Horizontal scaling of Compactor

{{< admonition type="caution" >}}
Compactor horizontal scaling is an experimental feature. Use it with caution in your production environments.
{{< /admonition >}}

Grafana Loki saw a major improvement in its operational complexity and cost-effectiveness with the introduction of object-storage-based indexes.
This change also led to the addition of a singleton Compactor service, initially responsible only for index compaction.
However, as new features like [Custom Retention](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/retention/) and [Deletion of Logs with line filters](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/logs-deletion/) were introduced, the Compactor's responsibilities grew.
With increasing scale and more demanding features, especially log deletion with line filters, the singleton Compactor began to show its scaling limits.

Now, you can run the Loki Compactor in a horizontally scalable mode.
Since log deletion with line filters is the compactor's most operationally intensive work, initially,
this horizontally scalable architecture will be leveraged to speed up and distribute the workload specifically for its log deletion with line filters functionality.

## How compactor horizontal scaling works

We have introduced two new modes to the Compactor for operating in horizontally scalable mode:

1. Main:

   * Runs all Compactor functions and distributes chunk processing for log line deletion with filters to the workers.
   * Should be deployed as a singleton with access to a disk, like the current singleton deployment.
2. Worker:

   * Connects to the Main Compactor over gRPC to get and execute the jobs.
   * Multiple replicas can be deployed to achieve higher job processing throughput.
   * Only needs access to Object Storage for reading/writing chunks.

### Compactor horizontal scaling implementation details

Although, Horizontally Scalable Compactor currently only supports distributing the work of log line deletion with filters,
in the future, we might add support for distributing more kind of work to the Workers.
We are going to use the current functionality to see in detail some of the implementation details.

#### Main mode

For Distribution of Chunk processing work from Main compactor, there are following core components involved:

1. **Deletion Manifest Builder**: The manifest builder works on a batch of delete request to discover all the chunks covered by them based on their labels filters and time range.
Using the discovered chunks, it creates structured manifests and stores it to the Object Storage. Here is what comprises Deletion Manifest:

   - *Segments*: Groups of up to 100K chunks per segment. Also acts as partition for chunks by Loki Tenant/Table.
   - *Manifest*: Complete metadata about all segments and requests to process.

2. **Job Builder**: The job builder converts manifests into discrete jobs and manages their lifecycle:

   - *Job Creation*: Breaks segments into jobs of up to 1K chunks each. Also includes line filters to apply on the chunks to remove the log lines requested for deletion.
   - *Progress Tracking*: Monitors job completion and stops processing of manifest when any of the jobs fail.
   - *Job Queueing*: Sends jobs to the Job Queue for processing.
   - *Storage Updates*: Collects storage updates suggested by Workers and stores them to Object Storage for each segment.
   - *Post-processing Cleanup*: Marks requests as processed and removes all the files from the storage.

3. **Job Queue**: The job queue manages job distribution and Worker-Job Builder communication.

   - *Job Distribution*: Sends jobs to available workers via gRPC streaming.
   - *Retry Logic*: Automatically retries failed or timed-out jobs until allowed number of attempts.
   - *Job Response*: Sends the job processing response to Job Builder. Also notifies about failed jobs after running out of retries.

#### Worker mode

Workers connect to the Main Compactor via gRPC to fetch and execute jobs, returning results on the same gRPC stream.
It uses `compactor_grpc_address` under the [common config](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#common) to connect to the Main Compactor.

When handling Deletion jobs, a worker downloads the listed chunks, applies the specified filters to rebuild them without the filtered lines, and then provides a comprehensive storage update as job execution response.
The storage update details which chunks to delete from Object Storage and which newly created chunks to index.

## Configure compactor mode

The `horizontal_scaling_mode` configuration option in the compactor controls the enablement of horizontally scalable compactor deployment.
It supports setting the following modes:

- **`disabled`** (default): Keeps the horizontal scaling mode disabled. Locally runs all the functions of the compactor.
- **`main`**: Runs all functions of the compactor. Distributes work to workers where possible.
- **`worker`**: Runs the compactor in worker mode, only working on jobs built by the main compactor.

### Configure Main mode

To run Compactor in Main mode, the Horizontal Scaling Mode needs to be set to "main":

```yaml
compactor:
  # CLI flag: -compactor.horizontal-scaling-mode
  horizontal_scaling_mode: "main"
```

Additionally, there are the below config options available for the Main compactor to configure some aspects of Job Building:

```yaml
compactor:
  jobs_config:
    deletion:
      # Object storage path prefix for storing deletion manifests.
      # CLI flag: -compactor.jobs.deletion.deletion-manifest-store-prefix
      [deletion_manifest_store_prefix: <string> | default = "__deletion_manifest__/"]
      
      # Maximum time to wait for a job before considering it failed and retrying.
      # CLI flag: -compactor.jobs.deletion.timeout
      [timeout: <duration> | default = 15m]
      
      # Maximum number of times to retry a failed or timed out job.
      # CLI flag: -compactor.jobs.deletion.max-retries
      [max_retries: <int> | default = 3]
```

### Configure Worker mode

To run Compactor in Worker mode, the Horizontal Scaling Mode needs to be set to "worker" and Main compactor's GRPC address needs to be set:

```yaml
common:
  compactor_grpc_address: <HOST>:<GRPC_PORT>
compactor:
  # CLI flag: -compactor.horizontal-scaling-mode
  horizontal_scaling_mode: "worker"
```

Additionally, there are the below config options available for the Worker to configure some aspects of Job execution:

```yaml
compactor:
  jobs_config:
    deletion:
      # Maximum number of chunks to process concurrently in each worker.
      # CLI flag: -compactor.jobs.deletion.chunk-processing-concurrency
      [chunk_processing_concurrency: <int> | default = 3]

  worker_config:
    # Number of sub-workers to run for concurrent processing of jobs. This
    # value must be greater than 0, or the compactor fails to start.
    # CLI flag: -compactor.worker.num-sub-workers
    [num_sub_workers: <int> | default = 4]
```

# Filesystem object store

The filesystem object store is the easiest way to get started with Grafana Loki. This topic describes its pros and cons.

The filesystem object store keeps every object, such as chunks, in a directory that you specify. The recommended way to configure this directory is in the `common.storage.filesystem` block:

```yaml
common:
  storage:
    filesystem:
      chunks_directory: /tmp/loki/chunks
      rules_directory: /tmp/loki/rules
```

You can also configure the chunks directory directly under `storage_config`:

```yaml
storage_config:
  filesystem:
    directory: /tmp/loki/
```

Loki creates a folder for every tenant. All the chunks for one tenant are stored in that tenant's folder.

If you run Loki in single-tenant mode, Loki puts all the chunks in a folder named `fake`. This is the synthesized tenant name that Loki uses in single-tenant mode.

See [multi-tenancy](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/multi-tenancy/) for more information.

{{< admonition type="note" >}}
Loki also supports an alternative, opt-in filesystem client based on `thanos-io/objstore`. Enable it by setting `use_thanos_objstore: true` and configuring `storage_config.object_store.filesystem.dir`. This client will become the default way to configure object store clients in a future release. For configuration examples, see [Thanos storage examples](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/examples/thanos-storage-configs/) and the [storage client migration guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/migrate/migrate-storage-clients/).
{{< /admonition >}}

## Pros

The filesystem object store is very simple. It requires no additional software to run Loki, and it works with TSDB, which is the recommended index store.

It's great for low volume applications, proof of concepts, and just playing around with Loki.

## Cons

Grafana Labs does not support the filesystem object store for production environments, including for customers who have purchased a support contract.

### Scaling

At some point there is a limit to how many chunks you can store in a single directory. For example, see [issue #1502](https://github.com/grafana/loki/issues/1502), which explains how a Loki user ran into a strange error with about **5.5 million chunk files** in their file store. That issue also describes a workaround for the problem.

Loki writes one chunk per stream, so keeping the number of active streams low reduces the number of chunk files. You can also tune the following ingester settings to reduce how many chunks get flushed, although lower flush rates trade off for higher memory consumption:

- `chunk_target_size` (default 1.5 MB): a target compressed size for each chunk.
- `max_chunk_age` (default 2h): the maximum time a chunk stays in memory before Loki flushes it. Consider increasing this beyond the default.
- `chunk_idle_period` (default 30m): how long an idle chunk stays in memory before Loki flushes it. Consider increasing this to match `max_chunk_age`.

It's still possible to store terabytes of log data with the filesystem store, but keep in mind the limitations on how many files a filesystem can efficiently store in a single directory.

### Durability

The durability of your objects depends entirely on the underlying filesystem. Other object stores, such as S3 and GCS, do a lot of work behind the scenes to offer much higher durability for your data.

### High availability

Running Loki as a cluster is not possible with the filesystem store, unless you share the filesystem in some way, for example over NFS. Using a shared filesystem is likely to give you a poor experience with Loki, just as it does with almost every other application.

### Retention and deletion

When you use the filesystem chunk store, Loki does not delete chunks based on disk usage or free space. Loki only deletes data according to your retention configuration, so you must handle disk-full scenarios yourself, outside of Loki.

Retention-based deletion runs through the compactor. When you enable retention with `compactor.retention_enabled`, you must also set `compactor.delete_request_store`. Set it to `filesystem` to store delete requests in this object store.

For more information, see [Retention](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/retention/) and the [storage retention section](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/storage/#retention).

# Log entry deletion

Grafana Loki supports the deletion of log entries from a specified stream.
Log entries that fall within a specified time window and match an optional line filter are those that will be deleted.

Log entry deletion is supported when the TSDB index is configured as the index store. It is also supported on the deprecated BoltDB Shipper index, but BoltDB Shipper is being removed in Loki 4.0, so new deployments should use TSDB.

The compactor component exposes REST [endpoints](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-http-api#compactor) that process delete requests.
Hitting the endpoint specifies the streams and the time window.
The deletion of the log entries takes place after a configurable cancellation time period expires.

Log entry deletion relies on configuration of the custom logs retention workflow as defined for the [compactor](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/retention/#compactor). The compactor looks at unprocessed requests which are past their cancellation period to decide whether a chunk is to be deleted or not.

## Configuration

Enable log entry deletion by setting `retention_enabled` to `true` in the compactor's configuration (`-compactor.retention-enabled` on the command line). The tenant's `deletion_mode` must also be `filter-only` or `filter-and-delete`, which is the case by default.
`delete_request_store` also needs to be configured when retention is enabled to process delete requests, this determines the storage bucket that stores the delete requests.

{{< admonition type="warning" >}}
Be very careful when enabling retention. It is strongly recommended that you also enable versioning on your objects in object storage to allow you to recover from accidental misconfiguration of a retention setting. If you want to enable deletion but do not want to enforce retention, configure the `retention_period` setting with a value of `0s`.
{{< /admonition >}}

`deletion_mode` is a global and per-tenant setting in [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config). Its default value is `filter-and-delete`, so log entry deletion is active for every tenant as soon as `retention_enabled` is `true` and `delete_request_store` is configured, unless you explicitly change the mode. Set `deletion_mode` in the main configuration file for a global default, or override it per tenant in the [runtime configuration file](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#runtime-configuration-file).

`deletion_mode` supports three values:

- `disabled`: Deletion is not allowed. Requests to the deletion API endpoints are rejected with a `403 Forbidden` response.
- `filter-only`: Log lines matching the query in the delete request are filtered out when querying Loki. They are not removed from storage.
- `filter-and-delete`: Log lines matching the query in the delete request are filtered out when querying Loki, and they are also removed from storage. This is the default.

A delete request may be canceled within a configurable cancellation period. Set the `delete_request_cancel_period` in the compactor's YAML configuration or on the command line when invoking Loki. Its default value is 24h. To cancel a delete request that has already started processing, pass the `force=true` query parameter to the cancellation endpoint.

Delete requests that include a line filter are split into smaller requests that each cover no more than `delete_max_interval` (24h by default). You can request a smaller split for a single request with the `max_interval` query parameter, but it cannot be larger than `delete_max_interval`. Delete requests without a line filter are not split. Refer to the [HTTP API reference](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-http-api/#request-log-deletion) for the full set of request parameters.

Delete requests themselves are stored using the database type set by `delete_request_store_db_type`, which defaults to `boltdb`. You can instead use `sqlite`. When you migrate from one database type to another, you can set `backup_delete_request_store_db_type` to `boltdb` so that delete requests are also written to a backup database. Refer to the [compactor configuration reference](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#compactor) for details.

The deletion API endpoints are registered only when `compactor.retention_enabled` is `true`. When retention is not enabled, the endpoints are unavailable for every tenant, whatever the `deletion_mode` value is. When retention is enabled, use the `deletion_mode` tenant override to control which tenants can use the deletion API.

{{< admonition type="note" >}}
Deletion of log lines with line filters is one of the compactor's most resource-intensive operations. If you delete large volumes of data with line filters, refer to [Horizontal scaling of Compactor](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/compactor-horizontal-scaling/) to distribute that work across multiple compactor instances.
{{< /admonition >}}

# Log retention

Retention in Grafana Loki is achieved through the [Compactor](#compactor).
By default the `compactor.retention-enabled` flag is not set, so the logs sent to Loki live forever.

{{< admonition type="note" >}}
If you have a lifecycle policy configured on the object store, please ensure that it is longer than the retention period.
{{< /admonition >}}

Granular retention policies to apply retention at per tenant or per stream level are also supported by the Compactor.

## Compactor

The Compactor is responsible for compaction of index files and applying log retention.

{{< admonition type="note" >}}
Run the Compactor as a singleton (a single instance).
{{< /admonition >}}

The Compactor runs compaction and retention on two independent schedules:

- Compaction runs every `compactor.compaction-interval`.
- Retention runs every `compactor.apply-retention-interval`. If you leave this at its default of `0s`, Loki sets it to the same value as `compactor.compaction-interval`. Whenever the two intervals are equal, Loki extends the retention interval by the smaller of `10m` or half the interval, so that compaction and retention do not run at the same time. For example, with the default `compaction_interval` of `10m`, retention runs every `15m`.

If the Compactor falls behind on either schedule, it runs the corresponding operation as soon as possible.
Both compaction and retention are idempotent, which means once the action has been performed, if the action is performed multiple times, it has no further effect on logs after the first time it is performed. If the Compactor restarts, it continues from where it left off.

{{< admonition type="note" >}}
Changes to your retention period are not retroactive, that is, they are not applied to logs that have already been ingested.
{{< /admonition >}}

The Compactor's algorithm to apply retention is as follows:

- For each day or table (one table per day with 24h index period):
  - Compact multiple index files in the table into per-tenant index files. Result of compaction is a single index file per tenant per day.
  - Traverse the per-tenant index. Use the tenant configuration to identify the chunks that need to be removed.
  - Remove the references to the matching chunks from the index and add the chunk references to a marker file on disk.
  - Upload the new modified index files.

Chunks are not deleted while applying the retention algorithm on the index. They are deleted asynchronously by a sweeper process
and this delay can be configured by setting `-compactor.retention-delete-delay`. Marker files are used to keep track of the chunks pending for deletion.

Chunks cannot be deleted immediately for the following reasons:

- Index Gateway downloads a copy of the index files to serve queries and refreshes them at a regular interval.
  Having a delay allows the index gateways to pull the modified index file which would not contain any reference to the chunks marked for deletion.
  Without the delay, index files (that are stale) on the gateways could refer to already deleted chunks leading to query failures.

- It provides a short window of time in which to cancel chunk deletion in the case of a configuration mistake.

By default, marker files are written to local disk, under `<working_directory>/retention/<object_store_type>_<period_from_date>/markers/`. Marker files should be stored on a persistent disk to ensure that the chunks pending for deletion are processed even if the Compactor process restarts.
{{< admonition type="note" >}}
Grafana Labs recommends running Compactor as a stateful deployment (StatefulSet when using Kubernetes) with a persistent storage for storing marker files.
{{< /admonition >}}

Alternatively, you can configure the Compactor to store marker files in object storage instead of local disk, by setting `compactor.deletion-marker-object-store-prefix` (`deletion_marker_object_store_prefix`) to a prefix that ends with `/`. When set, the Compactor writes marker files to the same per-period object store bucket used for chunks, under that prefix. On startup it also copies any existing marker files from local disk to that prefix, then removes the local copies. This lets you run the Compactor without a persistent volume for marker files. Leave this setting empty to keep using local disk.

### Retention Configuration

This Compactor configuration example activates retention.

```yaml
compactor:
  working_directory: /data/retention
  compaction_interval: 10m
  retention_enabled: true
  retention_delete_delay: 2h
  retention_delete_worker_count: 150
  delete_request_store: gcs
schema_config:
  configs:
    - from: "2020-07-31"
      index:
        period: 24h
        prefix: index_
      object_store: gcs
      schema: v13
      store: tsdb
storage_config:
  tsdb_shipper:
    active_index_directory: /data/index
    cache_location: /data/index_cache
  gcs:
    bucket_name: loki
```

{{< admonition type="note" >}}
Retention is only available if the index period is 24h. Single store TSDB requires a 24h index period.
{{< /admonition >}}

`retention_enabled` should be set to true. Without this, the Compactor will only compact tables.

`delete_request_store` should be set to configure the store for delete requests. This is required when retention is enabled.

`working_directory` is the directory where marked chunks and temporary tables will be saved.

`compaction_interval` dictates how often compaction is applied. If the Compactor falls behind, compaction occurs as soon as possible.

`apply_retention_interval` dictates how often retention is applied, independently of `compaction_interval`. Refer to [Compactor](#compactor) for how the default value is derived from `compaction_interval`.

`retention_delete_delay` is the delay after which the Compactor will delete marked chunks.

`retention_delete_worker_count` specifies the maximum quantity of goroutine workers instantiated to delete chunks.

A few other options are also relevant when tuning retention:

- `retention_table_timeout` bounds how long the Compactor spends applying retention and processing delete requests on any single table. It defaults to `0s`, meaning no timeout. If the work on a table exceeds this timeout, the Compactor logs a warning and stops early rather than failing, and the remaining work is picked up again on a later run.
- `retention_backoff_config` controls the retry behavior (minimum and maximum backoff, and number of retries) used by the sweeper when it fails to delete a marked chunk.
- `delete_request_store_key_prefix` sets the path prefix used to store delete requests within `delete_request_store` (default `index/`).
- `delete_request_store_db_type` sets the type of database used to store delete requests (default `boltdb`).

Refer to the [Configuration reference](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#compactor) for the full list of `compactor` options and their defaults.

#### Configuring the retention period

Retention period is configured within the [`limits_config`](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config) configuration section.

There are two ways of setting retention policies:

- `retention_period` which is applied globally for all log streams.
- `retention_stream` which is only applied to log streams matching the selector.

{{< admonition type="note" >}}
Each `retention_stream` rule's `period` must be at least 24h. Loki rejects configuration with a shorter value, both in the global `retention_stream` list and in any per-tenant override. This minimum does not apply to the `retention_period` setting, either globally or per tenant.
{{< /admonition >}}

This example configures global retention that applies to all tenants (unless overridden by configuring per-tenant overrides):

```yaml
...
limits_config:
  retention_period: 744h
  retention_stream:
  - selector: '{namespace="dev"}'
    priority: 1
    period: 24h
runtime_config:
  file: /etc/overrides.yaml
...
```

{{< admonition type="note" >}}
You can only use label matchers in the `selector` field of a `retention_stream` definition. Arbitrary LogQL expressions are not supported.
{{< /admonition >}}

Per tenant retention can be defined by configuring [runtime overrides](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#runtime-configuration-file). For example:

```yaml
overrides:
    "29":
        retention_period: 168h
        retention_stream:
        - selector: '{namespace="prod"}'
          priority: 2
          period: 336h
        - selector: '{container="loki"}'
          priority: 1
          period: 72h
    "30":
        retention_stream:
        - selector: '{container="nginx", level="debug"}'
          priority: 1
          period: 24h
```

Retention period for a given stream is decided based on the first match in this list:

1. If multiple per-tenant `retention_stream` selectors match the stream, retention period with the highest priority is picked.
2. If multiple global `retention_stream` selectors match the stream, retention period with the highest priority is picked. This value is not considered if per-tenant `retention_stream` is set.
3. If a per-tenant `retention_period` is specified, it will be applied.
4. The global `retention_period` will be applied if none of the above match.
5. If no global `retention_period` is specified, the default value of `0s` is used, which means logs are kept indefinitely.

{{< admonition type="note" >}}
The larger the priority value, the higher the priority. If two matching `retention_stream` rules have the same priority, Loki applies the shorter (lowest) of the two periods.
{{< /admonition >}}

Stream matching uses the same syntax as Prometheus label matching:

- `=`: Select labels that are exactly equal to the provided string.
- `!=`: Select labels that are not equal to the provided string.
- `=~`: Select labels that regex-match the provided string.
- `!~`: Select labels that do not regex-match the provided string.

The example configurations defined above will result in the following retention periods:

- For tenant `29`:
  - Streams that have the namespace label `prod` will have a retention period of `336h` (2 weeks), even if the container label is `loki`, since the priority of the `prod` rule is higher.
  - Streams that have the container label `loki` but are not in the namespace `prod` will have a `72h` retention period.
  - For the rest of the streams in this tenant, per-tenant override `retention_period` value of `168h` is applied.
- For tenant `30`:
  - Streams that have the label `nginx` and level `debug` will have a retention period of `24h`.
  - For the rest of the streams in this tenant the global retention period of `744h`, since there is no override specified.
- All tenants except `29` and `30`:
  - Streams that have the namespace label `dev` will have a retention period of `24h` hours.
  - Streams except those with the namespace label `dev` will have the retention period of `744h`.

{{< admonition type="note" >}}
If you are a Grafana Cloud customer, you can use the [config self-serve API](https://grafana.com/docs/grafana-cloud/send-data/logs/config-self-serve/#configure-retention) to configure your tenant retention.
{{< /admonition >}}

## Example Configuration

Example configuration with GCS with a 28 day retention:

```yaml
schema_config:
  configs:
    - from: 2018-04-15
      store: tsdb
      object_store: gcs
      schema: v13
      index:
        prefix: loki_index_
        period: 24h

storage_config:
  tsdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/index_cache
  gcs:
    bucket_name: GCS_BUCKET_NAME

limits_config:
  max_query_lookback: 672h # 28 days
  retention_period: 672h   # 28 days

compactor:
  working_directory: /data/retention
  delete_request_store: gcs
  retention_enabled: true
```

## Object store lifecycle policies

Loki stores chunks and indexes as objects in the configured object store (S3, GCS, Azure Blob Storage, and so on). Chunks are deleted by the [Compactor](#compactor) according to your configured retention, but the Compactor only removes chunk objects: it never deletes the objects that hold cluster state, such as the index or the cluster seed.

If you also configure an object store lifecycle policy on the bucket, scope it carefully. A blanket "delete everything older than N days" rule will eventually remove objects that Loki must keep, which corrupts the store and causes query failures. The note at the top of this page applies here as well: any lifecycle expiration must be longer than your retention period.

{{< admonition type="warning" >}}
Never apply a lifecycle rule with an empty prefix (one that targets the whole bucket). Always scope lifecycle rules to the per-tenant chunk prefixes described below.
{{< /admonition >}}

### Bucket object layout

The following objects are written to the configured bucket. The exact prefixes depend on your `schema_config` and on whether multi-tenancy is enabled, so confirm them against your own configuration before writing a lifecycle rule.

| Object or prefix | Content | Safe to expire with a lifecycle policy? |
|---|---|---|
| `<tenant_id>/` (for example, `fake/` in single-tenant mode, or your org ID per tenant) | Chunk objects (the log data). | Yes. These are the only objects a lifecycle rule should target. |
| Index objects (under the index path prefix, `index/` by default, set by `index.path_prefix` in `schema_config`) | TSDB or BoltDB index files, written by ingesters and rewritten by the Compactor. The `index.prefix` value (for example `index_` or `loki_index_`) is the table-name prefix used inside this path, not the path itself, so match a lifecycle rule against the path prefix. | No. Loki manages the index lifecycle internally. Deleting index objects removes references to live chunks and breaks queries. |
| `loki_cluster_seed.json` | A small file at the bucket root holding the cluster seed used by usage reporting. It is written once and never rotated. | No. A blanket age-based rule will delete it once it ages past the time to live (TTL). |
| Delete-request store objects (a `delete_requests/` table under `delete_request_store_key_prefix`, `index/` by default, in the bucket configured by `delete_request_store`) | The Compactor's database of delete requests submitted through the [Log entry deletion](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/logs-deletion/) API, and their processing status. | No. Removing these makes the Compactor lose track of pending delete requests. |
| Deletion marker objects (only present if you set `deletion_marker_object_store_prefix`; otherwise markers are kept on the Compactor's local disk and never appear in the bucket) | The Compactor's record of chunks that are marked for deletion but not yet swept. Written under the configured prefix in the same per-period bucket used for chunks. | No. Removing these makes the Compactor lose track of chunks pending deletion. |
| Ruler store objects (if the Ruler is backed by the same bucket) | Recording and alerting rule files. | No. These are configuration, not time-series data. |

### Compactor retention versus object store lifecycle policies

Compactor retention and object store lifecycle policies are two independent mechanisms, and they should not be configured in ways that conflict:

- The Compactor is index-aware. It only deletes a chunk after it has removed every reference to that chunk from the index, and it waits `compactor.retention-delete-delay` (default `2h`) before the sweeper deletes the marked chunk, so that index gateways pick up the rewritten index first.
- An object store lifecycle policy is index-unaware. It deletes objects purely by age and prefix, with no knowledge of whether a chunk is still referenced by the index.

For most deployments, enabling Compactor retention is sufficient and an object store lifecycle policy is not required. If you do add a lifecycle policy as a cost safety net, set its expiration **longer** than your retention period plus `compactor.retention-delete-delay`, so the Compactor always deletes chunks first and the lifecycle rule only ever catches objects the Compactor has already orphaned.

### Example: Amazon S3 lifecycle policy

This policy expires only chunk objects under a single tenant prefix. Replace `fake/` with your tenant prefix (use one rule per tenant, or your org ID prefix, when multi-tenancy is enabled). The `Days` value must be larger than your retention period plus the delete delay.

```json
{
  "Rules": [
    {
      "ID": "loki-chunk-expiration",
      "Status": "Enabled",
      "Filter": {
        "Prefix": "fake/"
      },
      "Expiration": {
        "Days": 395
      }
    }
  ]
}
```

Apply it with the AWS CLI:

```bash
aws s3api put-bucket-lifecycle-configuration \
  --bucket YOUR_LOKI_BUCKET \
  --lifecycle-configuration file://lifecycle.json
```

### Example: Google Cloud Storage lifecycle rule

The equivalent GCS rule expires objects by age and prefix. As with S3, scope `matchesPrefix` to the tenant chunk prefix and keep `age` larger than your retention period plus the delete delay.

```json
{
  "rule": [
    {
      "action": {"type": "Delete"},
      "condition": {
        "age": 395,
        "matchesPrefix": ["fake/"]
      }
    }
  ]
}
```

Apply it with the Google Cloud CLI:

```bash
gcloud storage buckets update gs://YOUR_LOKI_BUCKET --lifecycle-file=lifecycle.json
```

For Azure Blob Storage, use a management policy with a `prefixMatch` set to the tenant chunk prefix and the same "expiration must exceed retention plus delete delay" rule.

# Single Store TSDB (tsdb)

Starting with Loki v2.8, TSDB is the recommended Loki index. It is heavily inspired by the Prometheus TSDB [sub-project](https://github.com/prometheus/prometheus/tree/main/tsdb). This new index is more efficient, faster, and more scalable. It also resides in object storage like the [boltdb-shipper](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/boltdb-shipper/) index which preceded it.

For the on-disk binary layout of the index files, see [TSDB index format](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/).

## Example Configuration

To get started using TSDB, add the following configurations to your `config.yaml`:

```yaml
schema_config:
  configs:
    # Old boltdb-shipper schema. Included in example for reference but does not need changing.
    - from: "2023-01-03" # <---- A date in the past
      index:
        period: 24h
        prefix: index_
      object_store: gcs
      schema: v12
      store: boltdb-shipper
    # New TSDB schema below
    - from: "2023-01-05" # <---- A date in the future
      index:
        period: 24h
        prefix: index_
      object_store: gcs
      schema: v13
      store: tsdb

storage_config:
  # Old boltdb-shipper configuration. Included in example for reference but does not need changing.
  boltdb_shipper:
    active_index_directory: /data/index
    build_per_tenant_index: true
    cache_location: /data/boltdb-cache
    index_gateway_client:
      # only applicable if using microservices where index-gateways are independently deployed.
      # This example is using kubernetes-style naming.
      server_address: dns:///index-gateway.<namespace>.svc.cluster.local:9095
  # New tsdb-shipper configuration
  tsdb_shipper:
    active_index_directory: /data/tsdb-index
    cache_location: /data/tsdb-cache
    index_gateway_client:
      # only applicable if using microservices where index-gateways are independently deployed.
      # This example is using kubernetes-style naming.
      server_address: dns:///index-gateway.<namespace>.svc.cluster.local:9095

query_scheduler:
  # the TSDB index dispatches many more, but each individually smaller, requests. 
  # We increase the pending request queue sizes to compensate.
  max_outstanding_requests_per_tenant: 32768

querier:
  # Each `querier` component process runs a number of parallel workers to process queries simultaneously.
  # You may want to adjust this up or down depending on your resource usage
  # (more available cpu and memory can tolerate higher values and vice versa),
  # but we find the most success running at around `16` with tsdb
  max_concurrent: 16

```

## Operations

### Limits

We've added a user per-tenant limit called `tsdb_max_query_parallelism` in the `limits_config`. This functions the same as the prior `max_query_parallelism` configuration but applies to tsdb queries instead. Since the TSDB index will create many more smaller queries compared to the other index types before it, we've added a separate configuration so they can coexist. This is helpful when transitioning between index types. The default parallelism is `128` which should work well for most cases, but you can extend it globally in the `limits_config` or per-tenant in the `overrides` file as needed.

### Dynamic Query Sharding

Previously we would statically shard queries based on the index row shards configured [here](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#period_config).
TSDB does Dynamic Query Sharding based on how much data a query is going to be processing.
We additionally store size(KB) and number of lines for each chunk in the TSDB index which is then used by the [Query Frontend](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/components/#query-frontend) for planning the query.
Based on our experience from operating many Loki clusters, we have configured TSDB to aim for processing 300-600 MBs of data per query shard.
This means with TSDB we will be running more, smaller queries.

The per-tenant `tsdb_max_bytes_per_shard` setting in `limits_config` controls this target size. It defaults to `600MB`. Each time a query would need to process more data than this target, Loki doubles the number of shards for that query. As a result, each shard typically ends up processing between half and all of the target amount. This is why, with the default target of `600MB`, query shards typically process between 300 MB and 600 MB of data each.

Loki also supports the following related per-tenant settings in `limits_config`:

- `tsdb_sharding_strategy`: The sharding strategy to use during query planning. The default is `power_of_two`, the strategy described above. An alternative, `bounded`, is also available; refer to the [configuration reference](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config) before switching, since not all Loki components in a cluster may support it during a rolling upgrade.
- `tsdb_precompute_chunks`: When set to `true`, Loki computes chunk references once during query planning instead of resolving them again from the index during query execution. This can reduce the number of index calls a query makes, at the cost of higher memory usage. It defaults to `false`. This setting only takes effect when `tsdb_sharding_strategy` is set to `bounded`, because the `power_of_two` strategy does not support precomputed chunk references.

### Index Caching not required

TSDB is a compact and optimized format. Loki does not currently use an index cache for TSDB. If you are already using Loki with other index types, it is recommended to keep the index caching until all of your existing data falls out of [retention](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/retention/) or your configured `max_query_lookback` under [limits_config](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config). After that, we suggest running without an index cache (it isn't used in TSDB).

# Write Ahead Log

Ingesters temporarily store data in memory. In the event of a crash, there could be data loss. The Write Ahead Log (WAL) helps fill this gap in reliability.

The WAL in Grafana Loki records incoming data and stores it on the local file system in order to guarantee persistence of acknowledged data in the event of a process crash. Upon restart, Loki will "replay" all of the data in the log before registering itself as ready for subsequent writes. This allows Loki to maintain the performance and cost benefits of buffering data in memory _and_ durability benefits (it won't lose data once a write has been acknowledged).

This section will use Kubernetes as a reference deployment paradigm in the examples.

## Disclaimer and WAL nuances

The Write Ahead Log in Loki takes a few particular tradeoffs compared to other WALs you may be familiar with. The WAL aims to add additional durability guarantees, but _not at the expense of availability_. Particularly, there are two scenarios where the WAL sacrifices these guarantees.

1. Corruption/Deletion of the WAL prior to replaying it

    In the event the WAL is corrupted/partially deleted, Loki will not be able to recover all of its data. In this case, Loki will attempt to recover any data it can, but will not prevent Loki from starting.

    You can use the Prometheus metric `loki_ingester_wal_corruptions_total` to track and alert when this happens.

1. No space left on disk

    By default, Loki monitors disk usage on the volume that stores the WAL. If usage reaches 90% of capacity, the ingester rejects new writes instead of accepting data it cannot persist. This check runs every 10 seconds and measures the entire filesystem that holds the WAL directory, not just the size of the WAL files. Because of this, you should not share the WAL volume with other data. Writes resume automatically once disk usage drops back below the threshold. You can change the threshold with the `--ingester.wal-disk-full-threshold` flag, or set it to `0` to disable this protection. This throttling behavior isn't available on Windows.

    If you disable this threshold, or if a write reaches the disk in the short window before the check runs, the underlying disk can still become completely full. In that case, Loki will not fail the incoming write, but neither will it log the entry to the WAL. In this case, the persistence guarantees across process restarts will not hold for that entry.

    You can use the Prometheus metric `loki_ingester_wal_disk_full_failures_total` to track and alert when this happens. This counter increases both when disk usage crosses the throttling threshold and when a WAL write fails because the disk is completely full, so a nonzero value means the WAL disk needs attention, but it does not count each individual rejected write. You can also monitor the gauge `loki_ingester_wal_disk_usage_percent` to see current disk usage and how close it is to the threshold.

### Backpressure

The WAL also includes a backpressure mechanism to allow a large WAL to be replayed within a smaller memory bound. This is helpful after bad scenarios (i.e. an outage) when a WAL has grown past the point it may be recovered in memory. In this case, the ingester will track the amount of data being replayed and once it's passed 90% of the `ingester.wal-replay-memory-ceiling` threshold, will flush to storage. When this happens, it's likely that the Loki attempt to deduplicate chunks via content addressable storage will suffer. We deemed this efficiency loss an acceptable tradeoff considering how it simplifies operation and that it should not occur during regular operation (rollouts, rescheduling) where the WAL can be replayed without triggering this threshold. Setting `ingester.wal-replay-memory-ceiling` to `0` disables backpressure entirely.

### Metrics

The following metrics are available for monitoring the WAL:

* `loki_ingester_wal_corruptions_total`: Total number of WAL corruptions encountered
* `loki_ingester_wal_disk_full_failures_total`: Increments when disk usage crosses the `--ingester.wal-disk-full-threshold` throttling threshold, and when a WAL write fails because the disk is completely full. It does not count each individual throttled write.
* `loki_ingester_wal_disk_usage_percent`: Current disk usage, from 0.0 to 1.0, for the volume that stores the WAL. This is only reported when `--ingester.wal-disk-full-threshold` is greater than `0`.
* `loki_ingester_wal_records_logged_total`: Counter for WAL records logged
* `loki_ingester_wal_logged_bytes_total`: Total bytes written to WAL
* `loki_ingester_wal_replay_active`: Set to 1 while an ingester is replaying its WAL and checkpoint on startup, 0 otherwise
* `loki_ingester_wal_replay_duration_seconds`: Time taken to replay the checkpoint and the WAL

## Changes to deployment

1. Since ingesters need to have the same persistent volume across restarts/rollout, all the ingesters should be run on [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) with fixed volumes.

1. The following flags are relevant when deploying with a persistent volume:
    * `--ingester.wal-enabled` enables writing to WAL during ingestion. It defaults to `true`, so this typically doesn't need to be set explicitly.
    * `--ingester.wal-dir` sets the directory where the WAL data should be stored and/or recovered from. Note that this should be on the mounted volume.
    * `--ingester.checkpoint-duration` to the interval at which checkpoints should be created.
    * `--ingester.wal-replay-memory-ceiling` (default 4GB) may be set higher/lower depending on your resource settings. It handles memory pressure during WAL replays, allowing a WAL many times larger than available memory to be replayed. This is provided to minimize reconciliation time after very bad situations, i.e. an outage, and will likely not impact regular operations/rollouts _at all_. We suggest setting this to a high percentage (~75%) of available memory.
    * `--ingester.wal-disk-full-threshold` (default 0.90) sets the fraction of disk usage (0.0 to 1.0) at which the ingester throttles incoming writes to protect WAL durability. Set to `0` to disable this protection. For details, refer to [Disclaimer and WAL nuances](#disclaimer-and-wal-nuances).

## Changes in lifecycle when WAL is enabled

Flushing of data to chunk store during rollouts or scale down is disabled. This is because during a rollout of statefulset there are no ingesters that are simultaneously leaving and joining, rather the same ingester is shut down and brought back again with updated config. Hence flushing is skipped and the data is recovered from the WAL. If you need to ensure that data is always flushed to the chunk store when your pod shuts down, you can set the `--ingester.flush-on-shutdown` flag to `true`.

## Disk space requirements

Based on tests in real world:

* Numbers from an ingester with 5000 series ingesting ~5mb/s.
* Checkpoint period was 5mins.
* disk utilization on a WAL-only disk was steady at ~10-15GB.

You should not target 100% disk utilization.

## Migrating from stateless deployments

The ingester _Deployment without WAL_ and _StatefulSet with WAL_ should be scaled down and up respectively in sync without transfer of data between them to ensure that any ingestion after migration is reliable immediately.

Let's take an example of 4 ingesters. The migration would look something like this:

1. Bring up one stateful ingester `ingester-0` and wait until it's ready (accepting read and write requests).
1. Scale down the old ingester deployment to 3 and wait until the leaving ingester flushes all the data to chunk store.
1. Once that ingester has disappeared from `kc get pods ...`, add another stateful ingester and wait until it's ready. Now you have `ingester-0` and `ingester-1`.
1. Repeat step 2 to reduce remove another ingester from old deployment.
1. Repeat step 3 to add another stateful ingester. Now you have `ingester-0 ingester-1 ingester-2`.
1. Repeat step 4 and 5, and now you will finally have `ingester-0 ingester-1 ingester-2 ingester-3`.

## How to scale up/down

### Scale up

Scaling up is same as what you would do without WAL or StatefulSets. Nothing to change here.

### Scale down

When scaling down, we must ensure existing data on the leaving ingesters are flushed to storage instead of just the WAL. This is because we won't be replaying the WAL on an ingester that will no longer exist and we need to make sure the data is not orphaned.

Consider you have 4 ingesters `ingester-0 ingester-1 ingester-2 ingester-3` and you want to scale down to 2 ingesters, the ingesters which will be shut down according to StatefulSet rules are `ingester-3` and then `ingester-2`.

Hence before actually scaling down in Kubernetes, port forward those ingesters and hit the [`/ingester/shutdown?flush=true`](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-http-api#flush-in-memory-chunks-and-shut-down) endpoint. This will flush the chunks and remove itself from the ring, after which it will register as unready and may be deleted.

After hitting the endpoint for `ingester-2 ingester-3`, scale down the ingesters to 2.

Also you can set the `--ingester.flush-on-shutdown` flag to `true`. This enables chunks to be flushed to long-term storage when the ingester is shut down.

## Additional notes

### Kubernetes hacking

StatefulSets are significantly more cumbersome to work with, upgrade, and so on. Much of this stems from immutable fields on the specification. For example, if one wants to start using the WAL with single store Loki and wants separate volume mounts for the WAL and the TSDB, you may see immutability errors when attempting updates the Kubernetes StatefulSets.

In this case, try `kubectl -n <namespace> delete sts ingester --cascade=false`.
This will leave the Pods alive but delete the StatefulSet.
Then you may recreate the (updated) StatefulSet and one-by-one start deleting the `ingester-0` through `ingester-n` Pods _in that order_, allowing the StatefulSet to spin up new pods to replace them.

#### Scaling Down Using `/flush_shutdown` Endpoint and Lifecycle Hook

1. **StatefulSets for Ordered Scaling Down**: The Loki ingesters should be scaled down one by one, which is efficiently handled by Kubernetes StatefulSets. This ensures an ordered and reliable scaling process, as described in the [Deployment and Scaling Guarantees](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#deployment-and-scaling-guarantees) documentation.

1. **Using PreStop Lifecycle Hook**: During the Pod scaling down process, the PreStop [lifecycle hook](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) triggers the `/flush_shutdown` endpoint on the ingester. This action flushes the chunks and removes the ingester from the ring, allowing it to register as unready and become eligible for deletion.

1. **Using terminationGracePeriodSeconds**: Provides time for the ingester to flush its data before being deleted, if flushing data takes more than 30 minutes, you may need to increase it.

1. **Cleaning Persistent Volumes**: Persistent volumes are automatically cleaned up by leveraging the [enableStatefulSetAutoDeletePVC](https://kubernetes.io/blog/2021/12/16/kubernetes-1-23-statefulset-pvc-auto-deletion/) feature in Kubernetes.

By following the above steps, you can ensure a smooth scaling down process for the Loki ingesters while maintaining data integrity and minimizing potential disruptions.

### Non-Kubernetes or baremetal deployments

* When the ingester restarts for any reason (upgrade, crash, etc), it should be able to attach to the same volume in order to recover back the WAL and tokens.
* 2 ingesters should not be working with the same volume/directory for the WAL.
* A rollout should bring down an ingester completely and then start the new ingester, not the other way around.

# Troubleshoot Logs Drilldown

[//]: # 'Shared content for troubleshooting logs drilldown'
[//]: # 'This content is located in /logs-drilldown/blob/main/docs/sources/shared/troubleshoot-logs-drilldown.md'

{{< docs/shared lookup="troubleshoot-logs-drilldown.md" source="explore-logs" version="next" >}}

# Troubleshoot log ingestion (WRITE)

This guide helps you troubleshoot errors that occur when ingesting logs into Loki and writing logs to storage. When Loki rejects log ingestion requests, it's typically due to exceeding rate limits, violating validation rules, or encountering storage issues.

Before you begin, ensure you have the following:

- Access to Grafana Loki logs and metrics
- Understanding of your log ingestion pipeline (Alloy or custom clients)
- Permissions to configure limits and settings if needed

## Monitoring ingestion errors

All ingestion errors are tracked using Prometheus metrics:

- `loki_discarded_samples_total` - Count of discarded log samples by reason
- `loki_discarded_bytes_total` - Volume of discarded log data by reason

The `reason` label on these metrics indicates the specific error type. Set up alerts on these metrics to detect ingestion problems.

## Rate limit errors

Rate limits protect Loki from being overwhelmed by excessive log volume. When rate limits are exceeded, Loki returns HTTP status code `429 Too Many Requests`.

### Error: `rate_limited`

**Error message:**

`ingestion rate limit exceeded for user <tenant> (limit: <limit> bytes/sec) while attempting to ingest <lines> lines totaling <bytes> bytes, reduce log volume or contact your Loki administrator to see if the limit can be increased`

**Cause:**

The tenant has exceeded their configured ingestion rate limit. This is a global per-tenant limit enforced by the distributor.

**Default configuration:**

- `ingestion_rate_mb`: 4 MB/sec
- `ingestion_burst_size_mb`: 6 MB
- `ingestion_rate_strategy`: "global" (shared across all distributors)

**Resolution:**

* **Increase rate limits** (if you have sufficient cluster resources):

   ```yaml
   limits_config:
     ingestion_rate_mb: 8
     ingestion_burst_size_mb: 12
   ```

* **Reduce log volume** by:

  - Filtering unnecessary log lines using Alloy processing stages
  - Collecting logs from fewer targets
  - Sampling high-volume streams
  - Using Alloy's rate limiting and filtering capabilities

* **Switch to local strategy** (if using single-region deployment):

   ```yaml
   limits_config:
     ingestion_rate_strategy: local
   ```

   {{< admonition type="note" >}}
   This multiplies the effective limit by the number of distributor replicas.
   {{< /admonition >}}

**Properties:**

- Enforced by: Distributor
- Retryable: Yes
- HTTP status: 429 Too Many Requests
- Configurable per tenant: Yes

### Error: `per_stream_rate_limit`

**Error message:**

`Per stream rate limit exceeded (limit: <limit>/sec) while attempting to ingest for stream '<stream_labels>' totaling <bytes>, consider splitting a stream via additional labels or contact your Loki administrator to see if the limit can be increased`

**Cause:**

A single stream has exceeded its own rate limit. Each stream has a rate limit applied to it to prevent individual streams from overwhelming the set of ingesters it is distributed to. This limit is enforced by the ingester, not the distributor.

**Default configuration:**

- `per_stream_rate_limit`: 3 MB/sec
- `per_stream_rate_limit_burst`: 15 MB

**Resolution:**

* **Split the stream** into several smaller streams by adding more distinguishing labels, so the write volume is spread across more streams.

* **Reduce the rate of writes to the stream**, for example with an Alloy `stage.limit` block.

* **Increase the per-stream limits** (we typically recommend no higher than 5MB for `per_stream_rate_limit` and 20MB for `per_stream_rate_limit_burst`):

   ```yaml
   limits_config:
     per_stream_rate_limit: 5MB
     per_stream_rate_limit_burst: 20MB
   ```

**Properties:**

- Enforced by: Ingester
- Retryable: Yes
- HTTP status: 429 Too Many Requests
- Configurable per tenant: Yes

### Error: `stream_limit`

**Error message:**

`maximum active stream limit exceeded when trying to create stream <stream_labels>, reduce the number of active streams (reduce labels or reduce label values), or contact your Loki administrator to see if the limit can be increased, user: <tenant>`

**Cause:**

The tenant has reached the maximum number of active streams. Active streams are held in memory on ingesters, and excessive streams can cause out-of-memory errors.

{{< admonition type="note" >}}
Under normal conditions, this limit only blocks the creation of genuinely new streams. Log lines for streams that are already active continue to be accepted.

However, during ingester scaling (autoscaling, rollouts, or ring changes such as a partition-ingester migration), streams can be rebalanced to ingesters that were not previously handling them. The new ingester has no record of that stream in memory, so it treats the next write for that stream as a stream creation and checks it against its local share of the limit. If that share is already used up, for example because the same tenant is also adding new high-cardinality streams during the rebalance, writes to the already-active stream can be rejected with `stream_limit`, even though the tenant has not added new label cardinality for it.

This effect is temporary. It is bounded by the number of ingesters added or restarted, and it typically resolves once the ring is stable and stream ownership is recalculated. If you see `stream_limit` errors that line up with ingester scaling or rollouts rather than with new label values, this is the likely cause.
{{< /admonition >}}

**Default configuration:**

- `max_global_streams_per_user`: 5000 (globally)
- `max_streams_per_user`: 0 (no local limit per ingester)
- Active stream window: `chunk_idle_period` (default: 30 minutes)

**Terminology:**

- **Active stream**: A stream that has received logs within the `chunk_idle_period` (default: 30 minutes)

**Resolution:**

* **Reduce stream cardinality** by:

  - Using fewer unique label combinations by removing unnecessary labels
  - Avoiding high-cardinality labels (IDs, timestamps, UUIDs)
  - Using structured metadata instead of labels for high-cardinality data

* **Increase stream limits** (if you have sufficient memory):

   ```yaml
   limits_config:
     max_global_streams_per_user: 10000
   ```

{{< admonition type="note" >}}
Do not increase stream limits to accommodate high cardinality labels, this can result in Loki flushing extremely high numbers of small files which will make for extremely poor query performance.  As volume and the size of the infrastructure being monitored increase, it would be expected to increase the stream limit. However even for hundreds of TBs per day of logs you should avoid exceeding 300,000 max global streams per user.
{{< /admonition >}}

* **Check for a recent ingester rollout or scaling event** before increasing limits. If `stream_limit` errors started when ingesters were added, restarted, or migrated, the tenant may not have a real cardinality problem. Increasing the limit will not prevent this transient effect and can mask genuine cardinality growth.

**Properties:**

- Enforced by: Ingester
- Retryable: Yes
- HTTP status: 429 Too Many Requests
- Configurable per tenant: Yes

## Validation errors

Validation errors occur when log data doesn't meet Loki's requirements. These return HTTP status code `400 Bad Request` and are **not retryable**.

### Error: `line_too_long`

**Error message:**

`max entry size <max_size> bytes exceeded for stream <stream_labels> while adding an entry with length <entry_size> bytes`

**Cause:**

A log line exceeds the maximum allowed size.

**Default configuration:**

- `max_line_size`: 256 KB
- `max_line_size_truncate`: false

**Resolution:**

* **Truncate long lines** instead of discarding them:

   ```yaml
   limits_config:
     max_line_size: 256KB
     max_line_size_truncate: true
   ```

* **Increase the line size limit** (not recommended above 256KB):

   ```yaml
   limits_config:
     max_line_size: 256KB
   ```

   {{< admonition type="warning" >}}
   Loki was built as a large multi-user, multi-tenant database and as such this limit becomes very important to maintain stability and performance with many users query the database simultaneously. We strongly recommend against increasing the max_line_size, doing so will make it very difficult to provide consistent query performance and stability of the system without having to throw extremely large amounts of memory and/or increasing the GRPC message size limits to really high levels, both of which will likely lead to poorer performance and worse experiences.
   {{< /admonition >}}

* **Filter or truncate logs before sending** using Alloy processing stages:

   ```alloy
   stage.replace {
     expression = "^(.{10000}).*$"
     replace    = "$1"
   }
   ```

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: `invalid_labels`

**Error message:**

`error parsing labels <labels> with error: <parse_error>`

**Cause:**

Label names or values contain invalid characters or don't follow Prometheus naming conventions:

- Label names must match regex: `[a-zA-Z_][a-zA-Z0-9_]*`
- Label names cannot start with `__` (reserved for Grafana internal use)
- Labels must be valid UTF-8
- Properly escape special characters in label values
- Ensure label syntax follows PromQL format: `{key="value"}`

**Default configuration:**

This validation is always enabled and cannot be disabled.

**Resolution:**

* **Fix label names** in your log shipping configuration:

   ```yaml
   # Incorrect
   labels:
     "123-app": "value"      # Starts with number
     "app-name": "value"     # Contains hyphen
     "__internal": "value"   # Reserved prefix

   # Correct
   labels:
     app_123: "value"
     app_name: "value"
     internal_label: "value"
   ```

* **Use Alloy's relabeling stages** to fix labels:

   ```alloy
   stage.label_drop {
     values = ["invalid_label"]
   }
   
   stage.labels {
     values = {
       app_name = "",
     }
   }
   ```

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: `missing_labels`

**Error message:**

`error at least one label pair is required per stream`

**Cause:**

A log stream was submitted without any labels. Loki requires at least one label pair for each log stream.

**Default configuration:**

This validation is always enabled and cannot be disabled. Every stream must have at least one label.

**Resolution:**

Ensure all log streams have at least one label. Update your log shipping configuration to include labels:

```alloy
loki.write "default" {
  endpoint {
    url = "http://loki:3100/loki/api/v1/push"
  }
}

loki.source.file "logs" {
  targets = [
    {
      __path__ = "/var/log/*.log",
      job      = "myapp",
      environment = "production",
    },
  ]
  forward_to = [loki.write.default.receiver]
}
```

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: `too_far_behind`

These errors occur when log entries arrive in the wrong chronological order.

**Error messages:**

- `entry too far behind, entry timestamp is: <timestamp>, oldest acceptable timestamp is: <cutoff>`

**Cause:**

Logs are being ingested with timestamps that violate Loki's ordering constraints:

- Logs older than half of `max_chunk_age` from the newest entry are rejected

**Default configuration:**

- `unordered_writes`: true (since Loki 2.4). This flag, and `-ingester.unordered-writes`, are deprecated and will be removed in a future major release, after which out-of-order writes will be accepted unconditionally.
- `max_chunk_age`: 2 hours
- Acceptable timestamp window: 1 hour behind the newest entry (half of `max_chunk_age`)

**Resolution:**

* **Ensure timestamps are assigned correctly**:

  - Use a timestamp parsing stage in Alloy if logs have embedded timestamps
  - Avoid letting Alloy assign timestamps at read time for delayed log delivery

   ```alloy
   stage.timestamp {
     source = "timestamp_field"
     format = "RFC3339"
   }
   ```

* **Check for clock skew** between log sources and Loki. Ensure clocks are synchronized across your infrastructure.

**Properties:**

- Enforced by: Ingester
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes, for `unordered_writes`. No, for `max_chunk_age` (global only).

### Error: `greater_than_max_sample_age`

**Error message:**

`entry for stream <stream_labels> has timestamp too old: <timestamp>, oldest acceptable timestamp is: <cutoff>`

**Cause:**

The log entry's timestamp is older than the configured maximum sample age. This prevents ingestion of very old logs.

**Default configuration:**

- `reject_old_samples`: true
- `reject_old_samples_max_age`: 168 hours (7 days)

**Resolution:**

* **Increase the maximum sample age**:

   ```yaml
   limits_config:
     reject_old_samples_max_age: 336h  # 14 days
   ```

* **Fix log delivery delays** causing old timestamps.

* **For historical log imports**, temporarily disable the check per tenant.

* **Check for clock skew** between log sources and Loki. Ensure clocks are synchronized across your infrastructure.

* **Disable old sample rejection** (not recommended):

   ```yaml
   limits_config:
     reject_old_samples: false
   ```

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: `too_far_in_future`

**Error message:**

`entry for stream <stream_labels> has timestamp too new: <timestamp>`

**Cause:**

The log entry's timestamp is further in the future than the configured grace period allows.

**Default configuration:**

- `creation_grace_period`: 10 minutes

**Resolution:**

* **Check for clock skew** between log sources and Loki. Ensure clocks are synchronized across your infrastructure.

* **Verify application timestamps** Validate timestamp generation in your applications.

* **Verify timestamp parsing** in your log shipping configuration.

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: `max_label_names_per_series`

**Error message:**

`entry for stream <stream_labels> has <count> label names; limit <limit>`

**Cause:**

The stream has more labels than allowed.

**Default configuration:**

- `max_label_names_per_series`: 15

**Resolution:**

* **Reduce the number of labels** by:
  - Using structured metadata for high-cardinality data
  - Removing unnecessary labels
  - Combining related labels

{{< admonition type="warning" >}}
We strongly recommend against increasing `max_label_names_per_series`, doing so creates a larger index which hurts query performance as well as opens the door for cardinality explosions. You should be able to categorize your logs with 15 labels or typically much less. In all our years of running Loki, out of thousands of requests to increase this value, the number of valid exceptions we have seen can be counted on one hand.
{{< /admonition >}}

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: `label_name_too_long`

**Error message:**

`stream <stream_labels> has label name too long: <label_name>`

**Cause:**

A label name exceeds the maximum allowed length.

**Default configuration:**

- `max_label_name_length`: 1024 bytes

**Resolution:**

* **Shorten label names** in your log shipping configuration to under the configured limit. You can use abbreviations or shorter descriptive names.

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: `label_value_too_long`

**Error message:**

`stream <stream_labels> has label value too long: <label_value>`

**Cause:**

A label value exceeds the maximum allowed length.

**Default configuration:**

- `max_label_value_length`: 2048 bytes

**Resolution:**

* **Shorten label values** in your log shipping configuration to under the configured limit. You can use hash values for very long identifiers.

* **Use structured metadata** for long values instead of labels.

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: `duplicate_label_names`

**Error message:**

`stream <stream_labels> has duplicate label name: <label_name>`

**Cause:**

The stream has two or more labels with identical names.

**Default configuration:**

This validation is always enabled and cannot be disabled. Duplicate label names are never allowed.

**Resolution:**

* **Remove duplicates** Remove duplicate label definitions in your log shipping configuration to ensure unique label names per stream.

* **Check clients** Check your ingestion pipeline for label conflicts.

* **Verify processing** Verify your label processing and transformation rules.

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: `request_body_too_large`

**Error message:**

`Request body too large: <size> bytes, limit: <limit> bytes`

**Cause:**

The HTTP compressed push request body exceeds the configured limit in your gateway/reverse proxy or service that fronts Loki after decompression.

**Default configuration:**

- Loki server max request body size: Configured at the HTTP server level
- Nginx gateway (Helm): `gateway.nginxConfig.clientMaxBodySize` (default: 4 MB)
- Alloy default batch size: 1 MB

**Resolution:**

* **Split large batches** into smaller, more frequent requests.

* **Increase batch limit** Increase the allowed body size on your gateway/reverse proxy.  For example, in the Helm chart set `gateway.nginxConfig.clientMaxBodySize`; default is 4M.

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 413 Request Entity Too Large
- Configurable per tenant: No

## Storage errors

Storage errors occur when Loki cannot write data to its storage backend or the Write-Ahead Log (WAL).

### Error: Disk space full

**Error message:**

`no space left on device`

**Cause:**

Local storage has run out of space. This can affect ingester WAL, chunk cache, or temporary files.

**Default configuration:**

Disk space is not limited by Loki configuration; it depends on your infrastructure provisioning.

**Resolution:**

* **Free up disk space** on Loki instances.
* **Configure retention policies** to remove old data.
* **Scale storage capacity** by adding more disk space.
* **Monitor disk usage** and set up alerts before reaching critical levels.
* **Review compactor settings** to ensure old data is being cleaned up.

**Properties:**

- Enforced by: Operating system
- Retryable: Yes (after freeing space)
- HTTP status: 500 Internal Server Error
- Configurable per tenant: No

### Error: Chunk write failure

**Error message:**

`failed to store chunks: storage unavailable`

**Common causes and errors:**

* **S3/Object Storage errors:**
  - `NoSuchBucket`: Storage bucket doesn't exist
  - `AccessDenied`: Invalid credentials or permissions
  - `RequestTimeout`: Network or storage latency issues

**Resolution:**

* **Verify storage configuration**:

   ```yaml
   storage_config:
     aws:
       s3: s3://region/bucket-name
       s3forcepathstyle: true
   ```

* **Check credentials and permissions**:
  - Ensure service account has write permissions
  - Verify IAM roles for S3
  - Check API keys for cloud storage

* **Monitor storage health**:
  - Check object storage availability

* **Review storage metrics**:
  - `loki_ingester_chunks_flushed_total`
  - `loki_ingester_chunks_flush_errors_total`

* **Increase retries and timeouts**:

   ```yaml
   storage_config:
     aws:
       s3:
         http_config:
           idle_conn_timeout: 90s
           response_header_timeout: 0s
   ```

**Properties:**

- Enforced by: Ingester (during chunk flush)
- Retryable: Yes (with backoff)
- HTTP status: 500 Internal Server Error
- Configurable per tenant: No

### Error: WAL disk full

**Error logged:**

`Error writing to WAL, disk full, no further messages will be logged for this error`

**Metric:** `loki_ingester_wal_disk_full_failures_total`

**Cause:**

By default, the ingester monitors disk usage on the volume that stores the WAL and rejects new writes once usage reaches 90% of capacity (`-ingester.wal-disk-full-threshold`), so that it never accepts data it cannot persist. This error and the associated log message describe a different, rarer case: the disk still filled up completely, either because this throttling is disabled (threshold set to `0`), or because a write reached the disk in the short window before the check could catch it. In that case, the ingester continues accepting writes but doesn't log them to the WAL, losing durability guarantees for those entries.

**Default configuration:**

- WAL enabled by default
- WAL location: configured by `-ingester.wal-dir`
- Checkpoint interval: `ingester.checkpoint-duration` (default: 5 minutes)
- Disk full throttling threshold: `ingester.wal-disk-full-threshold` (default: 0.9, meaning 90%)

**Resolution:**

* **Increase disk space** for the WAL directory.

* **Monitor disk usage** and set up alerts:

   ```promql
   loki_ingester_wal_disk_full_failures_total > 0
   ```

   This counter increases both when disk usage crosses the throttling threshold and when a WAL write fails because the disk is completely full. It does not count each individual throttled write, so also watch the gauge `loki_ingester_wal_disk_usage_percent` to see how close the disk is to the threshold.

* **Reduce log volume** to decrease WAL growth.

* **Check WAL checkpoint frequency**:

   ```yaml
   ingester:
     checkpoint_duration: 5m
   ```

* **Verify WAL cleanup** is working - old segments should be deleted after checkpointing.

**Properties:**

- Enforced by: Ingester
- Retryable: N/A (writes are accepted but not persisted to WAL)
- HTTP status: N/A (writes succeed, but durability is compromised)
- Configurable per tenant: No

{{< admonition type="note" >}}
This section describes the disk becoming completely full. In normal operation, the ingester's disk usage throttle (`-ingester.wal-disk-full-threshold`, default 90%) rejects writes to that ingester before the disk actually fills up, so that durability guarantees are preserved. Because Loki replicates each write to multiple ingesters, a client write can still succeed even while one ingester is throttled. Use metric `loki_ingester_wal_disk_usage_percent` to monitor disk usage.
{{< /admonition >}}

### Error: WAL corruption

**Error message:**

`encountered WAL read error, attempting repair`

**Metric:** `loki_ingester_wal_corruptions_total`

**Cause:**

The WAL has become corrupted, possibly due to:

- Disk errors
- Process crashes during write
- Filesystem issues
- Partial deletion of WAL files

**Default configuration:**

- WAL enabled by default
- Automatic repair attempted on startup
- WAL location: configured by `-ingester.wal-dir`

**Resolution:**

* **Monitor for corruption**:

   ```promql
   increase(loki_ingester_wal_corruptions_total[5m]) > 0
   ```

* **Automatic recovery**: Loki attempts to recover readable data and continues starting.

* **Investigate root cause**:
  - Check disk health
  - Review system logs for I/O errors
  - Verify filesystem integrity

**Properties:**

- Enforced by: Ingester (on startup)
- Retryable: N/A (Loki attempts automatic recovery)
- HTTP status: N/A (occurs during startup, not request handling)
- Configurable per tenant: No

{{< admonition type="note" >}}
Loki prioritizes availability over complete data recovery. The replication factor provides redundancy if one ingester loses WAL data. Recovered data may be incomplete if corruption is severe.
{{< /admonition >}}

## Network and connectivity errors

These errors occur due to network issues or service unavailability.

### Error: Connection refused

**Error message:**

`connection refused when connecting to loki:3100`

**Cause:**

The Loki service is unavailable or not listening on the expected port.

**Default configuration:**

- Loki HTTP listen port: 3100 (`-server.http-listen-port`)
- Loki gRPC listen port: 9095 (`-server.grpc-listen-port`)

**Resolution:**

* **Verify Loki is running** and healthy.
* **Check network connectivity** between client and Loki.
* **Confirm the correct hostname and port** configuration.
* **Review firewall and security group** settings.
* **Check for CPU starvation** as a Loki pod that is overwhelmed could fail to respond to a request.

**Properties:**

- Enforced by: Network/OS
- Retryable: Yes (after service recovery)
- HTTP status: N/A (connection-level error)
- Configurable per tenant: No

### Error: Context deadline exceeded

**Error message:**

`context deadline exceeded`

**Cause:**

Requests are timing out due to slow response times or network issues.

**Default configuration:**

- Alloy default timeout: 10 seconds
- Loki server timeout: `-server.http-server-write-timeout` (default: 30s)

**Resolution:**

* **Increase timeout values** in your ingestion client.
* **Check Loki performance** and resource utilization.
* **Review network latency** between client and server.
* **Scale Loki resources** if needed.

**Properties:**

- Enforced by: Client/Server
- Retryable: Yes
- HTTP status: 504 Gateway Timeout (or client-side timeout)
- Configurable per tenant: No

### Error: Client canceled request (Code 499)

**Error message:**

`level=warn method=/logproto.Querier/GetChunkIDs duration=<duration> msg=gRPC err="rpc error: code = Code(499) desc = the request was cancelled by the client"`

**Cause:**

A downstream client (such as Grafana, a load balancer, or another Loki component) canceled a gRPC request before the server finished processing it. `Code(499)` is a non-standard gRPC status code derived from the HTTP 499 status ("Client Closed Request"), which Loki uses internally when it detects a Go `context.Canceled` error. Because it is not the standard gRPC `Canceled` code (code 1), the gRPC logging middleware logs it at `warn` level instead of `debug` level.

The `method` field indicates which gRPC operation was interrupted. Common methods include `/logproto.Querier/GetChunkIDs`, `/logproto.Querier/Query`, and `/logproto.Querier/Series`. Although these are query-path operations, the error appears in ingester logs because ingesters serve gRPC read requests from queriers.

Common triggers include:

- A user cancels a running query in Grafana
- A query timeout is reached in the query frontend, query scheduler, or a reverse proxy
- The query frontend cancels redundant split or sharded sub-queries after it has enough results
- A load balancer or ingress controller closes the connection due to its own timeout settings

**Default configuration:**

- Per-tenant query timeout: `query_timeout` in `limits_config` / `-querier.query-timeout` (default: 1m)
- Loki server HTTP write timeout: `-server.http-server-write-timeout` (default: 30s)
- Load balancer / reverse proxy timeout: depends on your infrastructure

**Resolution:**

* **Determine whether this is expected behavior.** Occasional Code(499) warnings are normal in production and indicate that a client disconnected before the server responded. These are safe to ignore if they occur infrequently.

* **Investigate frequent occurrences.** If you see a high rate of Code(499) errors, check for:
  - Queries that are too broad or slow, causing clients to time out
  - Timeout mismatches between layers (for example, a load balancer timeout shorter than the Loki query timeout)
  - Overloaded ingesters or queriers that cannot respond quickly enough

* **Align timeouts across your stack.** Ensure that timeouts increase from outer layers inward so that Loki has time to respond before upstream components give up:

  ```
  Grafana timeout > Load balancer timeout > Query frontend timeout > Querier timeout
  ```

* **Check query performance.** Use Loki metrics to identify slow queries:

   ```promql
   histogram_quantile(0.99, sum(rate(loki_request_duration_seconds_bucket[5m])) by (le, route))
   ```

* **Reduce log noise.** If the warnings are expected and you want to reduce log volume, increase the log level for the affected component to `error` using `-log.level=error`. Note that this suppresses all warn-level messages, not just Code(499) warnings.

**Properties:**

- Enforced by: gRPC logging middleware (dskit)
- Retryable: N/A (the original client is no longer waiting for a response)
- HTTP status: 499 (non-standard, client closed request)
- Configurable per tenant: No

### Error: Kafka producer backpressure

These errors occur only when the distributor is configured to write to Kafka (`-distributor.kafka-writes-enabled=true`),
and indicate that the Kafka producer could not keep up with the incoming write volume.

**Error messages:**

- `records have timed out before they were able to be produced`
- `the maximum amount of records are buffered, cannot buffer more`

**Cause:**

The distributor's Kafka producer wasn't able to deliver records to the Kafka brokers within `kafka_config.write_timeout`,
or the amount of unacknowledged, buffered data reached `kafka_config.producer_max_buffered_bytes`. Both are transient
backpressure conditions: the Kafka brokers or the network path to them are slower than the rate at which the distributor
is trying to produce records, for example during a broker rollout, a partition rebalance, or a broker-side incident.

**Default configuration:**

- `kafka_config.write_timeout` (`-kafka.write-timeout`): 10s
- `kafka_config.producer_max_buffered_bytes` (`-kafka.producer-max-buffered-bytes`): 1 GiB (0 disables the limit)

**Resolution:**

* **Implement retry logic** with exponential backoff in your client.
* **Check Kafka broker health and load** (under-replicated partitions, broker CPU/disk/network saturation, ongoing rollouts).
* **Increase `kafka_config.write_timeout`** if brokers are healthy but consistently slow to acknowledge writes:

   ```yaml
   kafka_config:
     write_timeout: 20s
   ```

   Ensure that this remains less than the distributor's `-distributor.write-timeout` (default: 30s) to avoid distributor-level timeouts.

**Properties:**

- Enforced by: Distributor (Kafka producer)
- Retryable: Yes
- HTTP status: 500 Internal Server Error
- Configurable per tenant: No

### Error: Service unavailable

**Error message:**

`service unavailable (503)`

**Cause:**

Loki is temporarily unable to handle requests due to high load or maintenance. This can occur when ingesters are unhealthy or the ring is not ready.

**Default configuration:**

- Ingester ring readiness: Requires minimum healthy ingesters based on replication factor
- Distributor: Returns 503 when no healthy ingesters available

**Resolution:**

* **Implement retry logic** with exponential backoff in your client.
* **Scale Loki ingester instances** to handle load.
* **Check for resource constraints** (CPU, memory, storage).
* **Review ingestion patterns** for sudden spikes.

**Properties:**

- Enforced by: Distributor/Gateway
- Retryable: Yes
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

## Structured metadata errors

These errors occur when using structured metadata incorrectly.

### Error: Structured metadata disabled

**Error message:**

`stream <stream_labels> includes structured metadata, but this feature is disallowed. Please see limits_config.allow_structured_metadata or contact your Loki administrator to enable it`

**Cause:**

`allow_structured_metadata` defaults to `true`, so this error usually means it has been explicitly disabled, either globally or as a per-tenant override.

**Default configuration:**

- `allow_structured_metadata`: true (enabled by default in recent versions)

**Resolution:**

* **Enable structured metadata**:

   ```yaml
   limits_config:
     allow_structured_metadata: true
   ```

* **Move metadata to regular labels** if structured metadata isn't needed.
* **Contact your Loki administrator** to enable the feature.

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Structured metadata too large

**Error message:**

`stream '{job="app"}' has structured metadata too large: '70000' bytes, limit: '65536' bytes. Please see limits_config.max_structured_metadata_size or contact your Loki administrator to increase it`

**Cause:**

The structured metadata size exceeds the configured limit.

**Default configuration:**

- `max_structured_metadata_size`: 64 KB

**Resolution:**

* **Reduce the size** of structured metadata by removing unnecessary fields.

* **Increase the limit**:

   ```yaml
   limits_config:
     max_structured_metadata_size: 128KB
   ```

* **Use compression or abbreviations** for metadata values.

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Too many structured metadata entries

**Error message:**

`stream '{job="app"}' has too many structured metadata labels: '150', limit: '128'. Please see limits_config.max_structured_metadata_entries_count or contact your Loki administrator to increase it`

**Cause:**

The number of structured metadata entries exceeds the limit.

**Default configuration:**

- `max_structured_metadata_entries_count`: 128

**Resolution:**

* **Reduce the number** of structured metadata entries by consolidating fields.

* **Increase the limit**:

   ```yaml
   limits_config:
     max_structured_metadata_entries_count: 256
   ```

* **Combine related metadata** into single entries using JSON or other formats.

**Properties:**

- Enforced by: Distributor
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

## Blocked ingestion errors

These errors occur when ingestion is administratively blocked.

### Ingestion blocked

**Error message:**

`ingestion blocked for user <tenant> until <time> with status code 260`

Or for policy-specific blocks:

`ingestion blocked for user <tenant> (policy: <policy>) until <time> with status code 260`

**Cause:**

Ingestion has been administratively blocked for the tenant, typically due to policy violations, billing issues, or maintenance. Status code 260 is a custom Loki status code for blocked ingestion.

**Default configuration:**

- `blocked_ingestion_status_code`: 260 (custom status code)
- Blocks are configured per tenant or per policy in runtime overrides

**Resolution:**

* **Wait until the block expires** if a time limit is set.
* **Contact your Loki administrator** to understand the block reason.
* **Review tenant usage patterns** and policies.
* **Check for policy-specific blocks** if using ingestion policies.

**Properties:**

- Enforced by: Distributor
- Retryable: No (until block is lifted)
- HTTP status: 260 (custom) or configured status code
- Configurable per tenant: Yes

## Troubleshooting workflow

Follow this workflow when investigating ingestion issues:

* **Check metrics**:

   ```promql
   # See which errors are occurring
   sum by (reason) (rate(loki_discarded_samples_total[5m]))
   
   # Identify affected tenants
   sum by (tenant, reason) (rate(loki_discarded_bytes_total[5m]))
   ```

* **Review logs** from distributors and ingesters:

   ```bash
   # Enable write failure logging per tenant
   limits_config:
     limited_log_push_errors: true
   ```

* **Test push endpoint**:

   ```bash
   curl -H "Content-Type: application/json" \
        -XPOST http://loki:3100/loki/api/v1/push \
        --data-raw '{"streams":[{"stream":{"job":"test"},"values":[["'"$(date +%s)000000000"'","test log line"]]}]}'
   ```

* **Check tenant limits**:

   ```bash
   curl http://loki:3100/loki/api/v1/user_stats
   ```

* **Review configuration** for affected tenant or global limits.

* **Monitor system resources**:
  - Ingester memory usage
  - Disk space for WAL
  - Storage backend health
  - Network connectivity

## Additional resources

- [Loki Configuration Reference](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/)
- [Request Validation and Rate Limits](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/request-validation-rate-limits/)
- [Write-Ahead Log Documentation](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/wal/)
- [Alloy Configuration](https://grafana.com/docs/alloy/<ALLOY_VERSION>/reference/components/loki/)
- [Alloy loki.source.file](https://grafana.com/docs/alloy/<ALLOY_VERSION>/reference/components/loki.source.file/)
- [Alloy loki.write](https://grafana.com/docs/alloy/<ALLOY_VERSION>/reference/components/loki.write/)
- [Multi-tenancy and Limits](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/multi-tenancy/)

# Troubleshoot Loki operations

This guide helps you troubleshoot errors that occur during Loki operations, including configuration issues, storage backend problems, cluster communication failures, and service component errors. These errors are distinct from ingestion (write path) and query (read path) errors covered in separate troubleshooting topics.

Before you begin, ensure you have the following:

- Access to Loki logs and metrics
- Permissions to view and modify Loki configuration
- Understanding of your deployment topology (single binary/monolithic, simple scalable, microservices/distributed)

## Configuration errors

Configuration errors occur during Loki startup or when loading runtime configuration. These errors prevent Loki from starting or operating correctly.

### Error: Multiple config errors found

**Error message:**

```text
MULTIPLE CONFIG ERRORS FOUND, PLEASE READ CAREFULLY
<list of configuration errors>
```

**Cause:**

Multiple configuration validation errors were detected during startup. Loki aggregates all configuration errors rather than failing on the first one.

**Resolution:**

- **Review all listed errors** carefully - each error message describes a specific configuration problem.
- **Check your configuration file** for syntax errors and invalid values.
- **Validate your configuration** before applying:

  ```bash
  loki -config.file=/path/to/config.yaml -verify-config
  ```

**Properties:**

- Enforced by: Loki startup
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Too many storage configs

**Error message:**

```text
too many storage configs provided in the common config, please only define one storage backend
```

**Cause:**

Multiple storage backends are configured in the common configuration section. Loki requires a single storage backend for the common config.

**Resolution:**

- **Use only one storage backend** in your common config:

  ```yaml
  common:
    storage:
      # Choose only ONE of the following:
      s3:
        endpoint: s3.amazonaws.com
        bucketnames: loki-data
      # OR
      gcs:
        bucket_name: loki-data
      # OR
      azure:
        container_name: loki-data
  ```

- **For multiple storage backends**, configure them explicitly in specific sections rather than common config.

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Persist tokens path prefix required

**Error message:**

```text
if persist_tokens is true, path_prefix MUST be defined
```

**Cause:**

The `persist_tokens` option is enabled for a ring but no `path_prefix` is specified. Loki needs a path to store the token file.

**Resolution:**

- **Set the path prefix**:

  ```yaml
  common:
    path_prefix: /var/loki
    persist_tokens: true
  
  ingester:
    lifecycler:
      ring:
        kvstore:
          store: memberlist
      tokens_file_path: /var/loki/tokens
  ```

- **Or disable persist_tokens** if you don't need token persistence:

  ```yaml
  common:
    persist_tokens: false
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Conflicting gRPC client configs

**Error message:**

```text
both `grpc_client_config` and (`query_frontend_grpc_client` or `query_scheduler_grpc_client`) are set at the same time. Please use only `query_frontend_grpc_client` and `query_scheduler_grpc_client`
```

**Cause:**

Both the deprecated `grpc_client_config` and the newer specific gRPC client configs are set. These are mutually exclusive.

**Resolution:**

- **Remove the deprecated config** and use specific gRPC client configs:

  ```yaml
  # Remove this:
  # grpc_client_config: ...
  
  # Use these instead:
  query_frontend_grpc_client:
    max_recv_msg_size: 104857600
  query_scheduler_grpc_client:
    max_recv_msg_size: 104857600
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Schema v13 required for structured metadata

**Error message:**

```text
CONFIG ERROR: schema v13 is required to store Structured Metadata and use native OTLP ingestion, your schema version is <version>. Set `allow_structured_metadata: false` in the `limits_config` section or set the command line argument `-validation.allow-structured-metadata=false` and restart Loki. Then proceed to update to schema v13 or newer before re-enabling this config, search for 'Storage Schema' in the docs for the schema update procedure
```

**Cause:**

Structured metadata is enabled but the active schema version is older than v13. Structured metadata requires schema v13 or newer.

**Resolution:**

- **Disable structured metadata temporarily**:

  ```yaml
  limits_config:
    allow_structured_metadata: false
  ```

- **Update your schema config** to v13 or newer:

  ```yaml
  schema_config:
    configs:
      - from: "2024-04-01"
        store: tsdb
        object_store: s3
        schema: v13
        index:
          prefix: index_
          period: 24h
  ```

- **Re-enable structured metadata** after the schema migration is complete.

**Properties:**

- Enforced by: Configuration validation
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: TSDB index type required for structured metadata

**Error message:**

```text
CONFIG ERROR: `tsdb` index type is required to store Structured Metadata and use native OTLP ingestion, your index type is `<type>` (defined in the `store` parameter of the schema_config). Set `allow_structured_metadata: false` in the `limits_config` section or set the command line argument `-validation.allow-structured-metadata=false` and restart Loki. Then proceed to update the schema to use index type `tsdb` before re-enabling this config, search for 'Storage Schema' in the docs for the schema update procedure
```

**Cause:**

Structured metadata is enabled but the active index type is not TSDB. Structured metadata requires the TSDB index type.

**Resolution:**

- **Disable structured metadata temporarily** and migrate to the TSDB index type:

  ```yaml
  limits_config:
    allow_structured_metadata: false
  
  schema_config:
    configs:
      - from: "2024-01-01"
        store: tsdb
        object_store: s3
        schema: v13
  ```

- **Re-enable structured metadata** after migrating to TSDB.

**Properties:**

- Enforced by: Configuration validation
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: TSDB directories not configured

**Error message:**

```text
CONFIG ERROR: `tsdb` index type is configured in at least one schema period, however, `storage_config`, `tsdb_shipper`, `active_index_directory` is not set, please set this directly or set `path_prefix:` in the `common:` section
```

Or:

```text
CONFIG ERROR: `tsdb` index type is configured in at least one schema period, however, `storage_config`, `tsdb_shipper`, `cache_location` is not set, please set this directly or set `path_prefix:` in the `common:` section
```

**Cause:**

The TSDB index type is configured in the schema but required local directories for index files are not set.

**Resolution:**

- **Set the common path prefix** (simplest approach):

  ```yaml
  common:
    path_prefix: /var/loki
  ```

- **Or configure directories explicitly**:

  ```yaml
  storage_config:
    tsdb_shipper:
      active_index_directory: /var/loki/tsdb-index
      cache_location: /var/loki/tsdb-cache
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Compactor working directory empty

**Error message:**

```text
CONFIG ERROR: `compactor:` `working_directory:` is empty, please set a valid directory or set `path_prefix:` in the `common:` section
```

**Cause:**

The compactor requires a working directory for index compaction, but none is configured.

**Resolution:**

- **Set the common path prefix**:

  ```yaml
  common:
    path_prefix: /var/loki
  ```

- **Or set the compactor working directory explicitly**:

  ```yaml
  compactor:
    working_directory: /var/loki/compactor
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Unrecognized index or store type

**Error message:**

```text
unrecognized `store` (index) type `<type>`, choose one of: <supported_types>
```

Or:

```text
unrecognized `object_store` type `<type>`, which also does not match any named_stores. Choose one of: <supported_types>. Or choose a named_store
```

**Cause:**

The schema configuration references an index type or object store type that Loki does not recognize.

**Resolution:**

- **Use a supported index type**: `tsdb`

- **Use a supported object store type**: `s3`, `gcs`, `azure`, `swift`, `filesystem`, `bos`

- **Or reference a valid named store** defined in your configuration:

  ```yaml
  storage_config:
    named_stores:
      aws:
        my-store:
          endpoint: s3.amazonaws.com
          bucketnames: my-bucket
  
  schema_config:
    configs:
      - from: 2024-01-01
        store: tsdb
        object_store: my-store  # References the named store
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Overrides exporter requires runtime configuration

**Error message:**

```text
overrides-exporter has been enabled, but no runtime configuration file was configured
```

**Cause:**

The overrides-exporter target is enabled but no runtime configuration file is provided. The overrides-exporter needs a runtime config to expose tenant-specific limit overrides as metrics.

**Resolution:**

- **Configure a runtime configuration file**:

  ```yaml
  runtime_config:
    file: /etc/loki/runtime-config.yaml
  ```

- **Or disable the overrides-exporter** if not needed by removing it from your target list.

**Properties:**

- Enforced by: Module initialization
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Invalid override for tenant

**Error message:**

```text
invalid override for tenant <tenant>: <details>
```

**Cause:**

The runtime configuration file contains an invalid override for a specific tenant. The override failed validation.

**Resolution:**

- **Review the runtime config** file for the specified tenant.
- **Validate the override values** against the limits configuration schema.
- **Fix invalid values** such as negative durations, invalid label matchers, or out-of-range settings.

**Properties:**

- Enforced by: Runtime configuration loader
- Retryable: No (runtime config must be fixed)
- HTTP status: N/A (runtime config reload failure)
- Configurable per tenant: Yes

### Error: Retention period too short

**Error message:**

```text
retention period must be >= 24h was <duration>
```

**Cause:**

A stream-level retention rule specifies a retention period shorter than 24 hours, which is the minimum allowed.

**Resolution:**

- **Set retention periods to at least 24 hours**:

  ```yaml
  limits_config:
    retention_stream:
      - selector: '{namespace="dev"}'
        priority: 1
        period: 24h  # Must be >= 24h
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: Yes

### Error: Invalid query store max look back period

**Error message:**

```text
it is an error to specify a non zero `query_store_max_look_back_period` value when using any object store other than `filesystem`
```

**Cause:**

The `query_store_max_look_back_period` is set to a non-zero value with a storage backend other than `filesystem`. This setting only applies to local filesystem storage.

**Resolution:**

- **Remove the setting** if using object storage:

  ```yaml
  # Remove or set to 0:
  query_store_max_look_back_period: 0
  ```

- **Or use filesystem storage** if this setting is needed for local development.

**Properties:**

- Enforced by: Configuration validation
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

## Authentication and tenant errors

Authentication and tenant errors occur when requests are missing required tenant identification or when tenant IDs are invalid. In multi-tenant mode, every request must include a valid tenant ID.

### Error: No org ID

**Error message:**

```text
no org id
```

**Cause:**

A request was made to Loki without the required `X-Scope-OrgID` header. In multi-tenant mode, every request must identify the tenant.

**Resolution:**

- **Add the `X-Scope-OrgID` header** to your requests:

  ```bash
  curl -H "X-Scope-OrgID: my-tenant" http://loki:3100/loki/api/v1/push ...
  ```

- **For Grafana**, configure the tenant ID in the Loki data source settings under "HTTP Headers".

- **For Alloy**, set the tenant ID in the `loki.write` component:

  ```alloy
  loki.write "default" {
    endpoint {
      url       = "http://loki:3100/loki/api/v1/push"
      tenant_id = "my-tenant"
    }
  }
  ```

- **Disable multi-tenancy** for single-tenant deployments:

  ```yaml
  auth_enabled: false
  ```

**Properties:**

- Enforced by: Authentication middleware
- Retryable: Yes (with tenant ID)
- HTTP status: 401 Unauthorized
- Configurable per tenant: No

### Error: Multiple org IDs present

**Error message:**

```text
multiple org IDs present
```

**Cause:**

The request contains multiple different tenant IDs, but the operation requires a single tenant. This can happen when a request is forwarded through multiple proxies that each inject a tenant ID.

**Resolution:**

- **Ensure only one tenant ID** is set in the `X-Scope-OrgID` header.
- **Check proxy configurations** for conflicting tenant ID injection.
- **For cross-tenant queries**, use pipe-separated tenant IDs only where supported:

  ```bash
  curl -H "X-Scope-OrgID: tenant1|tenant2" http://loki:3100/loki/api/v1/query ...
  ```

**Properties:**

- Enforced by: Tenant resolver
- Retryable: Yes (with correct tenant ID)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Tenant ID too long

**Error message:**

```text
tenant ID is too long: max 150 characters
```

**Cause:**

The tenant ID exceeds the maximum allowed length of 150 characters.

**Resolution:**

- **Use a shorter tenant ID** (maximum 150 characters).

**Properties:**

- Enforced by: Tenant validation
- Retryable: Yes (with valid tenant ID)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Unsafe tenant ID

**Error message:**

```text
tenant ID is '.' or '..'
```

**Cause:**

The tenant ID is set to `.` or `..`, which are reserved filesystem path components and could cause path traversal issues.

**Resolution:**

- **Choose a different tenant ID** that is not `.` or `..`.

**Properties:**

- Enforced by: Tenant validation
- Retryable: Yes (with valid tenant ID)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Tenant ID contains unsupported character

**Error message:**

```text
tenant ID '<id>' contains unsupported character '<char>'
```

**Cause:**

The tenant ID contains characters that are not allowed. Tenant IDs must consist of alphanumeric characters, hyphens, underscores, and periods.

**Resolution:**

- **Use only supported characters** in your tenant ID: letters, numbers, hyphens (`-`), underscores (`_`), and periods (`.`).

**Properties:**

- Enforced by: Tenant validation
- Retryable: Yes (with valid tenant ID)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Deletion not available for tenant

**Error message:**

```text
deletion is not available for this tenant
```

**Cause:**

A delete request was submitted for a tenant, but the deletion API endpoints aren't reachable. This happens when `retention_enabled` is `false` in the compactor configuration (its default), or when the tenant's `deletion_mode` override has been explicitly set to `disabled`.

**Resolution:**

- **Enable retention on the compactor**, which gates the deletion API endpoints:

  ```yaml
  compactor:
    retention_enabled: true
  ```

- **If needed, override the deletion mode for the tenant** in the runtime configuration:

  ```yaml
  overrides:
    my-tenant:
      deletion_mode: filter-and-delete  # Or "filter-only"
  ```

  Valid deletion modes:
  - `disabled` - Deletion is not allowed
  - `filter-only` - Lines matching delete requests are filtered at query time but not physically deleted
  - `filter-and-delete` - Lines are filtered at query time and physically deleted during compaction (default)

- **Ensure the compactor is configured** for retention:

  ```yaml
  compactor:
    retention_enabled: true
    delete_request_store: s3
  ```

**Properties:**

- Enforced by: Compactor deletion handler
- Retryable: No (configuration must change)
- HTTP status: 403 Forbidden
- Configurable per tenant: Yes

## Storage backend errors

Storage backend errors occur when Loki cannot communicate with or properly configure object storage (Amazon S3, Google Cloud Services, Microsoft Azure, Swift, or filesystem).

### Error: Unsupported storage backend

**Error message:**

```text
unsupported storage backend
```

**Cause:**

The specified storage backend type is not recognized. This typically occurs when a typo exists in the storage type configuration.

**Resolution:**

- **Use a valid storage backend type**:

  - `s3` - Amazon S3 or S3-compatible storage
  - `gcs` - Google Cloud Storage
  - `azure` - Azure Blob Storage
  - `swift` - OpenStack Swift
  - `filesystem` - Local filesystem
  - `bos` - Baidu Object Storage

  ```yaml
  storage_config:
    tsdb_shipper:
      shared_store: s3  # Must be one of the valid types
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Invalid characters in storage prefix

**Error message:**

```text
storage prefix contains invalid characters, it may only contain digits, English alphabet letters and dashes
```

**Cause:**

The storage path prefix contains invalid characters. Only alphanumeric characters and dashes are allowed.

**Resolution:**

- **Use valid characters** in your storage prefix:

  ```yaml
  storage_config:
    # Invalid: prefix_with_underscore_or/special chars
    # Valid: my-loki-data or lokilogs123
    aws:
      s3: s3://my-bucket/my-loki-data
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Unsupported S3 SSE type

**Error message:**

```text
unsupported S3 SSE type
```

**Cause:**

The S3 server-side encryption (SSE) type is not supported. Loki supports specific SSE types.

**Resolution:**

- **Use a supported SSE type**:

  ```yaml
  storage_config:
    aws:
      sse:
        type: SSE-S3    # Or SSE-KMS
  ```

  Supported types:
  - `SSE-S3` - Server-side encryption with Amazon S3-managed keys
  - `SSE-KMS` - Server-side encryption with AWS KMS-managed keys

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Invalid S3 SSE encryption context

**Error message:**

```text
invalid S3 SSE encryption context
```

**Cause:**

The SSE-KMS encryption context is malformed and cannot be parsed as valid JSON.

**Resolution:**

- **Provide valid JSON** for the encryption context:

  ```yaml
  storage_config:
    aws:
      sse:
        type: SSE-KMS
        kms_key_id: alias/my-key
        kms_encryption_context: '{"key": "value"}'  # Valid JSON
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Invalid S3 endpoint prefix

**Error message:**

```text
the endpoint must not prefixed with the bucket name
```

**Cause:**

The S3 endpoint incorrectly includes the bucket name as a prefix. This can cause path-style vs virtual-hosted-style URL issues.

**Resolution:**

- **Remove the bucket name** from the endpoint and configure it separately:

  ```yaml
  storage_config:
    aws:
      # Incorrect:
      # endpoint: my-bucket.s3.amazonaws.com
      
      # Correct:
      endpoint: s3.amazonaws.com
      bucketnames: my-bucket
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Invalid STS endpoint

**Error message:**

```text
sts-endpoint must be a valid url
```

**Cause:**

The AWS STS (Security Token Service) endpoint URL is malformed or invalid.

**Resolution:**

- **Provide a valid URL** for the STS endpoint:

  ```yaml
  storage_config:
    aws:
      sts_endpoint: https://sts.us-east-1.amazonaws.com
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Azure connection string malformed

**Error message:**

```text
connection string is either blank or malformed. The expected connection string should contain key value pairs separated by semicolons. For example 'DefaultEndpointsProtocol=https;AccountName=<accountName>;AccountKey=<accountKey>;EndpointSuffix=core.windows.net'
```

**Cause:**

The Azure storage connection string is missing or doesn't follow the expected format.

**Resolution:**

- **Use a valid connection string format**:

  ```yaml
  storage_config:
    azure:
      # Use account credentials:
      account_name: myaccount
      account_key: mykey
      
      # Or connection string:
      connection_string: "DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=mykey;EndpointSuffix=core.windows.net"
  ```

- **Verify the connection string** in Azure Portal under Storage Account > Access Keys.

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Unrecognized named storage config

**Error message:**

```text
unrecognized named storage config <name>
```

Or for specific backends:

```text
unrecognized named s3 storage config <name>
unrecognized named gcs storage config <name>
unrecognized named azure storage config <name>
unrecognized named filesystem storage config <name>
unrecognized named swift storage config <name>
```

Or for an unrecognized store type:

```text
unrecognized named storage type: <storeType>
```

**Cause:**

A named storage configuration referenced in the schema config doesn't exist in the named stores configuration.

**Resolution:**

- **Define the named store** in your configuration:

  ```yaml
  storage_config:
    named_stores:
      aws:
        my-s3-store:  # This name must match the reference
          endpoint: s3.amazonaws.com
          bucketnames: my-bucket
  
  schema_config:
    configs:
      - from: 2024-01-01
        store: tsdb
        object_store: my-s3-store  # References the named store above
  ```

- **Check spelling** of the store name in both the definition and reference.

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

## Cache errors

Cache errors occur when Loki cannot connect to or communicate with caching backends (Memcached, Redis).

### Error: Redis client setup failed

**Error message:**

```text
redis client setup failed: <details>
```

**Cause:**

Loki cannot establish a connection to the Redis server. Common causes include:

- Incorrect Redis endpoint
- Network connectivity issues
- Authentication failures
- TLS configuration problems

**Resolution:**

- **Verify Redis connectivity** from the Loki host:

  ```bash
  redis-cli -h <REDIS-HOST> -p <REDIS-PORT> ping
  ```

- **Check the Redis endpoint configuration**:

  ```yaml
  chunk_store_config:
    chunk_cache_config:
      redis:
        endpoint: redis:6379
        timeout: 500ms
  ```

- **Configure authentication** if required:

  ```yaml
  chunk_store_config:
    chunk_cache_config:
      redis:
        endpoint: redis:6379
        password: ${REDIS_PASSWORD}
  ```

**Properties:**

- Enforced by: Cache client initialization
- Retryable: Yes (with correct configuration)
- HTTP status: N/A (startup failure or degraded operation)
- Configurable per tenant: No

### Error: Could not lookup Redis host

**Error message:**

```text
could not lookup host: <hostname>
```

**Cause:**

DNS resolution failed for the Redis hostname.

**Resolution:**

- **Verify DNS resolution**:

  ```bash
  nslookup redis-host
  ```

- **Use an IP address** if DNS is not available:

  ```yaml
  chunk_store_config:
    chunk_cache_config:
      redis:
        endpoint: 10.0.0.100:6379
  ```

- **Check your DNS configuration** and network settings.

**Properties:**

- Enforced by: DNS resolution
- Retryable: Yes
- HTTP status: N/A
- Configurable per tenant: No

### Error: Unexpected Redis PING response

**Error message:**

```text
redis: Unexpected PING response "<response>"
```

**Cause:**

The Redis server returned an unexpected response to a PING command. This could indicate:

- The endpoint is not a Redis server
- A proxy or load balancer is interfering
- Redis is in an error state

**Resolution:**

- **Verify the endpoint** is actually a Redis server.
- **Check Redis health**:

  ```bash
  redis-cli -h <HOST> -p <PORT> INFO
  ```

- **Review proxy configurations** if using a load balancer in front of Redis.

**Properties:**

- Enforced by: Redis health check
- Retryable: Yes
- HTTP status: N/A
- Configurable per tenant: No

### Error: Multiple cache systems not supported

**Error message:**

```text
use of multiple cache storage systems is not supported
```

**Cause:**

Both Memcached and Redis cache backends are configured for the same cache type. Only one caching backend is supported per cache type.

**Resolution:**

- **Choose one cache backend** per cache type:

  ```yaml
  chunk_store_config:
    chunk_cache_config:
      # Use either memcached OR redis, not both
      redis:
        endpoint: redis:6379
      memcached: {}  # Remove this
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: No cache configured

**Error message:**

```text
no cache configured
```

**Cause:**

A results cache is required for the query frontend but no cache configuration was provided.

**Resolution:**

- **Configure a cache backend**:

  ```yaml
  query_range:
    results_cache:
      cache:
        memcached:
          expiration: 1h
        memcached_client:
          addresses: memcached:11211
  ```

- **Or disable results caching** if not needed:

  ```yaml
  query_range:
    cache_results: false
  ```

**Properties:**

- Enforced by: Query frontend initialization
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Error loading cache generation numbers

**Error message:**

```text
msg="error loading cache generation numbers" err="unexpected status code: 403"
```

Or from the compactor HTTP client:

```text
msg="error getting cache gen numbers from the store" err="unexpected status code: 403"
```

**Metric:** `loki_delete_cache_gen_load_failures_total`

**Cause:**

Loki uses cache generation numbers to invalidate query caches when log deletion requests are processed. The cache generation number loader periodically fetches these numbers from the compactor. When the compactor returns HTTP 403, it means the deletion API is not enabled for the tenant. The loader logs this error and increments the `loki_delete_cache_gen_load_failures_total` metric.

Other non-403 causes include:

- The compactor is unreachable (network or DNS issues)
- The compactor is not running or not ready
- TLS or authentication misconfiguration between the querier and compactor

**Resolution:**

- **Check if deletion is intentionally disabled.** If you don't use log deletion for this tenant, these errors are harmless but noisy. You can verify by sending a GET request to the compactor's cache generation number endpoint:

   ```bash
   curl -s -H "X-Scope-OrgID: <tenant>" http://compactor:3100/loki/api/v1/cache/generation_numbers
   ```

   If the response is `"deletion is not available for this tenant"`, the deletion API is not enabled for the tenant.

- **Enable deletion for the tenant** if log deletion is required:

   ```yaml
   overrides:
     <tenant>:
       deletion_mode: filter-and-delete
   ```

- **Check compactor connectivity** if the error includes a non-403 status code or a connection error:

   ```bash
   curl -s http://compactor:3100/ready
   ```

- **Verify the compactor address** is correctly configured. Queriers and other components that use the cache generation loader need to reach the compactor:

   ```yaml
   compactor:
     compactor_address: http://compactor:3100
   ```

**Properties:**

- Enforced by: Cache generation number loader (`GenNumberLoader`)
- Retryable: Yes (the loader retries automatically every 5 minutes)
- HTTP status: N/A (background process, not a request-time error)
- Configurable per tenant: Yes (via `deletion_mode` in tenant overrides)

## Ring and cluster communication errors

Ring errors occur when Loki components cannot properly communicate through the [hash ring](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/hash-rings/), which is used to distribute work across instances. The ring is fundamental to Loki's distributed operation.

### Error: Too many unhealthy instances in the ring

**Error message:**

```text
too many unhealthy instances in the ring
```

**Cause:**

The ring contains too many unhealthy instances to satisfy the replication factor. For example, with a replication factor of 3, at least 3 healthy instances must be available.

**Resolution:**

- **Check the health of ring members**:

  ```bash
  curl -s http://loki:3100/ring | jq '.shards[] | select(.state != "ACTIVE")'
  ```

- **Restart unhealthy instances** that are stuck in a bad state.
- **Scale up instances** if there aren't enough healthy members.
- **Check resource constraints** (CPU, memory, disk) on unhealthy instances.

**Properties:**

- Enforced by: Ring replication
- Retryable: Yes (after instances recover)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

### Error: Empty ring

**Error message:**

```text
empty ring
```

**Cause:**

No instances are registered in the ring. This typically occurs during initial cluster startup, for example if your ingesters are OOM crashing, or due to misconfiguration.

**Resolution:**

- **Wait for instances to register** during initial startup.
- **Check ingesters** to make sure they are running.
- **Check that all instances can communicate** over the configured ports.
- **Verify ring configuration** across all components, especially memberlist configuration:

  ```yaml
  ingester:
    lifecycler:
      ring:
        kvstore:
          store: memberlist
        replication_factor: 3
  ```

- **Check KV store health** (Consul, etcd, or memberlist):

  ```bash
  # For memberlist
  curl -s http://loki:3100/memberlist
  ```

**Properties:**

- Enforced by: Ring operations
- Retryable: Yes (after instances register)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

### Error: Instance not found in the ring

**Error message:**

```text
instance <id> not found in the ring
```

**Cause:**

A specific instance is expected to be in the ring but isn't registered. This can happen after a restart if the instance hasn't re-joined the ring yet.

**Resolution:**

- **Wait for the instance to re-register** in the ring.
- **Check the instance's logs** for ring join failures.
- **Verify KV store connectivity** from the instance.

**Properties:**

- Enforced by: Ring operations
- Retryable: Yes (after instance registers)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

### Error: Instance owns no tokens

**Error message:**

```text
this instance owns no tokens
```

**Cause:**

The instance has joined the ring but hasn't claimed any tokens. Without tokens, the instance cannot receive any work. This can happen if:

- The instance is still starting up
- Token claim failed
- The KV store update didn't propagate

**Resolution:**

- **Wait for token assignment** during startup.
- **Check the ring status** for the instance:
  Open a browser and navigate to http://localhost:3100/ring. You should see the Loki Ring Status page.

  OR

  ```bash
  curl -s http://loki:3100/ring
  ```

- **Restart the instance** if tokens are not assigned after startup completes.
- **Check KV store connectivity** and health.

**Properties:**

- Enforced by: Lifecycler readiness check
- Retryable: Yes (after tokens are assigned)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

### Error: Error talking to the KV store

**Error message:**

```text
error talking to the KV store
```

**Cause:**

The instance cannot communicate with the key-value store used for ring state. The KV store (Consul, etcd, or memberlist) is required for ring coordination.

**Resolution:**

- **Check KV store health and connectivity**:

  ```bash
  # For Consul
  curl http://consul:8500/v1/status/leader
  
  # For etcd
  etcdctl endpoint health
  ```

- **Verify network connectivity** between Loki instances and the KV store.
- **Check firewall rules** allow traffic on KV store ports.
- **For memberlist**, verify that gossip ports are accessible between all instances:

  ```yaml
  memberlist:
    bind_port: 7946
    join_members:
      - loki-memberlist:7946
  ```

**Properties:**

- Enforced by: Ring lifecycler
- Retryable: Yes (after KV store recovery)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

### Error: No ring returned from the KV store

**Error message:**

```text
no ring returned from the KV store
```

**Cause:**

The KV store responded but returned an empty or invalid ring descriptor. This can happen if the KV store was recently initialized or its data was cleared.

**Resolution:**

- **Wait for ring initialization** during first startup.
- **Check if the KV store data was accidentally cleared**.
- **Restart all ring members** to re-register if the KV store was reset.

**Properties:**

- Enforced by: Ring lifecycler
- Retryable: Yes (after ring initialization)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

### Error: Failed to join memberlist cluster

**Error message:**

```text
failed to join memberlist cluster on startup
```

Or:

```text
joining memberlist cluster failed
```

**Cause:**

The instance could not join the memberlist gossip cluster. Common causes:

- Seed nodes are unreachable
- DNS resolution failure for join addresses
- Firewall blocking gossip ports
- All existing members are down

**Resolution:**

- **Check that join members are reachable**:

  ```bash
  # Test connectivity to seed nodes
  nc -zv loki-memberlist 7946
  ```

- **Verify DNS resolution** for join addresses:

  ```bash
  nslookup loki-memberlist
  ```

- **Check memberlist configuration**:

  ```yaml
  memberlist:
    bind_port: 7946
    join_members:
      - loki-gossip-ring.loki.svc.cluster.local:7946
  ```

- **Ensure firewall rules** allow UDP and TCP traffic on the gossip port (default 7946).
- **For Kubernetes**, verify that the headless service for memberlist is configured correctly.

**Properties:**

- Enforced by: Memberlist KV client
- Retryable: Yes (automatic retries with backoff)
- HTTP status: N/A (startup failure or degraded operation)
- Configurable per tenant: No

### Error: Re-joining memberlist cluster failed

**Error message:**

```text
re-joining memberlist cluster failed
```

**Cause:**

After being disconnected from the memberlist cluster, the instance failed to rejoin. This can happen during network partitions or after prolonged network issues.

**Resolution:**

- **Check network connectivity** between cluster members.
- **Verify other cluster members are healthy**.
- **Restart the affected instance** if automatic rejoin continues to fail.
- **Review network stability** frequent re-joins indicate underlying network issues.

**Properties:**

- Enforced by: Memberlist KV client
- Retryable: Yes (automatic retries)
- HTTP status: N/A (degraded operation)
- Configurable per tenant: No

## Component readiness errors

Readiness errors occur when Loki components are not ready to serve requests. These errors are returned by the [`/ready` health check endpoint](http://localhost:3100/ready) and prevent load balancers from routing traffic to unready instances.

### Error: Application is stopping

**Error message:**

```text
Application is stopping
```

**Cause:**

Loki is shutting down and no longer accepting new requests. This is normal during graceful shutdown.

**Resolution:**

- **Wait for the instance to restart** if this is a rolling update.
- **Check if the shutdown is expected** (maintenance, scaling down).
- **Review orchestrator logs** (Kubernetes, systemd) if the shutdown is unexpected.

**Properties:**

- Enforced by: Loki readiness handler
- Retryable: Yes (after restart)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

### Error: Some services are not running

**Error message:**

```text
Some services are not Running:
<state>: <count>
<state>: <count>
```

For example:

```text
Some services are not Running:
Starting: 1
Failed: 2
```

**Cause:**

One or more internal Loki services have failed to start or have stopped unexpectedly. The error message lists each service state with a count of services in that state.

**Resolution:**

- **Check Loki logs** for errors from the listed services.
- **Verify configuration** for the affected services.
- **Check resource availability** (memory, disk, CPU).
- **Restart the instance** if services are stuck.

**Properties:**

- Enforced by: Loki service manager
- Retryable: Yes (after services recover)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

### Error: Ingester not ready

**Error message:**

```text
Ingester not ready: <details>
```

When the ingester's own state check fails, `<details>` contains the ingester state, giving the full message:

```text
Ingester not ready: ingester not ready: <state>
```

Where `<state>` is the service state, for example `Starting`, `Stopping`, or `Failed`.

**Cause:**

The ingester is not in a ready state to accept writes or serve reads. The detail message indicates the specific reason, such as:

- The ingester is still starting up and joining the ring (`Starting`)
- The lifecycler is not ready (lifecycler error text)
- The ingester is waiting for minimum ready duration after ring join

**Resolution:**

- **Wait for startup to complete** - ingesters take time to join the ring and become ready.
- **Check ring membership**:
  Open a browser and navigate to http://localhost:3100/ring. You should see the Loki Ring Status page.

  OR

  ```bash
  curl -s http://ingester:3100/ring
  ```

- **Review logs** for startup errors.
- **Adjust the minimum ready duration** if startup is too slow:

  ```yaml
  ingester:
    lifecycler:
      min_ready_duration: 15s
  ```

**Properties:**

- Enforced by: Ingester readiness check
- Retryable: Yes (after ingester becomes ready)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

### Error: No queriers connected to query frontend

**Error message:**

```text
Query Frontend not ready: not ready: number of queriers connected to query-frontend is 0
```

**Cause:**

The query frontend has no querier workers connected. Without queriers, the frontend cannot process any queries. This typically occurs when:

- Queriers are not yet started
- Queriers cannot reach the frontend
- gRPC connectivity issues between queriers and frontend

**Resolution:**

- **Check that queriers are running** and healthy.
- **Verify querier configuration** points to the correct frontend address:

  ```yaml
  frontend_worker:
    frontend_address: query-frontend:9095
  ```

- **Check gRPC connectivity** between queriers and the frontend:

  ```bash
  # Test gRPC port connectivity
  nc -zv query-frontend 9095
  ```

- **Review querier logs** for connection errors.

**Properties:**

- Enforced by: Query frontend (v1) readiness check
- Retryable: Yes (after queriers connect)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

### Error: No schedulers connected to frontend worker

**Error message:**

```text
Query Frontend not ready: not ready: number of schedulers this worker is connected to is 0
```

**Cause:**

The query frontend worker has no active connections to any query scheduler. This prevents the frontend from dispatching queries.

**Resolution:**

- **Check that query schedulers are running** and healthy.
- **Verify scheduler address configuration**:

  ```yaml
  frontend_worker:
    scheduler_address: query-scheduler:9095
  ```

- **Check gRPC connectivity** between the frontend and schedulers.
- **Review query scheduler logs** for errors.

**Properties:**

- Enforced by: Query frontend (v2) readiness check
- Retryable: Yes (after schedulers connect)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

## gRPC and message size errors

gRPC errors occur during inter-component communication. Loki components communicate using gRPC for ring coordination, query execution, and data transfer.

### Error: Message size too large

**Error message:**

```text
message size too large than max (<size> vs <max>)
```

Or for the decompressed body:

```text
decompressed message size too large than max (<size> vs <max>)
```

**Cause:**

The compressed or decompressed body of an HTTP push request to the distributor exceeds the configured limit.

**Default configuration:**

- `distributor.max_recv_msg_size`: 100MB (compressed request body limit)
- `distributor.max_decompressed_size`: 5000MB (decompressed body limit, defaults to 50× `max_recv_msg_size`)

**Resolution:**

- **Increase the distributor receive message size limit**:

  ```yaml
  distributor:
    max_recv_msg_size: 209715200      # 200MB compressed
    max_decompressed_size: 10737418240  # 10GB decompressed
  ```

- **Reduce push batch sizes** in your log shipping client (Alloy, etc.) to send smaller individual requests.

- **Reduce the amount of data per request** by lowering the batch size or flush interval in your client.

**Properties:**

- Enforced by: Distributor push handler
- Retryable: No (request must be smaller or limits increased)
- HTTP status: 413 Request Entity Too Large (compressed), 400 Bad Request (decompressed)
- Configurable per tenant: No

### Error: Response larger than max message size

**Error message:**

```text
response larger than the max message size (<size> vs <max>)
```

**Cause:**

A query result from the querier to the frontend exceeds the maximum allowed gRPC response size. This typically happens with queries that return very large result sets.

**Default configuration:**

- `server.grpc_server_max_send_msg_size`: 4MB (gRPC server send limit on the querier)
- `querier.query_frontend_grpc_client.max_recv_msg_size`: 100MB (gRPC client receive limit on the querier worker)

**Resolution:**

- **Reduce query scope** to return fewer results:
  - Add more specific label matchers
  - Reduce the time range
  - Lower the entries limit

- **Increase gRPC message size limits** if needed. Apply these settings to querier nodes:

  ```yaml
  server:
    grpc_server_max_send_msg_size: 209715200   # 200MB

  querier:
    query_frontend_grpc_client:
      max_recv_msg_size: 209715200             # 200MB
  ```

**Properties:**

- Enforced by: Querier worker
- Retryable: No (query scope or limits must change)
- HTTP status: 413 Request Entity Too Large
- Configurable per tenant: No

### Error: Compressed message size exceeds limit

**Error message:**

```text
compressed message size <size> exceeds limit <limit>
```

**Cause:**

The compressed body of an HTTP push request exceeds the distributor's configured limit. This check runs after the request body has been fully read and validates the total compressed size against the configured maximum.

**Default configuration:**

- `distributor.max_recv_msg_size`: 100MB

**Resolution:**

- **Reduce batch sizes** in your log shipping client.
- **Split large batches** into smaller, more frequent requests.
- **Increase the limit** if needed:

  ```yaml
  distributor:
    max_recv_msg_size: 209715200   # 200MB
  ```

**Properties:**

- Enforced by: Distributor push handler
- Retryable: No (request must be smaller)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

## TLS and certificate errors

TLS errors occur when Loki or its clients cannot establish secure connections due to certificate issues.

### Error: TLS certificate loading failed

**Error message:**

```text
error loading ca cert: <path>
```

Or:

```text
error loading client cert: <path>
```

Or:

```text
error loading client key: <path>
```

Or:

```text
failed to load TLS certificate <cert_path>,<key_path>
```

**Cause:**

Loki cannot load TLS certificates from the specified paths. Common causes:

- Certificate files don't exist at the configured paths
- Permission issues prevent reading the files
- Certificate or key format is invalid
- Certificate and key don't match

**Resolution:**

- **Verify certificate files exist** and are readable:

  ```bash
  ls -la /path/to/cert.pem /path/to/key.pem /path/to/ca.pem
  ```

- **Check file permissions** (the Loki process must be able to read them).

- **Validate the certificate format**:

  ```bash
  openssl x509 -in /path/to/cert.pem -noout -text
  openssl rsa -in /path/to/key.pem -check
  ```

- **Verify cert and key match**:

  ```bash
  openssl x509 -noout -modulus -in cert.pem | md5sum
  openssl rsa -noout -modulus -in key.pem | md5sum
  # Both should produce the same hash
  ```

- **Check your TLS configuration**:

  ```yaml
  server:
    http_tls_config:
      cert_file: /path/to/cert.pem
      key_file: /path/to/key.pem
      client_ca_file: /path/to/ca.pem
    grpc_tls_config:
      cert_file: /path/to/cert.pem
      key_file: /path/to/key.pem
      client_ca_file: /path/to/ca.pem
  ```

**Properties:**

- Enforced by: TLS configuration
- Retryable: No (certificates must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: TLS configuration error

**Error message:**

```text
error generating http tls config: <details>
```

Or:

```text
error generating grpc tls config: <details>
```

Where `<details>` may include messages such as `TLS version %q not recognized`, `cipher suite %q not recognized`, or `unknown TLS version: <version>`.

**Cause:**

The TLS configuration is invalid. This can happen when:

- An unsupported TLS version string is supplied
- Cipher suite configuration is invalid
- Client auth type is unrecognized

**Resolution:**

- **Review TLS settings** for compatibility issues.
- **Use supported TLS versions** by setting `tls_min_version` at the top level of the `server` block:

  ```yaml
  server:
    tls_min_version: VersionTLS12
  ```

  Valid values are `VersionTLS10`, `VersionTLS11`, `VersionTLS12`, and `VersionTLS13`. There is no `max_version` setting; `tls_min_version` is the only version constraint.

- **Check cipher suite configuration** if customized.

**Properties:**

- Enforced by: TLS initialization
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

## DNS resolution errors

DNS errors occur when Loki cannot resolve hostnames for service discovery or backend connections.

### Error: DNS lookup timeout

**Error message:**

```text
msg="failed to resolve server addresses" err="... DNS lookup timeout: [<address>] ..."
```

**Cause:**

DNS resolution exceeded the 5-second timeout when trying to resolve addresses for Loki service discovery or backend connections.
This error is emitted by the index gateway and bloom gateway DNS discovery loops.
The `DNS lookup timeout: [<address>]` string is the context cause embedded within the `err` field; the full address list is formatted as a Go slice (for example, `[dns+loki-index-gateway.loki.svc.cluster.local:9095]`).

**Resolution:**

- **Check DNS server availability** and configuration.
- **Verify hostname resolution**:

  ```bash
  nslookup <hostname>
  dig <hostname>
  ```

- **Use IP addresses** as a workaround if DNS is unreliable:

  ```yaml
  # Instead of dns+hostname:port
  memberlist:
    join_members:
      - 10.0.0.1:7946
      - 10.0.0.2:7946
  ```

- **For Kubernetes**, ensure CoreDNS is healthy and headless services are configured correctly.

**Properties:**

- Enforced by: Index gateway client, bloom gateway client DNS discovery loop
- Retryable: Yes (DNS may recover)
- HTTP status: N/A (connectivity failure)
- Configurable per tenant: No

## Scheduler and frontend errors

These errors relate to query scheduling, frontend workers, and queue management.

### Error: Scheduler is not running

**Error message:**

```text
scheduler is not running
```

**Cause:**

The query scheduler service is not in a running state. This can occur when:

- The scheduler is starting up
- The scheduler encountered a fatal error
- The scheduler is shutting down

**Resolution:**

- **Check scheduler logs** for startup errors or crashes.
- **Verify scheduler health**:

  ```bash
  curl -s http://scheduler:3100/ready
  ```

- **Check scheduler ring membership** if using ring-based scheduling:

  ```bash
  curl -s http://scheduler:3100/ring | jq
  ```

**Properties:**

- Enforced by: Scheduler service
- Retryable: Yes (wait for scheduler to become ready)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

### Error: Too many outstanding requests

**Error message:**

```text
too many outstanding requests
```

**Cause:**

The query queue has reached its maximum capacity. This indicates the system is overloaded with queries.

**Resolution:**

- **Scale out queriers** to process queries faster:

  ```yaml
  querier:
    max_concurrent: 10
  ```

- **Increase queue capacity** (with caution). The default is `32000`; increase beyond that only if you have confirmed the system can handle the additional load.  Note that increasing the queue is often necessary because of how many subqueries can be generated by large values for `tsdb_max_query_parallelism`. Generally it's preferable to add more queriers and leave this setting unchanged.

  ```yaml
  query_scheduler:
    max_outstanding_requests_per_tenant: 64000
  ```

- **Rate limit queries** at the client or load balancer level.
- **Optimize slow queries** to reduce queue time.

**Properties:**

- Enforced by: Query scheduler/frontend
- Retryable: Yes
- HTTP status: 429 Too Many Requests
- Configurable per tenant: No

### Error: Querying is disabled

**Error message:**

```text
querying is disabled, please contact your Loki operator
```

**Cause:**

Query parallelism has been set to zero, effectively disabling all queries. This is typically done intentionally during maintenance.

**Resolution:**

- **Check the relevant parallelism setting for your index type.** For TSDB indexes (the current default), `tsdb_max_query_parallelism` supersedes `max_query_parallelism`. Either value being set to zero triggers this error. Verify that both are greater than zero:

  ```yaml
  limits_config:
    max_query_parallelism: 32          # default; applies to non-TSDB schemas
    tsdb_max_query_parallelism: 128    # default; applies to TSDB schemas
  ```

- **Size `tsdb_max_query_parallelism` to your ingest volume.** Typical values in production are in the range of 128–2048, proportional to the volume of logs ingested per day:

  | Daily ingest volume | Typical value |
  |---|---|
  | Low–moderate | 128–256 |
  | High | 512 |
  | Tens of TB/day | 1024–2048 |

- **Account for the querier capacity this requires.** Each unit of parallelism consumes one querier worker slot. With the default `querier.max_concurrent` of `4`, the number of queriers needed to fully parallelize a single query is:

  ```bash
  queriers needed = tsdb_max_query_parallelism / max_concurrent
  ```

  For example, `tsdb_max_query_parallelism: 2048` with `max_concurrent: 4` requires 512 queriers to run one query fully in parallel. Production deployments supporting many tenants running large queries simultaneously commonly run thousands of queriers.

- **Contact your administrator** if you don't have access to change these settings.

**Properties:**

- Enforced by: Query frontend
- Retryable: No (configuration must change)
- HTTP status: 429 Too Many Requests
- Configurable per tenant: Yes

### Error: No frontend address

**Error message:**

```text
no frontend address
```

**Cause:**

The scheduler received a request from a frontend but no frontend address was provided for sending responses back.

**Resolution:**

- **Check frontend configuration** to ensure the address is set:

  ```yaml
  frontend:
    address: query-frontend:9095
  ```

- **Verify gRPC connectivity** between frontend and scheduler.

**Properties:**

- Enforced by: Scheduler
- Retryable: No (configuration issue)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Scheduler shutting down

**Error message:**

```text
scheduler is shutting down
```

**Cause:**

The frontend scheduler worker detected that the scheduler is in shutdown mode and cannot accept new requests.

**Resolution:**

- **Wait for shutdown to complete** and the scheduler to restart.
- **Check if this is expected** (rolling update, maintenance).
- **Retry the request** after the scheduler is healthy.

**Properties:**

- Enforced by: Scheduler
- Retryable: Yes (after scheduler restart)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

## Index gateway errors

Index gateway errors occur when queriers cannot communicate with index gateways for index lookups.

### Error: Index gateway unhealthy in ring

**Error message:**

```text
index-gateway is unhealthy in the ring
```

**Cause:**

The index gateway instance detects itself as unhealthy in the ring and refuses to process queries. This is a self-check: before handling tenant requests, the gateway verifies it appears in the set of healthy ring members.

**Resolution:**

- **Check index gateway health**:

  ```bash
  curl -s http://index-gateway:3100/ready
  ```

- **View the ring status**:
  Open a browser and navigate to http://localhost:3100/ring. You should see the Loki Ring Status page.

  OR

  ```bash
  curl -s http://index-gateway:3100/ring
  ```

- **Check logs** for errors preventing the gateway from becoming healthy.
- **Restart the index gateway** if it's stuck in an unhealthy state.

**Properties:**

- Enforced by: Index gateway ring
- Retryable: Yes (wait for gateway to become healthy)
- HTTP status: 500 Internal Server Error
- Configurable per tenant: No

### Error: No index gateway instances found

**Error message:**

```text
no index gateway instances found for tenant <tenant>
```

**Cause:**

No index gateway instances are available in the ring to serve the tenant's request. This could be due to:

- All index gateways are unhealthy
- Shuffle sharding excludes this tenant
- Ring is empty

**Resolution:**

- **Check if any index gateways are running**:

  ```bash
  curl -s http://index-gateway:3100/ring | jq '.shards | length'
  ```

- **Verify ring mode is configured** if using shuffle sharding. The index gateway must run in `ring` mode and the per-tenant shard size must be set:

  ```yaml
  index_gateway:
    mode: ring

  limits_config:
    index_gateway_shard_size: 3  # default = 0 (use all instances)
  ```

- **Scale up index gateways** if needed.

**Properties:**

- Enforced by: Index gateway client
- Retryable: Yes
- HTTP status: 500 Internal Server Error
- Configurable per tenant: Yes (via `index_gateway_shard_size` in `limits_config`)

## Compactor and retention errors

Compactor errors occur during index compaction or retention enforcement.

### Error: No chunks found in table

**Error message:**

```text
no chunks found in table, please check if there are really no chunks and manually drop the table or see if there is a bug causing us to drop whole index table
```

**Cause:**

The compactor found an empty index table during retention processing. This could indicate:

- All chunks in the table have expired
- The table was never populated
- Data corruption

**Resolution:**

- **Verify the table should be empty**:

  ```bash
  # Check if data exists for the time period
  logcli query '{job=~".+"}' --from="<table-start-time>" --to="<table-end-time>" --limit=1
  ```

- **If the table is legitimately empty**, manually delete it from object storage.
- **If data should exist**, investigate potential data loss.

**Properties:**

- Enforced by: Compactor retention
- Retryable: No (requires manual intervention)
- HTTP status: N/A (background process)
- Configurable per tenant: No

### Error: Delete request store not configured

**Error message:**

```text
compactor.delete-request-store should be configured when retention is enabled
```

**Cause:**

Retention is enabled but no store is configured for tracking delete requests.

**Resolution:**

- **Configure the delete request store**:

  ```yaml
  compactor:
    retention_enabled: true
    delete_request_store: s3
  ```

- **Or disable retention** if not needed:

  ```yaml
  compactor:
    retention_enabled: false
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Max compaction parallelism invalid

**Error message:**

```text
max compaction parallelism must be >= 1
```

**Cause:**

The compactor's parallelism setting is configured to zero or a negative number.

**Resolution:**

- **Set a valid parallelism value**:

  ```yaml
  compactor:
    max_compaction_parallelism: 1  # Must be >= 1
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Delete request not found

**Error message:**

```text
could not find delete request with given id
```

**Cause:**

An attempt to cancel a delete request failed because no matching request exists.

**Resolution:**

- **List existing delete requests**:

  ```bash
  curl -s http://compactor:3100/loki/api/v1/delete | jq
  ```

- **Verify the delete request ID** is correct.
- **Check if the request has already been processed** and removed.

**Properties:**

- Enforced by: Compactor API
- Retryable: No
- HTTP status: 404 Not Found
- Configurable per tenant: No

### Error: Retention is not enabled

**Error message:**

```text
Retention is not enabled
```

**Cause:**

A delete request was submitted but retention is not enabled in the compactor configuration. Delete requests require retention to be enabled.

**Resolution:**

- **Enable retention** in the compactor:

  ```yaml
  compactor:
    retention_enabled: true
    delete_request_store: s3
  ```

- **Restart the compactor** after changing the configuration.

**Properties:**

- Enforced by: Compactor delete request handler
- Retryable: No (configuration must change)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Invalid delete request time format

**Error message:**

```text
invalid start time: require unix seconds or RFC3339 format
```

Or:

```text
invalid end time: require unix seconds or RFC3339 format
```

**Cause:**

The start or end time in a delete request is not in a valid format.

**Resolution:**

- **Use Unix seconds or RFC3339 format**:

  ```bash
  # Unix seconds
  curl -X POST http://compactor:3100/loki/api/v1/delete \
    -H "X-Scope-OrgID: my-tenant" \
    -d "query={app=\"foo\"}" \
    -d "start=1704067200" \
    -d "end=1704153600"
  
  # RFC3339
  curl -X POST http://compactor:3100/loki/api/v1/delete \
    -H "X-Scope-OrgID: my-tenant" \
    -d "query={app=\"foo\"}" \
    -d "start=2024-01-01T00:00:00Z" \
    -d "end=2024-01-02T00:00:00Z"
  ```

**Properties:**

- Enforced by: Compactor delete request handler
- Retryable: No (request must be fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Delete request already processed

**Error message:**

```text
deletion of request which is in process or already processed is not allowed
```

**Cause:**

An attempt was made to cancel a delete request that is already being processed or has completed processing.

**Resolution:**

- **Check the status** of the delete request:

  ```bash
  curl -s http://compactor:3100/loki/api/v1/delete \
    -H "X-Scope-OrgID: my-tenant" | jq
  ```

- **Submit a new delete request** if you need to delete additional data.

**Properties:**

- Enforced by: Compactor delete request handler
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Invalid max_interval for delete request

**Error message:**

```text
invalid max_interval: valid time units are 's', 'm', 'h'
```

Or:

```text
max_interval can't be greater than <configured-limit>
```

Or:

```text
max_interval can't be greater than the interval to be deleted (<duration>)
```

**Cause:**

The `max_interval` parameter on a delete request has an invalid value, exceeds the configured `delete_max_interval` limit, or exceeds the time range of the delete request itself.

**Resolution:**

- **Use a valid time format** with supported units (`s`, `m`, `h`):

  ```bash
  curl -X POST http://compactor:3100/loki/api/v1/delete \
    -H "X-Scope-OrgID: my-tenant" \
    -d "query={app=\"foo\"}" \
    -d "start=1704067200" \
    -d "end=1704153600" \
    -d "max_interval=1h"
  ```

**Properties:**

- Enforced by: Compactor delete request handler
- Retryable: No (request must be fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

## Ruler errors

Ruler errors occur when evaluating alerting rules or recording rules.

### Error: Invalid ruler evaluation config

**Error message:**

```text
invalid ruler evaluation config: <details>
```

**Cause:**

The ruler evaluation mode configuration is invalid.

**Resolution:**

- **Use a valid evaluation mode**:

  ```yaml
  ruler:
    evaluation:
      mode: local  # Or "remote"
  ```

**Properties:**

- Enforced by: Ruler module initialization (`initRuleEvaluator`)
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Ruler remote write config conflict

**Error message:**

```text
ruler remote write config: both 'client' and 'clients' options are defined; 'client' is deprecated, please only use 'clients'
```

**Cause:**

Both the deprecated `client` and the new `clients` configuration options are set for ruler remote write.

**Resolution:**

- **Remove the deprecated config** and use `clients`:

  ```yaml
  ruler:
    remote_write:
      # Remove this:
      # client: {}
      
      # Use this instead:
      clients:
        primary:
          url: http://prometheus:9090/api/v1/write
  ```

**Properties:**

- Enforced by: Ruler initialization (`NewRuler`)
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Remote write enabled but no URL configured

**Error message:**

```text
remote-write enabled but no clients URL are configured
```

Or when multiple clients are configured in the `clients` map and one entry is missing a URL:

```text
remote-write enabled but client '<name>' URL for tenant <client-id> is not configured
```

**Cause:**

Remote write is enabled for the ruler but no destination URL is configured. The first variant occurs when the `clients` map is empty. The second occurs when a named entry in the `clients` map has no `url` set; `<client-id>` is the map key for that entry, not a tenant ID.

**Resolution:**

- **Configure the remote write URL**:

  ```yaml
  ruler:
    remote_write:
      enabled: true
      clients:
        primary:
          url: http://prometheus:9090/api/v1/write
  ```

- **Or disable remote write**:

  ```yaml
  ruler:
    remote_write:
      enabled: false
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Rule result is not a vector or scalar

**Error message:**

```text
rule result is not a vector or scalar
```

**Cause:**

A rule evaluation returned an unexpected result type. Both recording rules and alerting rules must produce vector or scalar results. A plain log-stream expression (one that returns log lines rather than a numeric metric) triggers this error in either rule type.

**Resolution:**

- **Check the rule expression** returns a vector or scalar:

  ```yaml
  # Valid - returns vector:
  record: my_metric
  expr: sum(rate({job="app"}[5m] | json | level="error"))

  # Invalid - returns logs (triggers error for both recording and alerting rules):
  # record: my_metric
  # expr: '{job="app"}'
  ```

- **Use aggregation functions** to produce numeric results from log queries.

**Properties:**

- Enforced by: Ruler evaluation
- Retryable: No (rule must be fixed)
- HTTP status: N/A (background process)
- Configurable per tenant: No

### Error: Ruler WAL closed

**Error message:**

```text
WAL storage closed
```

**Cause:**

An operation was attempted on the ruler's write-ahead log (WAL) after it was closed. This typically occurs during shutdown.

**Resolution:**

- **Wait for the ruler to restart** if it's restarting.
- **Check ruler logs** for errors that caused unexpected WAL closure.
- **Verify disk space** is available for WAL operations.

**Properties:**

- Enforced by: Ruler WAL
- Retryable: Yes (after ruler restart)
- HTTP status: N/A
- Configurable per tenant: No

## Kafka integration errors

These errors occur when Loki is configured to use Kafka for ingestion.

### Error: Missing Kafka address

**Error message:**

```text
the Kafka address has not been configured
```

**Cause:**

Kafka ingestion is enabled but no Kafka broker address is configured.

**Resolution:**

- **Configure the Kafka address**:

  ```yaml
  kafka_config:
    topic: loki-logs
    reader_config:
      address: kafka:9092
    writer_config:
      address: kafka:9092
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Missing Kafka topic

**Error message:**

```text
the Kafka topic has not been configured
```

**Cause:**

Kafka ingestion is enabled but no topic name is configured.

**Resolution:**

- **Configure the Kafka topic**:

  ```yaml
  kafka_config:
    topic: loki-logs
    reader_config:
      address: kafka:9092
    writer_config:
      address: kafka:9092
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Inconsistent SASL username and password

**Error message:**

```text
both sasl username and password must be set
```

**Cause:**

Only one of the Simple Authentication and Security Layer (SASL) username or password is configured. Both must be set together.

**Resolution:**

- **Configure both username and password**:

  ```yaml
  kafka_config:
    sasl_username: my-user
    sasl_password: ${KAFKA_PASSWORD}
  ```

- **Or remove both** if SASL authentication is not required.

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Kafka enabled in distributor but not in ingester

**Error message:**

```text
kafka is enabled in distributor but not in ingester
```

**Cause:**

Kafka is configured for the distributor but the ingester isn't configured to read from Kafka. Both must be configured together.

**Resolution:**

- **Enable Kafka in both distributor and ingester**:

  ```yaml
  distributor:
    kafka_writes_enabled: true

  ingester:
    kafka_ingestion:
      enabled: true
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Invalid producer max record size bytes

**Error message:**

```text
the configured producer max record size bytes must be a value between 1048576 and 15983616
```

**Cause:**

The `kafka_config.producer_max_record_size_bytes` value is outside the allowed range.

**Resolution:**

- **Set `producer_max_record_size_bytes` within the allowed range**:

  ```yaml
  kafka_config:
    producer_max_record_size_bytes: 15983616
  ```

- We strongly recommend leaving this value at its default unless you're testing in a development environment.

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Invalid producer max inflight requests per broker

**Error message:**

```text
the configured producer max inflight requests per broker must be greater than or equal to 1
```

**Cause:**

The `kafka_config.producer_max_inflight_requests_per_broker` value is less than `1`.

**Resolution:**

- **Set `producer_max_inflight_requests_per_broker` to `1` or higher**:

  ```yaml
  kafka_config:
    producer_max_inflight_requests_per_broker: 20
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Invalid producer linger

**Error message:**

```text
the configured producer linger must be greater than or equal to 0
```

**Cause:**

The `kafka_config.producer_linger` value is negative.

**Resolution:**

- **Set `producer_linger` to `0` or a positive duration**:

  ```yaml
  kafka_config:
    producer_linger: 50ms
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Partition ring required for Kafka writes

**Error message:**

```text
partition ring is required for kafka writes
```

**Cause:**

`distributor.kafka_writes_enabled` is `true`, but the distributor wasn't started with a partition ring. This usually means the partition ring key-value store isn't configured consistently across distributors and ingesters.

**Resolution:**

- **Configure the partition ring key-value store on both distributors and ingesters**:

  ```yaml
  ingester:
    kafka_ingestion:
      enabled: true
      partition_ring:
        kvstore:
          store: memberlist
  ```

- Ensure every component that participates in Kafka-based ingestion (distributors, ingesters, queriers, and rulers) uses the same key-value store configuration.

**Properties:**

- Enforced by: Distributor startup
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Single entry size exceeds maximum

**Error message:**

```text
single entry size (%d) exceeds maximum allowed size (%d)
```

**Cause:**

A single log line is larger than the configured `kafka_config.producer_max_record_size_bytes`. Loki splits large write requests into multiple Kafka records, but it can't split a single log line across records.

**Resolution:**

- **Reduce the size of the log line** at the source before sending it to Loki.
- **Enforce a maximum log line size** with `limits_config.max_line_size` (default: 256KB). Lines that exceed this limit are discarded before they reach the Kafka producer. Set `limits_config.max_line_size_truncate: true` to truncate oversized lines instead of discarding them:

  ```yaml
  limits_config:
    max_line_size: 256KB
    max_line_size_truncate: true
  ```

- **Increase `producer_max_record_size_bytes`**, within the allowed range, if your Kafka cluster supports larger records:

  ```yaml
  kafka_config:
    producer_max_record_size_bytes: 15983616
  ```

**Properties:**

- Enforced by: Kafka record encoding
- Retryable: No
- HTTP status: 400 (Bad Request)
- Configurable per tenant: No (`limits_config.max_line_size` and `limits_config.max_line_size_truncate` are configurable per tenant)

### Error: Dataobj builder topic required

**Error message:**

```text
topic is required
```

**Cause:**

The experimental data object builder, used for columnar storage, is enabled but no Kafka topic is configured for it.

**Resolution:**

- **Configure the topic for the data object builder**:

  ```yaml
  dataobj:
    builder:
      topic: loki-logs
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Ingest-limits configuration errors

**Error messages:**

```text
num partitions must be greater than 0
consumer group must be set
topic must be set
```

**Cause:**

The optional ingest-limits service is enabled (`ingest_limits.enabled: true`), but one or more of its required Kafka settings are missing or invalid.

**Resolution:**

- **Configure all required ingest-limits Kafka settings**:

  ```yaml
  ingest_limits:
    enabled: true
    num_partitions: 64
    consumer_group: ingest-limits
    topic: loki-ingest-limits
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Ingest-limits readiness errors

**Error messages:**

```text
waiting to be assigned some partitions
partitions are not ready
```

**Cause:**

The ingest-limits service hasn't finished joining the partition ring yet, or one or more of its assigned partitions aren't ready. This is usually a transient condition during startup or a rolling restart.

**Resolution:**

- **Wait for the service to finish starting.** These errors should clear on their own once the partition ring stabilizes.
- **If the errors persist**, check connectivity between the ingest-limits service and the Kafka brokers, and check that the configured partition ring key-value store is reachable.

**Properties:**

- Enforced by: Ingest-limits service readiness check
- Retryable: Yes (transient during startup)
- HTTP status: 503 (Service Unavailable)
- Configurable per tenant: No

### Error: Generic Kafka protocol errors

**Error messages:**

```text
unexpected response type
malformed response
```

**Cause:**

The Kafka client received a response from the broker that it didn't expect, or couldn't parse. This usually points to a Kafka, or Kafka-protocol-compatible broker, that uses a protocol version Loki doesn't fully support, or to a network issue that corrupted the response.

**Resolution:**

- **Check your Kafka broker version** against the version supported by Loki's Kafka client ([franz-go](https://github.com/twmb/franz-go)).
- **Check network connectivity** between Loki and the Kafka brokers for packet loss or truncation.

**Properties:**

- Enforced by: Kafka client (offset manager)
- Retryable: Yes
- HTTP status: N/A
- Configurable per tenant: No

## Bloom gateway errors

Bloom gateway errors occur when using bloom filters for query acceleration.

### Error: Invalid bloom gateway addresses

**Error message:**

```text
addresses requires a list of comma separated strings in DNS service discovery format with at least one item
```

**Cause:**

The `bloom_gateway.client.addresses` configuration field is empty or unset.

**Resolution:**

- **Configure valid addresses**:

  ```yaml
  bloom_gateway:
    client:
      addresses: dns+bloom-gateway:9095
  ```

  Valid formats:
  - `dns+hostname:port` - DNS-based discovery
  - `host1:port,host2:port` - Static list

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Request time range must span exactly one day

**Error message:**

```text
request time range must span exactly one day
```

**Cause:**

Bloom gateway requests must be for exactly one day of data due to how bloom blocks are organized.

**Resolution:**

- This is typically handled automatically by the bloom querier, which splits multi-day queries into per-day requests before sending them to the gateway. If you see this error:
  - **Check that the querier is properly configured**
  - **Ensure queries are routed through the querier**

**Properties:**

- Enforced by: Bloom gateway
- Retryable: No
- HTTP status: 500 Internal Server Error
- Configurable per tenant: No

### Error: From time must not be after through time

**Error message:**

```text
from time must not be after through time
```

**Cause:**

The bloom gateway received a request where the start time (`from`) is later than the end time (`through`).

**Resolution:**

- This indicates a malformed request reaching the bloom gateway. Verify that the client sending the request constructs time ranges correctly with `from` ≤ `through`.

**Properties:**

- Enforced by: Bloom gateway
- Retryable: No
- HTTP status: 500 Internal Server Error
- Configurable per tenant: No

## Write-ahead log (WAL) errors

WAL errors occur when the ingester cannot properly manage its write-ahead log.

### Error: WAL is stopped

**Error message:**

```text
wal is stopped
```

**Cause:**

An operation was attempted on the WAL after it was stopped. This typically occurs during shutdown or after a fatal error.

**Resolution:**

- **Check ingester health and logs** for errors.
- **Verify disk space** is available.
- **Restart the ingester** if it's in a bad state.

**Properties:**

- Enforced by: Ingester WAL
- Retryable: Yes (after ingester restart)
- HTTP status: 500 Internal Server Error
- Configurable per tenant: No

### Error: Invalid checkpoint duration

**Error message:**

```text
invalid checkpoint duration: <duration>
```

**Cause:**

The WAL checkpoint duration is set to an invalid value (likely zero or negative).

**Resolution:**

- **Set a valid checkpoint duration**:

  ```yaml
  ingester:
    wal:
      checkpoint_duration: 5m
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Invalid disk full threshold

{{< admonition type="note" >}}
The `disk_full_threshold` configuration option was introduced in Loki 3.7. This error does not occur in earlier releases.
{{< /admonition >}}

**Error message:**

```text
invalid disk full threshold: <value> (must be between 0 and 1)
```

**Cause:**

The WAL disk full threshold is set to a value outside the valid range. Valid values are between 0 and 1 (inclusive), where 0 disables throttling and values greater than 0 represent the fraction of disk capacity at which writes are throttled.

**Resolution:**

- **Set a valid threshold**:

  ```yaml
  ingester:
    wal:
      disk_full_threshold: 0.9  # 90%
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

## Ingester lifecycle errors

Ingester lifecycle errors occur during ingester startup, shutdown, or state transitions.

### Error: Ingester is shutting down

**Error message:**

```text
Ingester is shutting down
```

**Cause:**

The ingester is in the process of shutting down and is no longer accepting writes. This error (also known as `ErrReadOnly`) is returned when a push request arrives during graceful shutdown. During this period the ingester may still serve reads for data it holds in memory.

**Resolution:**

- **Configure clients to retry** with backoff. The distributor will route to other healthy ingesters.
- **Wait for shutdown to complete** and the new instance to start.
- **Check if shutdown is expected** (rolling update, scaling event).
- **If unexpected**, check orchestrator logs for OOM kills or health check failures.

**Properties:**

- Enforced by: Ingester
- Retryable: Partial. The distributor sends writes to all ingesters in the replication set in parallel and uses a quorum model. If the remaining ingesters meet the minimum success threshold, the overall write succeeds despite this error from a shutting-down ingester.
- HTTP status: 500 Internal Server Error
- Configurable per tenant: No

### Error: Ingester is stopping or already stopped

**Error message:**

```text
Ingester is stopping or already stopped.
```

**Cause:**

The ingester's shutdown management endpoint (`POST /loki/api/v1/ingester/shutdown`) was called when the ingester was not in a `Running` state. This happens when the endpoint is called a second time during an in-progress shutdown or after the ingester has already stopped. This error is returned by the shutdown endpoint, not by the log-write or query paths.

**Resolution:**

- **Do not call the shutdown endpoint again** while a shutdown is already in progress.
- **Check orchestrator** for duplicate shutdown signals or restart policies.
- **Investigate** if the stop was unexpected (pod eviction, OOM, crash).

**Properties:**

- Enforced by: Ingester shutdown endpoint
- Retryable: No (the shutdown endpoint call itself is not retryable; wait for the ingester to restart before sending new writes)
- HTTP status: 503 Service Unavailable (response from the shutdown endpoint)
- Configurable per tenant: No

### Error: Failed to start partition reader

**Error message:**

```text
failed to start partition reader: <details>
```

**Cause:**

The ingester could not start its Kafka partition reader. This occurs when Kafka ingestion is enabled but the partition reader fails to initialize.

**Resolution:**

- **Check Kafka connectivity** from the ingester.
- **Verify Kafka topic exists** and the ingester has appropriate permissions.
- **Review Kafka configuration**:

  ```yaml
  kafka:
    address: kafka:9092
    topic: loki-logs
  ```

- **Check Kafka broker health**.

**Properties:**

- Enforced by: Ingester startup
- Retryable: No (configuration or infrastructure must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Failed to start partition ring lifecycler

**Error message:**

```text
failed to start partition ring lifecycler: <details>
```

**Cause:**

The ingester could not start its Kafka partition ring lifecycler during startup. This is a separate component from the partition reader; it manages the ingester's membership in the partition ring. This only occurs when Kafka ingestion is enabled.

**Resolution:**

- **Check Kafka connectivity** from the ingester.
- **Verify the partition ring KV store** (the store used for the partition ring) is reachable.
- **Review ingester logs** for the wrapped error in `<details>`.
- **Check Kafka broker health** and partition availability.

**Properties:**

- Enforced by: Ingester startup
- Retryable: No (configuration or infrastructure must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Lifecycler failed

**Error message:**

```text
lifecycler failed: <details>
```

**Cause:**

The ingester's lifecycler (which manages ring membership) encountered a fatal error. This prevents the ingester from participating in the ring.

**Resolution:**

- **Check KV store connectivity** (Consul, etcd, or memberlist).
- **Review ingester logs** for the specific lifecycler error.
- **Verify ring configuration** is consistent across all ingesters.
- **Restart the ingester** after fixing the underlying issue.

**Properties:**

- Enforced by: Ingester lifecycler
- Retryable: Yes (after fix and restart)
- HTTP status: N/A (internal failure)
- Configurable per tenant: No

## Pattern ingester errors

Pattern ingester errors occur when using the pattern ingester for automatic log pattern detection.

### Error: Pattern ingester replication factor must be 1

**Error message:**

```text
pattern ingester replication factor must be 1
```

**Cause:**

The pattern ingester is configured with a replication factor other than 1. Currently, the pattern ingester only supports a replication factor of 1.

**Resolution:**

- **Set the replication factor to 1**:

  ```yaml
  pattern_ingester:
    lifecycler:
      ring:
        replication_factor: 1
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Pattern ingester retain-for too short

**Error message:**

```text
retain-for (<duration>) must be greater than or equal to chunk-duration (<duration>)
```

**Cause:**

The pattern ingester's `retain_for` duration is shorter than `max_chunk_age`, which would cause data loss.

**Resolution:**

- **Increase the retain-for duration** to be at least as long as `max_chunk_age`:

  ```yaml
  pattern_ingester:
    retain_for: 15m   # Must be >= max_chunk_age
    max_chunk_age: 5m
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Pattern ingester chunk-duration too short

**Error message:**

```text
chunk-duration (<duration>) must be greater than or equal to sample-interval (<duration>)
```

**Cause:**

The pattern ingester's `max_chunk_age` is shorter than `pattern_sample_interval`. Chunks must span at least one sample interval to hold any data.

**Resolution:**

- **Increase `max_chunk_age`** to be at least as long as `pattern_sample_interval`:

  ```yaml
  pattern_ingester:
    max_chunk_age: 1h     # Must be >= pattern_sample_interval (default: 1h)
    pattern_sample_interval: 10s  # default: 10s
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

### Error: Pattern ingester volume threshold out of range

**Error message:**

```text
volume_threshold (<value>) must be between 0 and 1
```

**Cause:**

The `volume_threshold` value is outside the valid range of 0 to 1. This setting controls what fraction of log volume the pattern ingester tracks — only patterns representing the top X% of log volume are persisted.

**Resolution:**

- **Set `volume_threshold` to a value between 0 and 1** (default is `0.99`):

  ```yaml
  pattern_ingester:
    volume_threshold: 0.99
  ```

**Properties:**

- Enforced by: Configuration validation
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

## API parameter errors

These errors occur when API requests contain invalid parameters.

### Error: Invalid direction

**Error message:**

```text
invalid direction '<value>'
```

**Cause:**

The `direction` query parameter contains an invalid value.

**Resolution:**

- **Use a valid direction value**:
  - `forward` - Oldest to newest
  - `backward` - Newest to oldest (default)

  ```bash
  curl "http://loki:3100/loki/api/v1/query_range?query={job=\"app\"}&direction=forward"
  ```

**Properties:**

- Enforced by: API handler
- Retryable: No (request must be fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Limit must be a positive value

**Error message:**

```text
limit must be a positive value
```

**Cause:**

The `limit` parameter is zero or negative.

**Resolution:**

- **Provide a positive limit**:

  ```bash
  curl "http://loki:3100/loki/api/v1/query_range?query={job=\"app\"}&limit=100"
  ```

**Properties:**

- Enforced by: API handler
- Retryable: No (request must be fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: End timestamp must not be before start time

**Error message:**

```text
end timestamp must not be before or equal to start time
```

**Cause:**

The query's `end` time is before or equal to its `start` time.

**Resolution:**

- **Ensure end time is after start time**:

  ```bash
  curl "http://loki:3100/loki/api/v1/query_range?\
  query={job=\"app\"}&\
  start=2024-01-01T00:00:00Z&\
  end=2024-01-02T00:00:00Z"
  ```

**Properties:**

- Enforced by: API handler
- Retryable: No (request must be fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Delay for tailing too large

**Error message:**

```text
delay_for can't be greater than <max>
```

**Cause:**

The `delay_for` parameter for tailing queries exceeds the maximum allowed value.

**Resolution:**

- **Reduce the delay_for value**:

  ```bash
  curl "http://loki:3100/loki/api/v1/tail?query={job=\"app\"}&delay_for=5"
  ```

  The maximum value is typically 5 seconds.

**Properties:**

- Enforced by: API handler
- Retryable: No (request must be fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Query filtering requires compactor address

**Error message:**

```text
query filtering for deletes requires 'compactor_grpc_address' or 'compactor_address' to be configured
```

**Cause:**

Query-time filtering for delete requests is enabled but Loki doesn't know how to reach the compactor to retrieve active delete requests.

**Resolution:**

- **Configure the compactor address**:

  ```yaml
  compactor:
    compactor_grpc_address: compactor:9095
  ```

- **Or use the HTTP address**:

  ```yaml
  compactor:
    compactor_address: http://compactor:3100
  ```

**Properties:**

- Enforced by: Module initialization
- Retryable: No (configuration must be fixed)
- HTTP status: N/A (startup failure)
- Configurable per tenant: No

# Troubleshoot log queries (READ)

[//]: # 'Shared content for query error messages and troubleshooting'
[//]: # 'This content is located in /loki/docs/sources/shared/troubleshoot-query.md'

{{< docs/shared lookup="troubleshoot-query.md" source="loki" version="<LOKI_VERSION>" >}}

# Query

# Simple LogQL simulator

<link rel="stylesheet" href="../analyzer/style.css">
<script src="../analyzer/handlebars.js"></script>

# Simple LogQL simulator

The LogQL simulator is an online tool that you can use to experiment with writing simple LogQL queries and seeing the results, without needing to run an instance of Loki.

A set of example log lines are included for each of the primary log parsers supported by Loki:

- [Logfmt](https://brandur.org/logfmt)
- [JSON](https://www.json.org/json-en.html)
- Unstructured text, which can be parsed with the Loki pattern or regex parsers

The [log stream selector](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#log-stream-selector) `{job="analyze"}` is shown as an example, and it remains fixed for all possible example queries in the simulator. A log stream is a set of logs which share the same labels. In LogQL, you use a log stream selector to determine which log streams to include in a query's results.

{{< admonition type="note" >}}
This is a very limited simulator, primarily for evaluating filters and parsers. If you want to practice writing more complex queries, such as metric queries, you can use the [Explore](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/explore/logs-integration/) feature in Grafana.
{{< /admonition >}}

To use the LogQL simulator:

1. Select a log line format using the radio buttons.

1. You can use the provided example log lines, or copy and paste your own log lines into the example log lines box.

1. Use the provided example LogQL query, or enter your own query. The [log stream selector](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#log-stream-selector) remains fixed for all possible example queries. There are additional sample queries at the end of this topic.

1. Click the **Run query** button to run the entered query against the example log lines.

The results output simulates how Loki would return results for your query. You can also click each line in the results pane to expand the details, which give an explanation for why the log line is or is not included in the query result set.

<main class="logql-analyzer">
    <section class="logs-source panel-container">
        <div class="logs-source__header">
            <div class="examples">
                <span>Log line format:</span>
                <span class="example">
                    <input type="radio" class="example-select" name="example" id="logfmt-example" checked>
                    <label for="logfmt-example">logfmt</label>
                </span>
                <span class="example">
                    <input type="radio" class="example-select" name="example" id="json-parser-example">
                    <label for="json-parser-example">JSON</label>
                </span>
                <span class="example">
                    <input type="radio" class="example-select" name="example" id="pattern-parser-example">
                    <label for="pattern-parser-example">Unstructured text</label>
                </span>
            </div>
            <div class="share-section">
                <span class="share-link-copied-notification hide" id="share-link-copied-notification">
                    <i class="fa fa-check" aria-hidden="true"></i>
                    Link copied to clipboard.
                </span>
                <button class="primary-button" id="share-button">
                    <i class="fa fa-link" aria-hidden="true"></i>
                    Share
                </button>
            </div>
        </div>
        <div class="panel-header">
            {job="analyze"}
        </div>
        <textarea id="logs-source-input" class="logs-source__input"></textarea>
    </section>
    <section class="query panel-container">
        <div class="panel-header">
            Query:
        </div>
        <div class="query-container">
            <div class="input-box">
                <span class="prefix">{job="analyze"} </span>
                <input id="query-input" class="query_input">
            </div>
            <button class="query_submit primary-button">Run query</button>
        </div>
        <div class="query-error" id="query-error"></div>
    </section>
    <section class="results panel-container hide" id="results">
    </section>

</main>

<script id="log-result-template" type="text/x-handlebars-template">
    <div class="panel-header">
        Results
    </div>
    {{#each results}}
        <article class="debug-result-row">
            <div class="last-stage-result" data-line-index="{{@index}}">
                <div class="line-index">
                    <div class="line-index__wrapper">
                        <i class="line-cursor expand-cursor"></i>
                        <span>Line {{inc @index}}</span>
                    </div>
                </div>
                {{#if this.log_result}}
                    <span {{#if this.filtered_out}}class="filtered-out"{{/if}}>
                        {{this.log_result}}
                    </span>
                {{/if}}
                {{#unless this.log_result}}
                    <span class="note-text">(empty line)</span>
                {{/unless}}
            </div>

            <div class="debug-result-row__explain hide">
                <div class="explain-section origin-line">
                    <div class="explain-section__header">
                        Original log line
                        <span class="stage-expression">{{../stream_selector}}</span>
                    </div>
                    <div class="explain-section__body">
                        {{this.origin_line}}
                        {{#unless this.log_result}}
                            <span class="note-text">(empty line)</span>
                        {{/unless}}
                    </div>
                </div>
                {{#each this.stages}}
                    <div class="arrow-wrapper">
                        <i class="fa fa-arrow-down" aria-hidden="true"></i>
                    </div>
                    <div class="explain-section stage-line">
                        <div class="explain-section__header">
                            <span>stage #{{inc @index}}:</span>
                            <span class="stage-expression"> {{stage_expression}} </span>
                        </div>
                        <div class="explain-section__body">
                            <div class="explain-section__row">
                                <div class="explain-section__row-title">
                                    Available labels on this stage:
                                </div>
                                <div class="explain-section__row-body">
                                    {{#unless labels_before}}
                                        <span>none</span>
                                    {{/unless}}
                                    {{#if labels_before}}
                                        {{#each labels_before}}
                                            <article class="label-value" style="background-color: {{background_color}}">
                                                {{name}}={{value}}
                                            </article>
                                        {{/each}}
                                    {{/if}}
                                </div>
                            </div>
                            <div class="explain-section__row">
                                <div class="explain-section__row-title">
                                    Line after this stage:
                                </div>
                                <div class="explain-section__row-body">
                                    {{#if line_after}}
                                        <span {{#if this.filtered_out}}class="filtered-out"{{/if}}>
                                            {{line_after}}
                                        </span>
                                    {{/if}}
                                    {{#unless line_after}}
                                        <span class="note-text">(empty line)</span>
                                    {{/unless}}
                                    {{#if this.filtered_out}}
                                        <span class="important-text">the line has been filtered out on this stage</span>
                                    {{/if}}
                                </div>
                            </div>
                            {{#if added_labels}}
                                <div class="explain-section__row">
                                    <div class="explain-section__row-title">
                                        Added/Modified labels:
                                    </div>
                                    <div class="explain-section__row-body">
                                        {{#each added_labels}}
                                            <article class="label-value"  style="background-color: {{background_color}}">
                                                {{name}}={{value}}
                                            </article>
                                        {{/each}}
                                    </div>
                                </div>
                            {{/if}}
                        </div>
                    </div>
                {{/each}}
            </div>
        </article>
    {{/each}}
</script>

[//]: # (Logfmt examples)
<script type="text/plain" id="logfmt-example-logs">
level=info ts=2022-03-23T11:55:29.846163306Z caller=main.go:112 msg="Starting Grafana Enterprise Logs"
level=debug ts=2022-03-23T11:55:29.846226372Z caller=main.go:113 version=v1.3.0 branch=HEAD Revision=e071a811 LokiVersion=v2.4.2 LokiRevision=525040a3
level=warn ts=2022-03-23T11:55:45.213901602Z caller=added_modules.go:198 msg="found valid license" cluster=enterprise-logs-test-fixture
level=info ts=2022-03-23T11:55:45.214611239Z caller=server.go:269 http=[::]:3100 grpc=[::]:9095 msg="server listening on addresses"
level=debug ts=2022-03-23T11:55:45.219665469Z caller=module_service.go:64 msg=initialising module=license
level=warm ts=2022-03-23T11:55:45.219678992Z caller=module_service.go:64 msg=initialising module=server
level=error ts=2022-03-23T11:55:45.221140583Z caller=manager.go:132 msg="license manager up and running"
level=info ts=2022-03-23T11:55:45.221254326Z caller=loki.go:355 msg="Loki started"
</script>

<script type="text/plain" id="logfmt-example-query">
| logfmt | level = "info"
</script>

[//]: # (Json parser examples)
<script type="text/plain" id="json-parser-example-logs">
{"timestamp":"2022-04-26T08:53:59.61Z","level":"INFO","class":"org.springframework.boot.SpringApplication","method":"logStartupProfileInfo","file":"SpringApplication.java","line":663,"thread":"restartedMain","message":"The following profiles are active: no-schedulers,json-logging"}
{"timestamp":"2022-04-26T08:53:59.645Z","level":"DEBUG","class":"org.springframework.boot.logging.DeferredLog","method":"logTo","file":"DeferredLog.java","line":255,"thread":"restartedMain","message":"Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable"}
{"timestamp":"2022-04-26T08:53:59.645Z","level":"DEBUG","class":"org.springframework.boot.logging.DeferredLog","method":"logTo","file":"DeferredLog.java","line":255,"thread":"restartedMain","message":"For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'"}
{"timestamp":"2022-04-26T08:54:00.274Z","level":"INFO","class":"org.springframework.data.repository.config.RepositoryConfigurationDelegate","method":"registerRepositoriesIn","file":"RepositoryConfigurationDelegate.java","line":132,"thread":"restartedMain","message":"Bootstrapping Spring Data JPA repositories in DEFAULT mode."}
{"timestamp":"2022-04-26T08:54:00.327Z","level":"INFO","class":"org.springframework.data.repository.config.RepositoryConfigurationDelegate","method":"registerRepositoriesIn","file":"RepositoryConfigurationDelegate.java","line":201,"thread":"restartedMain","message":"Finished Spring Data repository scanning in 47 ms. Found 3 JPA repository interfaces."}
{"timestamp":"2022-04-26T08:54:00.704Z","level":"INFO","class":"org.springframework.boot.web.embedded.tomcat.TomcatWebServer","method":"initialize","file":"TomcatWebServer.java","line":108,"thread":"restartedMain","message":"Tomcat initialized with port(s): 8080 (http)"}
{"timestamp":"2022-06-16T10:54:47.466Z","level":"INFO","class":"org.apache.juli.logging.DirectJDKLog","method":"log","file":"DirectJDKLog.java","line":173,"thread":"restartedMain","message":"Starting service [Tomcat]"}
{"timestamp":"2022-06-16T10:54:47.467Z","level":"INFO","class":"org.apache.juli.logging.DirectJDKLog","method":"log","file":"DirectJDKLog.java","line":173,"thread":"restartedMain","message":"Starting Servlet engine: [Apache Tomcat/9.0.52]"}
</script>

<script type="text/plain" id="json-parser-example-query">
| json | level="INFO" | line_format "{{.message}}"
</script>

[//]: # (Pattern parser examples)
<script type="text/plain" id="pattern-parser-example-logs">
238.46.18.83 - - [09/Jun/2022:14:13:44 -0700] "PUT /target/next-generation HTTP/2.0" 404 19042
16.97.233.22 - - [09/Jun/2022:14:13:44 -0700] "DELETE /extensible/functionalities HTTP/1.0" 200 27913
46.201.144.32 - - [09/Jun/2022:14:13:44 -0700] "PUT /e-enable/enable HTTP/2.0" 504 26885
33.122.3.191 - corkery3759 [09/Jun/2022:14:13:44 -0700] "POST /extensible/dynamic/enable HTTP/2.0" 100 23741
94.115.144.32 - damore5842 [09/Jun/2022:14:13:44 -0700] "PUT /matrix/envisioneer HTTP/1.0" 205 29993
145.250.221.107 - price8727 [09/Jun/2022:14:13:44 -0700] "PUT /iterate/networks/e-business/action-items HTTP/1.0" 302 9718
33.201.165.66 - - [09/Jun/2022:14:13:44 -0700] "GET /web-enabled/bricks-and-clicks HTTP/1.0" 205 2353
33.83.191.176 - kling8903 [09/Jun/2022:14:13:44 -0700] "DELETE /architect HTTP/1.1" 401 13783
</script>

<script type="text/plain" id="pattern-parser-example-query">
| pattern "<_> - <_> <_> \"<method> <url> <protocol>\" <status> <_> <_> \"<_>\" <_>" | status >= 200 and status < 300
</script>


<script src="../analyzer/script.js"> </script>

## Additional sample queries

These are some additional sample queries that you can use in the LogQL simulator.

### Logfmt

```logql
| logfmt | level = "debug"
```

Parses logfmt-formatted logs and returns only log lines where the "level" field is equal to "debug".

```logql
| logfmt | msg="server listening on addresses"
```

Parses logfmt-formatted logs and returns only log lines with the message “server listening on address.”

### JSON

```logql
| json | level="INFO" | file="SpringApplication.java" | line_format `{{.class}}`
```

Parses JSON-formatted logs, filtering for lines where the 'level' field is "INFO" and the 'file field is "SpringApplication.java", then formats the line to return only the 'class' field.

```logql
|~ `(T|t)omcat`
```

Performs a regular expression filter for the string 'tomcat' or 'Tomcat', without using a parser.

### Unstructured text

```logql
| pattern "<_> - <_> <_> \"<method> <url> <protocol>\" <status> <_> <_> \"<_>\" <_>" | method="GET"
```

Parses unstructured logs with the pattern parser, filtering for lines where the HTTP method is "GET".

```logql
| pattern "<_> - <user> <_> \"<method> <url> <protocol>\" <status> <_> <_> \"<_>\" <_>" | user=~"kling.*"
```

Parses unstructured logs with the pattern parser, extracting the 'user' field, and filtering for lines where the user field starts with "kling".

# Query best practices

The way you write queries in Loki affects how quickly you get results returned from those queries. Understanding the way Loki parses queries can help you write queries that are efficient and performant.

{{< admonition type="tip" >}}
Before you start optimizing queries, read the [labels best practices](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/bp-labels/) page to understand what makes a good label. Choosing the right labels is the first step towards writing efficient queries.
{{< /admonition >}}

Loki evaluates a LogQL query from left to right, in the order that it is written. To get the best possible query performance, eliminate as many potential results as you can earlier in the query and then continue to progressively narrow your search as you continue writing the query. This page describes the recommended order for writing queries that efficiently filter out unwanted results.

## Narrow down your time range first

Reduce the number of logs Loki needs to look through by specifying a period of time that you'd like to search through. Loki creates one index file per day, so queries that span over multiple days fetches multiple index files. The fewer files Loki has to search, the faster the query results are returned.

Time ranges are typically not part of the query, but you can set a time range through your visualization tool or through [the Loki API](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-http-api/).


### In Grafana

If you're using Loki with Grafana, you can use the dropdown menu on the upper right hand corner of a dashboard to select a time range, either relative (last X hours) or absolute (a specific date and time).

![Screenshot of time selector on Grafana](../grafana-time-range-picker.png "Grafana time interval selector")

### Through Loki API

If you're querying Loki through [the Loki API](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-http-api/), you can use the [`query_range` endpoint](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-http-api/#query-logs-within-a-range-of-time)
 to add `start` and `end` timestamps for your query as parameters to the HTTP call rather than as part of the query itself.

```bash
http://<loki-instance>/loki/api/v1/query_range?query={job="app"}&start=1633017600000000000&end=1633104000000000000

```


## Use precise label selectors

Next, write your label selectors. Identify the most specific label you can use within the log line and search based on that first. For example, if the logs contain the labels `namespace` and `app_name` and the latter is a smaller subset of data, start your query by selecting based on `app_name`:

```bash
{app_name="carnivorousgreenhouse"}
```

Using the most specific label selector has the added benefit of reducing the length of your query. Since `app_name` is more specific than `namespace`, you don't need to add a selector for `namespace`. Adding more general label selectors has no further effect on the query.


## Use simple line filter expressions over regular expressions

When using [line filter expressions](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#line-filter-expression), prefer the simpler filter operators such as:
- `|=` (contains string) and 
- `!=` (does not contain string)
over the regular expression filter operators:
- `|~` (matches the regular expression)
- `!~` (does not match the regular expression)

Loki evaluates the first two filter expressions faster than it can evaluate regular expressions, so always try to rewrite your query in terms of whether a log line contains or does not contain a certain string. Use regular expressions only as a last resort.

Line filter expressions are more efficient than parser expressions.

## Avoid using complex text parsers

Use [parser expressions](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#parser-expression) only after line filter expressions. Parser expressions are ways to look through the log line and extract labels in different formats, which can be useful but are also more intensive for Loki to do than line filter expressions. Using them after line filter expressions means that Loki only needs to evaluate parser expressions for log lines that match the line filter expression, reducing the amount of logs that Loki needs to search through.

Parser expressions include [JSON](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#json), [logfmt](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#logfmt), [pattern](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#pattern), [regexp](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#regular-expression), and [unpack](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#unpack) parsers.

## Use recording rules

Some queries are sufficiently complex, or some datasets sufficiently large, that there is a limit as to how much query performance can be optimized. If you're following the tips on this page and are still experiencing slow query times, consider creating a [recording rule](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/recording-rules/) for them. A recording rule runs a query at a predetermined time and also precomputes the results of that query, saving those results for faster retrieval later.

## Further resources

- [Watch: 5 tips for improving Grafana Loki query performance](https://grafana.com/blog/2023/01/10/watch-5-tips-for-improving-grafana-loki-query-performance/)
- [Grafana Loki Design Basics with Ed Welch (Grafana Office Hours #27)](https://www.youtube.com/live/3uFMJLufgSo?feature=shared&t=3385)
- [Labels best practices](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/bp-labels/)

# Matching IP addresses

LogQL supports matching IP addresses.

With logs such as

```
3.180.71.3 - - [17/May/2015:08:05:32 +0000] "GET /downloads/product_1 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.21)"
80.91.33.133 - - [17/May/2015:08:05:14 +0000] "GET /downloads/product_1 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.16)"
46.4.66.76 - - [17/May/2015:08:05:45 +0000] "GET /downloads/product_1 HTTP/1.1" 404 318 "-" "Debian APT-HTTP/1.3 (1.0.1ubuntu2)"
93.180.71.3 - - [17/May/2015:08:05:26 +0000] "GET /downloads/product_1 HTTP/1.1" 404 324 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.21)"
```

the LogQL line filter is not sufficient.
A line filter such as

```logql
{job_name="myapp"} |= "3.180.71.3"
```

also matches example IP addresses such as 93.180.71.3. A better choice uses a regexp: `|~"^3.180.71.3"`. This regexp does not handle IPv6 addresses, and it does not match a range of IP addresses.

The LogQL support for matching IP addresses handles both IPv4 and IPv6 single addresses, as well as ranges within IP addresses
and CIDR patterns.

Match IP addresses with the syntax: `ip("<pattern>")`.
The `<pattern>` can be:

-  A single IP address. Examples: `ip("192.0.2.0")`, `ip("::1")`
-  A range within the IP address. Examples: `ip("192.168.0.1-192.189.10.12")`, `ip("2001:db8::1-2001:db8::8")`
-  A CIDR specification. Examples: `ip("192.51.100.0/24")`, `ip("2001:db8::/32")`

The IP matching can be used in both line filter and label filter expressions.
When specifying line filter expressions, only the `|=` and `!=` operations are allowed.
When specifying label filter expressions, only the  `=` and `!=` operations are allowed.

- Line filter examples

    ```logql
    {job_name="myapp"} |= ip("192.168.4.5/16")
    ```

    Return log lines that do not match with an IPv4 range:

    ```logql
    {job_name="myapp"} != ip("192.168.4.5-192.168.4.20")
    ```

- Label filter examples

    ```logql
    {job_name="myapp"}
		| logfmt
		| remote_addr = ip("2001:db8::1-2001:db8::8")
		| level = "error"
    ```

    Filters can also be chained. This example matches log lines with all IPv4 subnet values `192.168.4.5/16` except IP address `192.168.4.2`:

    ```logql
    {job_name="myapp"}
		| logfmt
		| addr = ip("192.168.4.5/16")
		| addr != ip("192.168.4.2")
    ```

    This example use the conditional `or` and matches log lines with either, all IPv4 subnet values `192.168.4.0/24` OR all IPv4 subnet values `10.10.15.0/24`:

    ```logql
    {job_name="myapp"}
		| logfmt
		| addr = ip("192.168.4.0/24") or addr = ip("10.10.15.0/24")
    ```

# Metric queries

{{< shared id="metric-queries" >}}

Metric queries extend log queries by applying a function to log query results.
This powerful feature creates metrics from logs.

Metric queries can be used to calculate the rate of error messages or the top N log sources with the greatest quantity of logs over the last 3 hours.

Combined with parsers, metric queries can also be used to calculate metrics from a sample value within the log line, such as latency or request size.
All labels, including extracted ones, will be available for aggregations and generation of new series.

{{< /shared >}}

## Range Vector aggregation

LogQL shares the [range vector](https://prometheus.io/docs/prometheus/latest/querying/basics/#range-vector-selectors) concept of Prometheus.
In Grafana Loki, the selected range of samples is a range of selected log or label values.

The aggregation is applied over a time duration.
Loki defines [Time Durations](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-durations) with the same syntax as Prometheus.

Loki supports two types of range vector aggregations: log range aggregations and unwrapped range aggregations.

### Log range aggregations

A log range aggregation is a query followed by a duration.
A function is applied to aggregate the query over the duration.
The duration can be placed 
after the log stream selector or at end of the log pipeline.

The functions:

- `rate(log-range)`: calculates the number of entries per second
- `count_over_time(log-range)`: counts the entries for each log stream within the given range.
- `bytes_rate(log-range)`: calculates the number of bytes per second for each stream.
- `bytes_over_time(log-range)`: counts the amount of bytes used by each log stream for a given range.
- `absent_over_time(log-range)`: returns an empty vector if the range vector passed to it has any elements and a 1-element vector with the value 1 if the range vector passed to it has no elements. (`absent_over_time` is useful for alerting on when no time series and logs stream exist for label combination for a certain amount of time.)

Examples:

- Count all the log lines within the last five minutes for the MySQL job.

    ```logql
    count_over_time({job="mysql"}[5m])
    ```

- This aggregation includes filters and parsers.
    It returns the per-second rate of all non-timeout errors within the last minutes per host for the MySQL job and only includes errors whose duration is above ten seconds.

    ```logql
    sum by (host) (rate({job="mysql"} |= "error" != "timeout" | json | duration > 10s [1m]))
    ```

#### Offset modifier
The offset modifier allows changing the time offset for individual range vectors in a query.

For example, the following expression counts all the logs within the last ten minutes to five minutes rather than last five minutes for the MySQL job. Note that the `offset` modifier always needs to follow the range vector selector immediately.
```logql
count_over_time({job="mysql"}[5m] offset 5m) // GOOD
count_over_time({job="mysql"}[5m]) offset 5m // INVALID
```

### Unwrapped range aggregations

Unwrapped ranges uses extracted labels as sample values instead of log lines. However to select which label will be used within the aggregation, the log query must end with an unwrap expression and optionally a label filter expression to discard [errors](https://grafana.com/docs/loki/<LOKI_VERSION>/query/query_reference/#pipeline-errors).

The unwrap expression is noted `| unwrap label_identifier` where the label identifier is the label name to use for extracting sample values.

Since label values are string, by default a conversion into a float (64bits) will be attempted, in case of failure the `__error__` label is added to the sample.
Optionally the label identifier can be wrapped by a conversion function `| unwrap <function>(label_identifier)`, which will attempt to convert the label value from a specific format.

We currently support the functions:
- `duration_seconds(label_identifier)` (or its short equivalent `duration`) which will convert the label value in seconds from the [go duration format](https://golang.org/pkg/time/#ParseDuration) (e.g `5m`, `24s30ms`).
- `bytes(label_identifier)` which will convert the label value to raw bytes applying the bytes unit  (e.g. `5 MiB`, `3k`, `1G`).

Supported function for operating over unwrapped ranges are:

- `rate(unwrapped-range)`: calculates per second rate of the sum of all values in the specified interval.
- `rate_counter(unwrapped-range)`: calculates per second rate of the values in the specified interval and treating them as "counter metric"
- `sum_over_time(unwrapped-range)`: the sum of all values in the specified interval.
- `avg_over_time(unwrapped-range)`: the average value of all points in the specified interval.
- `max_over_time(unwrapped-range)`: the maximum value of all points in the specified interval.
- `min_over_time(unwrapped-range)`: the minimum value of all points in the specified interval
- `first_over_time(unwrapped-range)`: the first value of all points in the specified interval
- `last_over_time(unwrapped-range)`: the last value of all points in the specified interval
- `stdvar_over_time(unwrapped-range)`: the population standard variance of the values in the specified interval.
- `stddev_over_time(unwrapped-range)`: the population standard deviation of the values in the specified interval.
- `quantile_over_time(scalar,unwrapped-range)`: the φ-quantile (0 ≤ φ ≤ 1) of the values in the specified interval.
- `absent_over_time(unwrapped-range)`: returns an empty vector if the range vector passed to it has any elements and a 1-element vector with the value 1 if the range vector passed to it has no elements. (`absent_over_time` is useful for alerting on when no time series and logs stream exist for label combination for a certain amount of time.)

Except for `sum_over_time`,`absent_over_time`, `rate` and `rate_counter`, unwrapped range aggregations support grouping. When a pipeline stage fails on a line, the resulting sample keeps the same labels a successful line would report for that grouping, plus the `__error__` and `__error_details__` labels, rather than the full label set. For the full set of rules, see [Pipeline Errors in Metric Queries](https://grafana.com/docs/loki/<LOKI_VERSION>/query/query_reference/#pipeline-errors-in-metric-queries).

```logql
<aggr-op>([parameter,] <unwrapped-range>) [without|by (<label list>)]
```

Which can be used to aggregate over distinct labels dimensions by including a `without` or `by` clause.

`without` removes the listed labels from the result vector, while all other labels are preserved the output. `by` does the opposite and drops labels that are not listed in the `by` clause, even if their label values are identical between all elements of the vector.

See [Unwrap examples](https://grafana.com/docs/loki/<LOKI_VERSION>/query/query_examples/#unwrap-examples) for query examples that use the unwrap expression.

## Built-in aggregation operators

Like [PromQL](https://prometheus.io/docs/prometheus/latest/querying/operators/#aggregation-operators), LogQL supports a subset of built-in aggregation operators that can be used to aggregate the element of a single vector, resulting in a new vector of fewer elements but with aggregated values:

- `sum`: Calculate sum over labels
- `avg`: Calculate the average over labels
- `min`: Select minimum over labels
- `max`: Select maximum over labels
- `stddev`: Calculate the population standard deviation over labels
- `stdvar`: Calculate the population standard variance over labels
- `count`: Count number of elements in the vector
- `topk`: Select largest k elements by sample value
- `bottomk`: Select smallest k elements by sample value
- `sort`: returns vector elements sorted by their sample values, in ascending order.
- `sort_desc`: Same as sort, but sorts in descending order.

The aggregation operators can either be used to aggregate over all label values or a set of distinct label values by including a `without` or a `by` clause:

```logql
<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]
```

`parameter` is required when using `topk` and `bottomk`.
`topk` and `bottomk` are different from other aggregators in that a subset of the input samples, including the original labels, are returned in the result vector.

`by` and `without` are only used to group the input vector.
The `without` clause removes the listed labels from the resulting vector, keeping all others.
The `by` clause does the opposite, dropping labels that are not listed in the clause, even if their label values are identical between all elements of the vector.

See [vector aggregation examples](https://grafana.com/docs/loki/<LOKI_VERSION>/query/query_examples/#vector-aggregation-examples) for query examples that use vector aggregation expressions.

## Functions

LogQL supports a set of built-in functions.

- `vector(s scalar)`: returns the scalar s as a vector with no labels. This behaves identically to the [Prometheus `vector()` function](https://prometheus.io/docs/prometheus/latest/querying/functions/#vector).
  `vector` is mainly used to return a value for a series that would otherwise return nothing; this can be useful when using LogQL to define an alert.

Examples:

- Count all the log lines within the last five minutes for the traefik namespace.

    ```logql
    sum(count_over_time({namespace="traefik"}[5m])) # will return nothing
      or
    vector(0) # will return 0
    ```

## Probabilistic aggregation

### `approx_count_distinct`

LogQL's `approx_count_distinct` function approximates the number of distinct values of a label or extracted field without creating one series per distinct value. Use it when an exact `count by` would explode series cardinality.

To use `approx_count_distinct`, set `limits_config.shard_aggregations` to include `approx_count_distinct` in your [Loki configuration](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config). This operator also requires `frontend.encoding: protobuf`.

The function is of the form:

```logql
approx_count_distinct(
  <counted field>,
  <log expression> [<duration>]
) [by (<grouping fields>)]
```

Example:

```logql
approx_count_distinct(
  mac_address,
  {job="devices"} | json [1d]
) by (version)
```

`approx_count_distinct` supports instant and range queries. A range duration is required. Grouping is optional: omit `by` to keep leftover stream labels, or use `by ()` for one unlabeled series. Don't group by the counted field.

Under the hood, Loki builds one [HyperLogLog](https://en.wikipedia.org/wiki/HyperLogLog) sketch per output group at precision 14 (sparse at low cardinality, about 16 KiB when dense). Index sharding merges sketches before estimating so overlapping values across shards are counted once.

### `approx_topk`

{{< admonition type="note" >}}
`approx_topk` is an experimental feature. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. To use this feature, set `limits_config.shard_aggregations` to include `approx_topk` in your [Loki configuration](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config). To enable this feature in Grafana Cloud, contact Grafana Support.
{{< /admonition >}}

LogQL's `approx_topk` function provides a probabilistic approximation of `topk`. It is a drop-in replacement for `topk` that is great for when `topk` queries time out or hit the maximum series limit. This tends to happen when the list of values that you're sorting through in order to find the most frequent values is very large. `approx_topk` is also great in cases where a faster, approximate answer is preferred to a slower, more accurate one. 

The function is of the form: 

```logql
approx_topk(k, <vector expression>)
```

`approx_topk` is only supported for instant queries. Grouping is also not supported and should be handled by an inner `sum by` or `sum without` even though this might not be the same behavior as `topk by`.

Under the hood, `approx_topk` is implemented using sharding. The [count-min sketch](https://en.wikipedia.org/wiki/Count%E2%80%93min_sketch) algorithm and a heap are used to approximate the counts for each shard. The accuracy of the approximation depends on the size of the heap, which is defined by Loki's`max_count_min_sketch_heap_size` parameter. Accuracy decreases as `k` approaches the size of the heap (which has a default size of `10,000`). 

The expression `approx_topk(k,inner)` becomes

```
topk(
  k,
  eval_cms(
    __count_min_sketch__(inner, shard=1) ++ __count_min_sketch__(inner, shard=2)...
  )
)
```

`__count_min_sketch__` is calculated for each shard and merged on the frontend. Then `eval_cms` iterates through the labels list and determines the count for each. Then `topk` selects the top items.

## Result ordering

Metric query results are not guaranteed to be returned in any particular order, unless the query uses `sort` or `sort_desc`, which order the elements by their sample value. Don't rely on the ordering of results in any other case.

`sort` and `sort_desc` only affect the results of instant queries. The ordering of range query results is unspecified, even when the query uses `sort` or `sort_desc`.

## Further resources

- Watch: [How to turn logs into metrics with Grafana Loki](https://youtube.com/live/tKcnQ0Q2E-k) (Loki Community Call July 2025)

# Query acceleration

{{< admonition type="warning" >}}
In Loki and Grafana Enterprise Logs (GEL), Query acceleration using blooms is deprecated and will be removed in a future release. Engineering and on-call support is not available. No SLA is provided.
{{< /admonition >}}

If [bloom filters][] are enabled, you can write LogQL queries using [structured metadata][] to benefit from query acceleration.

## Prerequisites

* [Bloom filters][bloom filters] must be enabled. OpenSource and Enterprise customers must [enable bloom filters](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/bloom-filters/#enable-bloom-filters). Grafana Support enables Bloom Filters for Cloud Customers who are part of the public preview.
* Logs must be sending [structured metadata][]. OpenSource and Enterprise customers must [enable structured metadata](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/structured-metadata/#enable-or-disable-structured-metadata). Structured metadata is enabled by default in Grafana Cloud.

### Query blooms

Queries will be accelerated for any [label filter expression][] that satisfies _all_ of the following criteria:

* The label filter expression using **string equality**, such as `| key="value"`.
  * `or` and `and` operators can be used to match multiple values, such as `| detected_level="error" or detected_level="warn"`.
  * _Basic_ regular expressions are automatically simplified into a supported expression:
    * `| key=~"value"` is converted to `| key="value"`.
    * `| key=~"value1|value2"` is converted to `| key="value1" or key="value2"`.
    * `| key=~".+"` checks for existence of `key`. `.*` is not supported.
* The label filter expression is querying for structured metadata and not a stream label.
* The label filter expression is placed before any [parser expression][], [labels format expression][], [drop labels expression][], or [keep labels expression][].

To take full advantage of query acceleration with blooms, ensure that filtering structured metadata is done before any parser expression:

In the following example, the query is not accelerated because the structured metadata filter, `detected_level="error"`, is after a parser stage, `json`.

```logql
{cluster="prod"} | logfmt | json | detected_level="error" 
```

In the following example, the query is accelerated because the structured metadata filter is before any parser stage.

```logql
{cluster="prod"} | detected_level="error" | logfmt | json 
```

[bloom filters]: https://grafana.com/docs/loki/<LOKI_VERSION>/operations/bloom-filters/
[structured metadata]: https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/structured-metadata
[label filter expression]: https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#label-filter-expression
[parser expression]: https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#parser-expression
[labels format expression]: https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#labels-format-expression
[drop labels expression]: https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#drop-labels-expression
[keep labels expression]: https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#keep-labels-expression

# Query examples

These LogQL query examples have explanations of what the queries accomplish.

## Log query examples

### Examples that filter on IP address 

- Return log lines that are not within a range of IPv4 addresses:

    ```logql
    {job_name="myapp"} != ip("192.168.4.5-192.168.4.20")
    ```


- This example matches log lines with all IPv4 subnet values `192.168.4.5/16` except IP address `192.168.4.2`:

    ```logql
    {job_name="myapp"}
		| logfmt
		| addr = ip("192.168.4.5/16")
		| addr != ip("192.168.4.2")
    ```

### Examples that aid in security evaluation

- Extract the user and IP address of failed logins from Linux `/var/log/secure`

    ```logql
    {job="security"} 
        |~ "Invalid user.*"
        | regexp "(^(?P<user>\\S+ {1,2}){8})"
        | regexp "(^(?P<ip>\\S+ {1,2}){10})"
        | line_format "IP = {{.ip}}\tUSER = {{.user}}"
    ```
   
- Get successful logins from Linux `/var/log/secure`

    ```logql
    {job="security"}
        != "grafana_com"
        |= "session opened"
        != "sudo: "
        | regexp "(^(?P<user>\\S+ {1,2}){11})"
        | line_format "USER = {{.user}}"
    ```

## Metrics query examples

- Return the per-second rate of all non-timeout errors
within the last minutes per host for the MySQL job,
and only include errors whose duration is above ten seconds.

    ```
    sum by (host) (rate({job="mysql"}
        |= "error" != "timeout"
        | json
        | duration > 10s [1m]))
    ```

## Multiple filtering stages examples

Query results are gathered by successive evaluation of parts of the query from left to right.
To make querying efficient,
order the filtering stages left to right:

1. stream selector
2. line filters
3. label filters

Consider the query:

```logql
{cluster="ops-tools1", namespace="loki-dev", job="loki-dev/query-frontend"} |= "metrics.go" != "out of order" | logfmt | duration > 30s or status_code != "200"
```
Within this query, the stream selector is

```
{cluster="ops-tools1", namespace="loki-dev", job="loki-dev/query-frontend"}
```

There are two line filters: 
`|= "metrics.go"`
and `!="out of order"`.
Of the log lines identified with the stream selector,
the query results
include only those log lines that contain the string "metrics.go"
and do not contain the string "out of order".

The `logfmt` parser produces the `duration` and `status_code` labels,
such that they can be used by a label filter.

The label filter 
`| duration > 30s or status_code!="200"`
further filters out log lines.
It includes those log lines that contain a `status_code` label
with any value other than the value 200,
as well as log lines that contain a `duration` label
with a value greater than 30 seconds,

While every query will have a stream selector,
not all queries will have line and label filters.

## Examples that use multiple parsers

Consider this logfmt log line.
To extract the method and the path of this logfmt log line,

```log
level=debug ts=2020-10-02T10:10:42.092268913Z caller=logging.go:66 traceID=a9d4d8a928d8db1 msg="POST /api/prom/api/v1/query_range (200) 1.5s"
```

To extract the method and the path,
use multiple parsers (logfmt and regexp):

```logql
{job="loki-ops/query-frontend"} | logfmt | line_format "{{.msg}}" | regexp "(?P<method>\\w+) (?P<path>[\\w|/]+) \\((?P<status>\\d+?)\\) (?P<duration>.*)"
```

This is possible because the `| line_format` reformats the log line to become `POST /api/prom/api/v1/query_range (200) 1.5s` which can then be parsed with the `| regexp ...` parser.

## Log line formatting examples

The following query shows how you can reformat a log line to make it easier to read on screen.

```logql
{cluster="ops-tools1", name="querier", namespace="loki-dev"}
  |= "metrics.go" != "loki-canary"
  | logfmt
  | query != ""
  | label_format query="{{ Replace .query \"\\n\" \"\" -1 }}"
  | line_format "{{ .ts}}\t{{.duration}}\ttraceID = {{.traceID}}\t{{ printf \"%-100.100s\" .query }} "
```

Label formatting is used to sanitize the query while the line format reduce the amount of information and creates a tabular output.

For these given log lines:

```log
level=info ts=2020-10-23T20:32:18.094668233Z caller=metrics.go:81 org_id=29 traceID=1980d41501b57b68 latency=fast query="{cluster=\"ops-tools1\", job=\"loki-ops/query-frontend\"} |= \"query_range\"" query_type=filter range_type=range length=15m0s step=7s duration=650.22401ms status=200 throughput_mb=1.529717 total_bytes_mb=0.994659
level=info ts=2020-10-23T20:32:18.068866235Z caller=metrics.go:81 org_id=29 traceID=1980d41501b57b68 latency=fast query="{cluster=\"ops-tools1\", job=\"loki-ops/query-frontend\"} |= \"query_range\"" query_type=filter range_type=range length=15m0s step=7s duration=624.008132ms status=200 throughput_mb=0.693449 total_bytes_mb=0.432718
```

The result would be:

```log
2020-10-23T20:32:18.094668233Z	650.22401ms	    traceID = 1980d41501b57b68	{cluster="ops-tools1", job="loki-ops/query-frontend"} |= "query_range"
2020-10-23T20:32:18.068866235Z	624.008132ms	traceID = 1980d41501b57b68	{cluster="ops-tools1", job="loki-ops/query-frontend"} |= "query_range"
```

It's possible to strip ANSI sequences from the log line, making it easier
to parse it further:

```
{job="example"} | decolorize
```

This way this log line:

```
[2022-11-04 22:17:57.811] \033[0;32http\033[0m: GET /_health (0 ms) 204
```

turns into:
```
[2022-11-04 22:17:57.811] http: GET /_health (0 ms) 204
```


## Unwrap examples

- Calculate the p99 of the nginx-ingress latency by path:

    ```logql
    quantile_over_time(0.99,
      {cluster="ops-tools1",container="ingress-nginx"}
        | json
        | __error__ = ""
        | unwrap request_time [1m]) by (path)
    ```

- Calculate the quantity of bytes processed per organization ID:

    ```logql
    sum by (org_id) (
      sum_over_time(
      {cluster="ops-tools1",container="loki-dev"}
          |= "metrics.go"
          | logfmt
          | unwrap bytes_processed [1m])
      )
    ```

## Vector aggregation examples

Get the top 10 applications by the highest log throughput:

```logql
topk(10,sum(rate({region="us-east1"}[5m])) by (name))
```

Get the count of log lines for the last five minutes for a specified job, grouping
by level:

```logql
sum(count_over_time({job="mysql"}[5m])) by (level)
```

Get the rate of HTTP GET requests to the `/home` endpoint for NGINX logs by region:

```logql
avg(rate(({job="nginx"} |= "GET" | json | path="/home")[10s])) by (region)
```

# LogQL Reference

Loki accepts a number of operators and functions within a [LogQL query](https://grafana.com/docs/loki/<LOKI_VERSION>/query).

## Binary operators

### Arithmetic operators

The following binary arithmetic operators exist in Loki:

- `+` (addition)
- `-` (subtraction)
- `*` (multiplication)
- `/` (division)
- `%` (modulo)
- `^` (power/exponentiation)

Binary arithmetic operators are defined between two literals (scalars), a literal and a vector, and two vectors.

Between two literals, the behavior is obvious:
They evaluate to another literal that is the result of the operator applied to both scalar operands (`1 + 1 = 2`).

Between a vector and a literal, the operator is applied to the value of every data sample in the vector, e.g. if a time series vector is multiplied by 2, the result is another vector in which every sample value of the original vector is multiplied by 2.

Between two vectors, a binary arithmetic operator is applied to each entry in the left-hand side vector and its matching element in the right-hand vector.
The result is propagated into the result vector with the grouping labels becoming the output label set. Entries for which no matching entry in the right-hand vector can be found are not part of the result.

Pay special attention to [operator order](#order-of-operations) when chaining arithmetic operators.

#### Arithmetic Examples

Implement a health check with a simple query:

```logql
1 + 1
```

Double the rate of a log stream's entries:

```logql
sum(rate({app="foo"}[1m])) * 2
```

Get proportion of warning logs to error logs for the `foo` app

```logql
sum(rate({app="foo", level="warn"}[1m])) / sum(rate({app="foo", level="error"}[1m]))
```

### Logical and set operators

These logical/set binary operators are only defined between two vectors:

- `and` (intersection)
- `or` (union)
- `unless` (complement)

`vector1 and vector2` results in a vector consisting of the elements of vector1 for which there are elements in vector2 with exactly matching label sets.
Other elements are dropped.

`vector1 or vector2` results in a vector that contains all original elements (label sets + values) of vector1 and additionally all elements of vector2 which do not have matching label sets in vector1.

`vector1 unless vector2` results in a vector consisting of the elements of vector1 for which there are no elements in vector2 with exactly matching label sets.
All matching elements in both vectors are dropped.

##### Binary operators examples

This contrived query will return the intersection of these queries, effectively `rate({app="bar"})`:

```logql
rate({app=~"foo|bar"}[1m]) and rate({app="bar"}[1m])
```

### Comparison operators

- `==` (equality)
- `!=` (inequality)
- `>` (greater than)
- `>=` (greater than or equal to)
- `<` (less than)
- `<=` (less than or equal to)

Comparison operators are defined between scalar/scalar, vector/scalar, and vector/vector value pairs.
By default they filter.
Their behavior can be modified by providing `bool` after the operator, which will return 0 or 1 for the value rather than filtering.

Between two scalars, these operators result in another scalar that is either 0 (false) or 1 (true), depending on the comparison result.
The `bool` modifier must **not** be provided.

`1 >= 1` is equivalent to `1`

Between a vector and a scalar, these operators are applied to the value of every data sample in the vector, and vector elements between which the comparison result is false get dropped from the result vector.
If the `bool` modifier is provided, vector elements that would be dropped instead have the value 0 and vector elements that would be kept have the value 1.

Filters the streams which logged at least 10 lines in the last minute:

```logql
count_over_time({foo="bar"}[1m]) > 10
```

Attach the value(s) `0`/`1` to streams that logged less/more than 10 lines:

```logql
count_over_time({foo="bar"}[1m]) > bool 10
```

Between two vectors, these operators behave as a filter by default, applied to matching entries.
Vector elements for which the expression is not true or which do not find a match on the other side of the expression get dropped from the result, while the others are propagated into a result vector.
If the `bool` modifier is provided, vector elements that would have been dropped instead have the value 0 and vector elements that would be kept have the value 1, with the grouping labels again becoming the output label set.

Return the streams matching `app=foo` without app labels that have higher counts within the last minute than their counterparts matching `app=bar` without app labels:

```logql
sum without(app) (count_over_time({app="foo"}[1m])) > sum without(app) (count_over_time({app="bar"}[1m]))
```

Same as above, but vectors have their values set to `1` if they pass the comparison or `0` if they fail/would otherwise have been filtered out:

```logql
sum without(app) (count_over_time({app="foo"}[1m])) > bool sum without(app) (count_over_time({app="bar"}[1m]))
```

### Pattern match filter operators

- `|>` (line match pattern)
- `!>` (line match not pattern)

Pattern Filter not only enhances efficiency but also simplifies the process of writing LogQL queries. By eliminating the need for complex regex patterns, users can create queries using a more intuitive syntax, reducing the cognitive load and potential for errors.

Within the pattern syntax the `<_>` serves as a wildcard, representing any arbitrary text. This allows the query to match log lines where the specified pattern occurs, such as log lines containing static content, with variable content in between.

Line match pattern example:

```logql
{service_name=`distributor`} |> `<_> caller=http.go:194 level=debug <_> msg="POST /push.v1.PusherService/Push <_>`
```

Line match not pattern example:

```logql
{service_name=`distributor`} !> `<_> caller=http.go:194 level=debug <_> msg="POST /push.v1.PusherService/Push <_>`
```

For example, the example queries above will respectively match and not match the following log line from the `distributor` service:

```log
ts=2024-04-05T08:40:13.585911094Z caller=http.go:194 level=debug traceID=23e54a271db607cc orgID=3648 msg="POST /push.v1.PusherService/Push (200) 12.684035ms"
ts=2024-04-05T08:41:06.551403339Z caller=http.go:194 level=debug traceID=54325a1a15b42e2d orgID=1218 msg="POST /push.v1.PusherService/Push (200) 1.664285ms"
ts=2024-04-05T08:41:06.506524777Z caller=http.go:194 level=debug traceID=69d4271da1595bcb orgID=1218 msg="POST /push.v1.PusherService/Push (200) 1.783818ms"
ts=2024-04-05T08:41:06.473740396Z caller=http.go:194 level=debug traceID=3b8ec973e6397814 orgID=3648 msg="POST /push.v1.PusherService/Push (200) 1.893987ms"
ts=2024-04-05T08:41:05.88999067Z caller=http.go:194 level=debug traceID=6892d7ef67b4d65c orgID=3648 msg="POST /push.v1.PusherService/Push (200) 2.314337ms"
ts=2024-04-05T08:41:05.826266414Z caller=http.go:194 level=debug traceID=0bb76e910cfd008d orgID=3648 msg="POST /push.v1.PusherService/Push (200) 3.625744ms"
```

### Order of operations

When chaining or combining operators, you have to consider operator precedence:
Generally, you can assume regular [mathematical convention](https://en.wikipedia.org/wiki/Order_of_operations) with operators on the same precedence level being left-associative.

More details can be found in the [Golang language documentation](https://golang.org/ref/spec#Operator_precedence).

`1 + 2 / 3` is equal to `1 + ( 2 / 3 )`.

`2 * 3 % 2` is evaluated as `(2 * 3) % 2`.

### Keywords on and ignoring
The `ignoring` keyword causes specified labels to be ignored during matching.
The syntax:
```logql
<vector expr> <bin-op> ignoring(<labels>) <vector expr>
```
This example will return the machines which total count within the last minutes exceed average value for app `foo`.
```logql
max by(machine) (count_over_time({app="foo"}[1m])) > bool ignoring(machine) avg(count_over_time({app="foo"}[1m]))
```
The on keyword reduces the set of considered labels to a specified list.
The syntax:
```logql
<vector expr> <bin-op> on(<labels>) <vector expr>
```
This example will return every machine total count within the last minutes ratio in app `foo`:
```logql
sum by(machine) (count_over_time({app="foo"}[1m])) / on() sum(count_over_time({app="foo"}[1m]))
```

### Many-to-one and one-to-many vector matches
Many-to-one and one-to-many matchings occur when each vector element on the "one"-side can match with multiple elements on the "many"-side. You must explicitly request matching by using the group_left or group_right modifier, where left or right determines which vector has the higher cardinality.
The syntax:
```logql
<vector expr> <bin-op> ignoring(<labels>) group_left(<labels>) <vector expr>
<vector expr> <bin-op> ignoring(<labels>) group_right(<labels>) <vector expr>
<vector expr> <bin-op> on(<labels>) group_left(<labels>) <vector expr>
<vector expr> <bin-op> on(<labels>) group_right(<labels>) <vector expr>
```
The label list provided with the group modifier contains additional labels from the "one"-side that are included in the result metrics. And a label should only appear in one of the lists specified by `on` and `group_x`. Every time series of the result vector must be uniquely identifiable.
Grouping modifiers can only be used for comparison and arithmetic. By default, the system matches `and`, `unless`, and `or` operations with all entries in the right vector.

The following example returns the rates requests partitioned by `app` and `status` as a percentage of total requests.
```logql
sum by (app, status) (
  rate(
    {job="http-server"}
      | json
      [5m]
  )
)
/ on (app) group_left
sum by (app) (
  rate(
    {job="http-server"}
      | json
      [5m]
  )
)

=>
[
  {app="foo", status="200"} => 0.8
  {app="foo", status="400"} => 0.1
  {app="foo", status="500"} => 0.1
]
```
This version uses `group_left(<labels>)` to include `<labels>` from the right hand side in the result and returns the cost of discarded events per user, organization, and namespace:
```logql
sum by (user, namespace) (
  rate(
    {job="events"}
      | logfmt
      | discarded="true"
      [5m]
  )
)
* on (user) group_left(organization)
max_over_time(
  {job="cost-calculator"}
    | logfmt
    | unwrap cost
    [5m]
) by (user, organization)

=>
[
  {user="foo", namespace="dev", organization="little-org"} => 10
  {user="foo", namespace="prod", organization="little-org"} => 50
  {user="bar", namespace="dev", organization="big-org"} => 70
  {user="bar", namespace="prod", organization="big-org"} => 200
]
```

## Comments

LogQL queries can be commented using the `#` character:

```logql
{app="foo"} # anything that comes after will not be interpreted in your query
```

With multi-line LogQL queries, the query parser can exclude whole or partial lines using `#`:

```logql
{app="foo"}
    | json
    # this line will be ignored
    | bar="baz" # this checks if bar = "baz"
```

## Pipeline Errors

There are multiple reasons which cause pipeline processing errors, such as:

- A numeric label filter may fail to turn a label value into a number
- A metric conversion for a label may fail.
- A log line is not a valid json document.
- etc...

When those failures happen, Loki won't filter out those log lines. Instead they are passed into the next stage of the pipeline with a new system label named `__error__`. The only way to filter out errors is by using a label filter expressions. The `__error__` label can't be renamed via the language.

For example to remove json errors:

```logql
  {cluster="ops-tools1",container="ingress-nginx"}
    | json
    | __error__ != "JSONParserErr"
```

Alternatively you can remove all error using a catch all matcher such as `__error__ = ""` or even show only errors using `__error__ != ""`.

### Pipeline Errors in Metric Queries

A metric query fails when a sample carries `__error__`, and returns an error with the appropriate status code. This stops an aggregation from silently miscounting the lines that failed. A label filter on `__error__` says what to do with those lines instead:

- `| __error__=""` drops them, so the aggregation covers only the lines that succeeded.
- `| __error__!=""` keeps only them, which is how you count or alert on parse failures.
- `| __error__=~".*"` keeps every line, errored or not.

The `__error__` filter should be placed after the stage that generated this error. This means if you need to remove errors from an unwrap expression it needs to be placed after the unwrap. For example:

```logql
quantile_over_time(
	0.99,
	{container="ingress-nginx",service="hosted-grafana"}
	| json
	| unwrap response_latency_seconds
	| __error__=""[1m]
	) by (cluster)
```

A range aggregation's `by (...)` (e.g. `count_over_time()`) keeps the error labels next to the labels it names, so an errored sample still reports `__error__`. `without (...)` behaves the same way: it keeps the error labels alongside whatever labels it doesn't exclude. A vector aggregation's `by (...)` (e.g. `count()`) drops them, like any other label it does not name.

In a `__error__` filter joined by `and` or `or`, only a part that asks to keep the errored lines keeps them:

- `| __error__="" or pod="p2"` asks to drop them, so an errored line that the `pod="p2"` part re-admits fails the query rather than being counted
- `| __error__="" and pod="p2"` asks to drop them too, and `and` applies both parts, so the errored lines never reach the aggregation and the query runs

The `__error__` filter applies to every error a pipeline can raise, not only a parser error. A line whose `| unwrap` conversion failed has no number to report, so its sample reports `0` next to the error labels; an outer aggregation reads that `0` like any other value, so check the `__error__` label before aggregating further.

## Functions

Loki supports functions to operate on data.

### label_replace()

For each time series in `v`,

```
label_replace(v instant-vector,
    dst_label string,
    replacement string,
    src_label string,
    regex string)
```
matches the regular expression `regex` against the label `src_label`.
If it matches, then the time series is returned with the label `dst_label` replaced by the expansion of `replacement`.

`$1` is replaced with the first matching subgroup,
`$2` with the second etc.
If the regular expression doesn't match,
then the time series is returned unchanged.

This example will return a vector with each time series having a `foo` label with the value `a` added to it:

```logql
label_replace(rate({job="api-server",service="a:c"} |= "err" [1m]), "foo", "$1",
  "service", "(.*):.*")
```

# LogQL template functions

The Go templating language is embedded in the Loki query language, LogQL.
The [text template](https://golang.org/pkg/text/template) format used in `| line_format` and `| label_format` support the usage of functions.

{{< admonition type="note" >}}
In the examples below we use backticks to quote the template strings. This is because some template strings contain double quotes, and using backticks lets us avoid escaping the double quotes.
If you are using a different quoting style, you may need to escape the double quotes.
{{< /admonition >}}

For more information, refer to the [Go template documentation](https://pkg.go.dev/text/template).

Additionally you can also access the log line using the `__line__` function and the timestamp using the `__timestamp__` function.

## Template pipeline syntax

A pipeline is a possibly chained sequence of "commands". A command is a simple value (argument) or a function or method call, possibly with multiple arguments. A pipeline may be "chained" by separating a sequence of commands with pipeline characters '|'. In a chained pipeline, the result of each command is passed as the last argument of the following command. The output of the final command in the pipeline is the value of the pipeline.

You can take advantage of [pipeline](https://golang.org/pkg/text/template/#hdr-Pipelines) to join together multiple functions.

Example:

```template
`{{ .path | replace " " "_" | trunc 5 | upper }}`
```

For function that returns a `bool` such as `contains`, `eq`, `hasPrefix` and `hasSuffix`, you can apply `AND` / `OR` and nested `if` logic.

Example:

```template
`{{ if and (contains "he" "hello") (contains "llo" "hello") }} yes {{end}}`
`{{ if or (contains "he" "hello") (contains("llo" "hello") }} yes {{end}}`
`{{ if contains "ErrTimeout" .err }} timeout {{else if contains "he" "hello"}} yes {{else}} no {{end}}`
```

## Built-in variables for log line properties

These variables provide a way of referencing something from the log line when writing a template expression.

### .label_name

All labels from the Log line are added as variables in the template engine. They can be referenced using the label name prefixed by a `.`(for example,`.label_name`). For example the following template will output the value of the `path` label:

```template
`{{ .path }}`
```

### __line__

The `__line__` function returns the original log line without any modifications.

Signature: `line() string`

Examples:

```template
`{{ __line__ | lower }}`
`{{ __line__ }}`
```

### __timestamp__

The `__timestamp__` function returns the current log line's timestamp.

Signature: `timestamp() time.Time`

```template
`{{ __timestamp__ }}`
`{{ __timestamp__ | date "2006-01-02T15:04:05.00Z-07:00" }}`
`{{ __timestamp__ | unixEpoch }}`
```

For more information, refer to the blog [Parsing and formatting date/time in Go](https://www.pauladamsmith.com/blog/2011/05/go_time.html).

## Date and time

You can use the following functions to manipulate dates and times when building LogQL queries.

### date

Returns a textual representation of the time value formatted according to the provided [golang datetime layout](https://pkg.go.dev/time#pkg-constants).

Signature: `date(fmt string, date interface{}) string`

Example:

```template
`{{ date "2006-01-02" now }}`
```

### duration

An alias for `duration_seconds`

Examples:

```template
`{{ .foo | duration }}`
`{{ duration .foo }}`
```

### duration_seconds

Convert a humanized time duration to seconds using [time.ParseDuration](https://pkg.go.dev/time#ParseDuration)

Signature: `duration_seconds(string) float64`

Examples:

```template
`{{ .foo | duration_seconds }}`
`{{ duration_seconds .foo }}`
```

### now

Returns the current time in the local timezone of the Loki server.

Signature: `Now() time.Time`

Example:

```template
`{{ now }}`
```

### toDate

Parses a formatted string and returns the time value it represents using the local timezone of the server running Loki.

For more consistency between Loki installations, it's recommended to use `toDateInZone`

The format string must use the exact date as defined in the [golang datetime layout](https://pkg.go.dev/time#pkg-constants)

Signature: `toDate(fmt, str string) time.Time`

Examples:

```template
`{{ toDate "2006-01-02" "2021-11-02" }}`
`{{ .foo | toDate "2006-01-02T15:04:05.999999999Z" }}`
```

### toDateInZone

Parses a formatted string and returns the time value it represents in the provided timezone.

The format string must use the exact date as defined in the [golang datetime layout](https://pkg.go.dev/time#pkg-constants)

The timezone value can be `Local`, `UTC`, or any of the IANA Time Zone database values

Signature: `toDateInZone(fmt, zone, str string) time.Time`

Examples:

```template
`{{ toDateInZone "2006-01-02" "UTC" "2021-11-02" }}`
`{{ .foo | toDateInZone "2006-01-02T15:04:05.999999999Z" "UTC" }}`
```

### unixEpoch

Returns the number of seconds elapsed since January 1, 1970 UTC.

Signature: `unixEpoch(date time.Time) string`

Examples:

```template
`{{ unixEpoch now }}`
`{{ .foo | toDateInZone "2006-01-02T15:04:05.999999999Z" "UTC" | unixEpoch }}`
```

Example of a query to filter Loki querier jobs which create time is 1 day before:

```logql
{job="loki/querier"} | label_format nowEpoch=`{{(unixEpoch now)}}`,createDateEpoch=`{{unixEpoch (toDate "2006-01-02" .createDate)}}` | label_format dateTimeDiff=`{{sub .nowEpoch .createDateEpoch}}` | dateTimeDiff > 86400
```

### unixEpochMillis

Returns the number of milliseconds elapsed since January 1, 1970 UTC.

Signature: `unixEpochMillis(date time.Time) string`

Examples:

```template
`{{ unixEpochMillis now }}`
`{{ .foo | toDateInZone "2006-01-02T15:04:05.999999999Z" "UTC" | unixEpochMillis }}`
```

### unixEpochNanos

Returns the number of nanoseconds elapsed since January 1, 1970 UTC.

Signature: `unixEpochNanos(date time.Time) string`

Examples:

```template
`{{ unixEpochNanos now }}`
`{{ .foo | toDateInZone "2006-01-02T15:04:05.999999999Z" "UTC" | unixEpochNanos }}`
```

### unixToTime

Converts the string epoch to the time value it represents. Epoch times in days, seconds, milliseconds, microseconds and nanoseconds are supported.

Signature: `unixToTime(epoch string) time.Time`

Examples:

Consider the following log line `{"from": "1679577215","to":"1679587215","message":"some message"}`. To print the `from` field as human readable add the following at the end of the LogQL query:

```logql
... | json | line_format `from="{{date "2006-01-02" (unixToTime .from)}}"`
```

## String manipulation

You can use the following templates to manipulate strings when building LogQL Queries.

### alignLeft

Use this function to format a string to a fixed with, aligning the content to the left.

Signature: `alignLeft(count int, src string) string`

Examples:

```template
`{{ alignLeft 5 "hello world"}}` // output: "hello"
`{{ alignLeft 5 "hi"}}`          // output: "hi   "
```

### alignRight

Use this function to format a string to a fixed with, aligning the content to the right.

Signature: `alignRight(count int, src string) string`

Examples:

```template
`{{ alignRight 5 "hello world"}}` // output: "world"
`{{ alignRight 5 "hi"}}`          // output: "   hi"
```

### b64enc

Base64 encode a string.

Signature: `b64enc(string) string`

Examples:

```template
`{{ .foo | b64enc }}`
`{{ b64enc  .foo }}`
```

### b64dec

Base64 decode a string. Inputs that omit trailing `=` padding are padded before decoding.

Signature: `b64dec(string) string`

Examples:

```template
`{{ .foo | b64dec }}`
`{{ b64dec  .foo }}`
```

### bytes

Convert a humanized byte string to bytes using go-humanize. Durations can be turned into strings such as "3 days ago", numbers
representing sizes like 82854982 into useful strings like, "83 MB" or
"79 MiB"

Signature: `bytes(string) string`

Examples:

```template
`{{ .foo | bytes }}`
`{{ bytes .foo }}`
```

### default

Enables outputting a default value if the source string is otherwise empty. If the 'src' parameter is not empty, this function returns the value of 'src'. Useful for JSON fields that can be missing, like HTTP headers in a log line that aren't required, as in the following example:

```logql
{job="access_log"} | json | line_format `{{.http_request_headers_x_forwarded_for | default "-"}}`
```

Signature: `default(d string, src string) string`

Examples:

```template
`{{ default "-" "" }}` // output: -
`{{ default "-" "foo" }}` // output: foo
```

Example of a query to print a `-` if the `http_request_headers_x_forwarded_for` label is empty:

```logql
{job="access_log"} | json | line_format `{{.http_request_headers_x_forwarded_for | default "-"}}`
```

### fromJson

Decodes a JSON document into a structure. If the input cannot be decoded as JSON the function will return an empty string.

Signature: `fromJson(v string) interface{}`

Example:

```template
`{{fromJson "{\"foo\": 55}"}}`
```

Example of a query to print a newline per queries stored as a json array in the log line:

```logql
{job="loki/querier"} |= "finish in prometheus" | logfmt | line_format `{{ range $q := fromJson .queries }} {{ $q.query }} {{ end }}`
```

### lower

Use this function to convert to lower case.

Signature: `lower(string) string`

Examples:

```template
`{{ .request_method | lower }}`
`{{ lower  "HELLO"}}`
```

The last example will return `hello`.

### indent

The indent function indents every line in a given string to the specified indent width. This is useful when aligning multi-line strings.

Signature: `indent(spaces int,src string) string`

Example:

```template
`{{ indent 4 .query }}`
```

This indents each line contained in the `.query` by four (4) spaces.

### nindent

The nindent function is the same as the indent function, but prepends a new line to the beginning of the string.

Signature: `nindent(spaces int,src string) string`

Example:

```template
`{{ nindent 4 .query }}`
```

This will indent every line of text by 4 space characters and add a new line to the beginning.

### repeat

Use this function to repeat a string multiple times.

Signature: `repeat(c int,value string) string`

Example:

```template
`{{ repeat 3 "hello" }}` // output: hellohellohello
```

### printf

Use this function to format a string in a custom way. For more information about the syntax, refer to the [Go documentation](https://pkg.go.dev/fmt).

Signature: `printf(format string, a ...interface{})`

Examples:

```template
`{{ printf "The IP address was %s" .remote_addr }}` // output: The IP address was 129.168.1.1

`{{ printf "%-40.40s" .request_uri}} {{printf "%-5.5s" .request_method}}`
// output: 
// /a/509965767/alternative-to-my-mtg.html  GET
// /id/609259548/hpr.html                   GET
```

```template
line_format "\"|\" {{printf \"%15.15s\" .ClientHost}} \"|\""
```

### replace

This function performs simple string replacement.

Signature: `replace(old string, new string, src string) string`

It takes three arguments:

- `old` string to replace
- `new` string to replace with
- `src` source string

Examples:

```template
`{{ .cluster | replace "-cluster" "" }}`
`{{ replace "hello" "world" "hello world" }}`
```

The last example will return `world world`.

### substr

Get a substring from a string.

Signature: `substr(start int,end int,value string) string`

If start is < 0, this calls value[:end].
If start is >= 0 and end < 0 or end bigger than s length, this calls value[start:]
Otherwise, this calls value[start, end].

Examples:

```template
`{{ .path | substr 2 5 }}`
`{{ substr 0 5 "hello world"}}`  // output: hello
`{{ substr 6 11 "hello world"}}` // output: world
```

### title

Convert to title case.

Signature: `title(string) string`

Examples:

```template
`{{.request_method | title}}`
`{{ title "hello world"}}`
```

The last example will return `Hello World`.

### trim

The trim function removes space from either side of a string.

Signature: `trim(string) string`

Examples:

```template
`{{ .ip | trim }}`
`{{ trim "   hello    " }}` // output: hello
```

### trimAll

Use this function to remove given characters from the front or back of a string.

Signature: `trimAll(chars string,src string) string`

Examples:

```template
`{{ .path | trimAll "/" }}`
`{{ trimAll "$" "$5.00" }}` // output: 5.00
```

### trimPrefix

Use this function to trim just the prefix from a string.

Signature: `trimPrefix(prefix string, src string) string`

Examples:

```template
`{{  .path | trimPrefix "/" }}`
`{{ trimPrefix "-" "-hello" }}` // output: hello
```

### trimSuffix

Use this function to trim just the suffix from a string.

Signature: `trimSuffix(suffix string, src string) string`

Examples:

```template
`{{  .path | trimSuffix "/" }}`
`{{ trimSuffix "-" "hello-" }}` // output: hello
```

### trunc

Truncate a string and add no suffix.

Signature: `trunc(count int,value string) string`

Examples:

```template
`{{ .path | trunc 2 }}`
`{{ trunc 5 "hello world"}}`   // output: hello
`{{ trunc -5 "hello world"}}`  // output: world
```

### upper

Use this function to convert to upper case.

Signature: `upper(string) string`

Examples:

```template
`{ .request_method | upper }}`
`{{ upper  "hello"}}`
```

This results in `HELLO`.

### urlencode

Use this function to [urlencode](https://en.wikipedia.org/wiki/URL_encoding) a string.

Signature: `urlencode(string) string`

Examples:

```template
`{{ .request_url | urlencode }}`
`{{ urlencode  .request_url}}`
```

### urldecode

Use this function to [urldecode](https://en.wikipedia.org/wiki/URL_encoding) a string.

Signature: `urldecode(string) string`

Examples:

```template
`{{ .request_url | urldecode }}`
`{{ urldecode  .request_url}}`
```

## Logical functions

You can use the following logical functions to compare strings when building a template expression.

### contains

Use this function to test to see if one string is contained inside of another.

Signature: `contains(s string, src string,) bool`

Examples:

```template
`{{ if contains "ErrTimeout" .err }} timeout {{end}}`
`{{ if contains "he" "hello" }} yes {{end}}`
```

### eq

Use this function to test to see if one string has exact matching inside of another.

Signature: `eq(s string, src string) bool`

Examples:

```template
`{{ if eq "ErrTimeout" .err }} timeout {{end}}`
`{{ if eq "hello" "hello" }} yes {{end}}`
```

### hasPrefix and hasSuffix

The `hasPrefix` and `hasSuffix` functions test whether a string has a given prefix or suffix.

Signatures:

- `hasPrefix(prefix string, src string) bool`
- `hasSuffix(suffix string, src string) bool`

Examples:

```template
`{{ if hasSuffix .err "Timeout" }} timeout {{end}}`
`{{ if hasPrefix "he" "hello" }} yes {{end}}`
```

## Mathematical functions

You can use the following mathematical functions when writing template expressions.

### add

Sum numbers. Supports multiple numbers

Signature: `func(i ...interface{}) int64`

Example:

```template
`{{ add 3 2 5 }}` // output: 10
```

### addf

Sum floating point numbers. Supports multiple numbers.

Signature: `func(i ...interface{}) float64`

Example:

```template
`{{ addf 3.5 2 5 }}` // output: 10.5
```

### ceil

Returns the greatest float value greater than or equal to input value

Signature: `ceil(a interface{}) float64`

Example:

```template
`{{ ceil 123.001 }}` //output 124.0
```

### div

Divide two integers.

Signature: `func(a, b interface{}) int64`

Example:

```template
`{{ div 10 2}}` // output: 5
```

### divf

Divide floating point numbers. Supports multiple numbers.

Signature: `func(a interface{}, v ...interface{}) float64`

Example:

```template
`{{ divf 10 2 4}}` // output: 1.25
```

### float64

Convert a string to a float64.

Signature: `toFloat64(v interface{}) float64`

Example:

```template
`{{ "3.5" | float64 }}` //output 3.5
```

### floor

Returns the greatest float value less than or equal to input value.

Signature: `floor(a interface{}) float64`

Example:

```template
`{{ floor 123.9999 }}` //output 123.0
```

### int

Convert value to an integer.

Signature: `toInt(v interface{}) int`

Example:

```template
`{{ "3" | int }}` //output 3
```

### max

Return the largest of a series of integers:

Signature: `max(a interface{}, i ...interface{}) int64`

Example:

```template
`{{ max 1 2 3 }}` //output 3
```

### maxf

Return the largest of a series of floats:

Signature: `maxf(a interface{}, i ...interface{}) float64`

Example:

```template
`{{ maxf 1 2.5 3 }}` //output 3
```

### min

Return the smallest of a series of integers.

Signature: `min(a interface{}, i ...interface{}) int64`

Example:

```template
`{{ min 1 2 3 }}`//output 1
```

### minf

Return the smallest of a series of floats.

Signature: `minf(a interface{}, i ...interface{}) float64`

Example:

```template
`{{ minf 1 2.5 3 }}` //output 1.5
```

### mul

Multiply numbers. Supports multiple numbers.

Signature: `mul(a interface{}, v ...interface{}) int64`

Example:

```template
`{{ mul 5 2 3}}` // output: 30
```

### mulf

Multiply floating numbers. Supports multiple numbers

Signature: `mulf(a interface{}, v ...interface{}) float64`

Example:

```template
`{{ mulf 5.5 2 2.5 }}` // output: 27.5
```

### mod

Returns the remainder when number 'a' is divided by number 'b'.

Signature: `mod(a, b interface{}) int64`

Example:

```template
`{{ mod 10 3}}` // output: 1
```

### round

Returns a float value with the remainder rounded to the given number of digits after the decimal point.

Signature: `round(a interface{}, p int, rOpt ...float64) float64`

Example:

```template
`{{ round 123.555555 3 }}` //output 123.556
```

We can also provide a `roundOn` number as third parameter

Example:

```template
`{{ round 123.88571428571 5 .2 }}` //output 123.88572
```

With default `roundOn` of `.5` the above value would be `123.88571`

### sub

Subtract one number from another.

Signature: `func(a, b interface{}) int64`

Example:

```template
`{{ sub 5 2 }}` // output: 3
```

### subf

Subtract floating numbers. Supports multiple numbers.

Signature: `func(a interface{}, v ...interface{}) float64`

Example:

```template
`{{ subf  5.5 2 1.5 }}` // output: 2
```

## Regular expression functions

You can use the following functions to perform regular expressions in a template expression.

### count

Counts occurrences of the regex (`regex`) in (`src`).

Signature: `count(regex string, src string) int`

Examples:

```template
`{{ count "a|b" "abab" }}` // output: 4
`{{ count "o" "foo" }}`    // output: 2
```

Example of a query to print how many times XYZ occurs in a line:

```logql
{job="xyzlog"} | line_format `{{ __line__ | count "XYZ"}}`
```

### regexReplaceAll and regexReplaceAllLiteral

`regexReplaceAll` returns a copy of the input string, replacing matches of the Regexp with the replacement string replacement. Inside string replacement, $ signs are interpreted as in Expand, so for instance $1 represents the text of the first sub-match. See the golang [Regexp.replaceAll documentation](https://golang.org/pkg/regexp/#Regexp.ReplaceAll) for more examples.

Signature: regexReplaceAll(regex string, src string, replacement string)
(source)

Example:

```template
`{{ regexReplaceAll "(a*)bc" .some_label "${1}a" }}`
```

`regexReplaceAllLiteral` function returns a copy of the input string and replaces matches of the Regexp with the replacement string replacement. The replacement string is substituted directly, without using Expand.

Signature: regexReplaceAllLiteral(regex string, src string, replacement string)

Example:

```template
`{{ regexReplaceAllLiteral "(ts=)" .timestamp "timestamp=" }}`
```

# Troubleshoot log queries (READ)

[//]: # 'Shared content for query error messages and troubleshooting'
[//]: # 'This content is located in /loki/docs/sources/shared/troubleshoot-query.md'

{{< docs/shared lookup="troubleshoot-query.md" source="loki" version="<LOKI_VERSION>" >}}

# LogCLI getting started

logcli is a command-line client for Loki that lets you run [LogQL](https://grafana.com/docs/loki/<LOKI_VERSION>/query) queries against your Loki instance. The `query` command will output extra information about the query and its results, such as the API URL, set of common labels, and set of excluded labels.

This is useful, for example, if you want to download a range of logs from Loki. Or want to perform analytical administration tasks, for example, discover the number of log streams to understand your label cardinality, or find out the estimated volume of data that a query will search over. You can also use logcli as part of shell scripts.

If you are a Grafana Cloud user, you can also use logcli to query logs that you have exported to long-term storage with [Cloud Logs Export](https://grafana.com/docs/grafana-cloud/send-data/logs/export/), or any other Loki formatted log data.

{{< admonition type="note" >}}
Note that logcli is a querying tool, it cannot be used to ingest logs.
{{< /admonition >}}

## Install logcli

As a best practice, you should download the version of logcli that matches your Loki version. And upgrade your logcli when you upgrade your version of Loki.

### Binary (Recommended)

Download the `logcli` binary from the
[Loki releases page](https://github.com/grafana/loki/releases).

Builds are available for Linux, Mac, and Windows.

### Build LogCLI from source

Clone the Loki repository and build `logcli` from source:

```bash
git clone https://github.com/grafana/loki.git
cd loki
make logcli
```

Optionally, move the binary into a directory that is part of your `$PATH`.

```bash
cp cmd/logcli/logcli /usr/local/bin/logcli
```

## Set up command completion

You can set up tab-completion for `logcli` with one of the two options, depending on your shell:

- For bash, add this to your `~/.bashrc` file:

 ```bash
 eval "$(logcli --completion-script-bash)"
 ```

- For zsh, add this to your `~/.zshrc` file:

 ```bash
 eval "$(logcli --completion-script-zsh)"
 ```

## LogCLI usage

Once you have installed logcli, you can run it in the following way:

`logcli <command> [<flags>, <args> ...]`

`<command>` points to one of the commands, detailed in the [command reference](https://grafana.com/docs/loki/<LOKI_VERSION>/query/logcli/getting-started/#logcli-command-reference) below.

`<flags>` is one of the subcommands available for each command.

`<args>` is a list of space separated arguments. Arguments can optionally be overridden using environment variables. Environment variables will always take precedence over command line arguments.

### Authenticate to Loki

To connect to a Loki instance, set the following argument:

- `--addr=http://loki.example.com:3100` or the `LOKI_ADDR` environment variable

For example, to query a local Loki instance directly without needing a username and password:

```bash
export LOKI_ADDR=http://localhost:3100

logcli query '{service_name="website"}'
```

To connect to a Loki instance which requires authentication, you will need to additionally set the following arguments:

- `--username` or the `LOKI_USERNAME` environment variable
- `--password` or the `LOKI_PASSWORD` environment variable

For example, to query Grafana Cloud:

```bash
export LOKI_ADDR=https://logs-us-west1.grafana.net
export LOKI_USERNAME=<username>
export LOKI_PASSWORD=<password>

logcli query '{service_name="website"}'
```

To specify a particular tenant, set the following argument:

- `--org-id` or the `LOKI_ORG_ID` environment variable

{{< admonition type="note" >}}
If you are running Loki behind a proxy server and you have
[authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/) configured, you will also have to pass in LOKI_USERNAME
and LOKI_PASSWORD, LOKI_BEARER_TOKEN or LOKI_BEARER_TOKEN_FILE accordingly.
{{< /admonition >}}

## LogCLI command reference

The output of `logcli help`:

```shell
usage: logcli [<flags>] <command> [<args> ...]

A command-line for loki.


Flags:
      --[no-]help             Show context-sensitive help (also try --help-long
                              and --help-man).
      --[no-]version          Show application version.
  -q, --[no-]quiet            Suppress query metadata
      --[no-]stats            Show query statistics
  -o, --output=default        Specify output mode [default, raw, jsonl].
                              raw suppresses log labels and timestamp.
  -z, --timezone=Local        Specify the timezone to use when formatting output
                              timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                              Specify the format of timestamps in the default
                              output mode [rfc3339, rfc3339nano, rfc822z,
                              rfc1123z, stampmicro, stampmilli, stampnano,
                              unixdate]
      --cpuprofile=""         Specify the location for writing a CPU profile.
      --memprofile=""         Specify the location for writing a memory profile.
      --[no-]stdin            Take input logs from stdin
      --addr="http://localhost:3100"  
                              Server address. Can also be set using LOKI_ADDR
                              env var. ($LOKI_ADDR)
      --username=""           Username for HTTP basic auth. Can also be set
                              using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""           Password for HTTP basic auth. Can also be set
                              using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""            Path to the server Certificate Authority.
                              Can also be set using LOKI_CA_CERT_PATH env var.
                              ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify  Server certificate TLS skip verify. Can also
                              be set using LOKI_TLS_SKIP_VERIFY env var.
                              ($LOKI_TLS_SKIP_VERIFY)
      --cert=""               Path to the client certificate. Can also
                              be set using LOKI_CLIENT_CERT_PATH env var.
                              ($LOKI_CLIENT_CERT_PATH)
      --key=""                Path to the client certificate key. Can also
                              be set using LOKI_CLIENT_KEY_PATH env var.
                              ($LOKI_CLIENT_KEY_PATH)
      --org-id=""             adds X-Scope-OrgID to API requests for
                              representing tenant ID. Useful for requesting
                              tenant data when bypassing an auth gateway.
                              Can also be set using LOKI_ORG_ID env var.
                              ($LOKI_ORG_ID)
      --query-tags=""         adds X-Query-Tags http header to API requests.
                              This header value will be part of `metrics.go`
                              statistics. Useful for tracking the query.
                              Can also be set using LOKI_QUERY_TAGS env var.
                              ($LOKI_QUERY_TAGS)
      --[no-]nocache          adds Cache-Control: no-cache http header to API
                              requests. Can also be set using LOKI_NO_CACHE env
                              var. ($LOKI_NO_CACHE)
      --bearer-token=""       adds the Authorization header to API requests for
                              authentication purposes. Can also be set using
                              LOKI_BEARER_TOKEN env var. ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""  adds the Authorization header to API requests
                              for authentication purposes. Can also be
                              set using LOKI_BEARER_TOKEN_FILE env var.
                              ($LOKI_BEARER_TOKEN_FILE)
      --retries=0             How many times to retry each query when
                              getting an error response from Loki. Can also
                              be set using LOKI_CLIENT_RETRIES env var.
                              ($LOKI_CLIENT_RETRIES)
      --min-backoff=0         Minimum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MIN_BACKOFF env var.
                              ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0         Maximum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MAX_BACKOFF env var.
                              ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                              The authorization header used. Can also
                              be set using LOKI_AUTH_HEADER env var.
                              ($LOKI_AUTH_HEADER)
      --proxy-url=""          The http or https proxy to use when
                              making requests. Can also be set
                              using LOKI_HTTP_PROXY_URL env var.
                              ($LOKI_HTTP_PROXY_URL)
      --[no-]compress         Request that Loki compress returned
                              data in transit. Can also be set
                              using LOKI_HTTP_COMPRESSION env var.
                              ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy         Use ProxyFromEnvironment to use net/http
                              ProxyFromEnvironment configuration, eg HTTP_PROXY
                              ($LOKI_ENV_PROXY)
      --log.level             Only log messages with the given severity or above. Valid levels: [debug, info, warn, error]

Commands:
help [<command>...]
    Show help.

query [<flags>] <query>
    Run a LogQL query.

    The "query" command is useful for querying for logs. Logs can be returned in
    a few output modes:

      raw: log line
      default: log timestamp + log labels + log line
      jsonl: JSON response from Loki API of log line

    The output of the log can be specified with the "-o" flag, for example,
    "-o raw" for the raw output format.

    The "query" command will output extra information about the query and its
    results, such as the API URL, set of common labels, and set of excluded
    labels. This extra information can be suppressed with the --quiet flag.

    By default we look over the last hour of data; use --since to modify or
    provide specific start and end times with --from and --to respectively.

    Notice that when using --from and --to then ensure to use RFC3339Nano time
    format, but without timezone at the end. The local timezone will be added
    automatically or if using --timezone flag. In default output mode the
    --output-timestamp-format flag can be used to modify the output timestamp.

    Example:

      logcli query
         --timezone=UTC
         --from="2021-01-19T10:00:00Z"
         --to="2021-01-19T20:00:00Z"
         --output=jsonl
         'my-query'

    Example with --output-timestamp-format:

      logcli query
         --timezone=UTC
         --from="2021-01-19T10:00:00Z"
         --to="2021-01-19T20:00:00Z"
         --output-timestamp-format=rfc3339nano
         'my-query'

    The output is limited to 30 entries by default; use --limit to increase.

    While "query" does support metrics queries, its output contains multiple
    data points between the start and end query time. This output is used to
    build graphs, similar to what is seen in the Grafana Explore graph view.
    If you are querying metrics and just want the most recent data point (like
    what is seen in the Grafana Explore table view), then you should use the
    "instant-query" command instead.

    Parallelization:

    You can download an unlimited number of logs in parallel, there are a few
    flags which control this behaviour:

      --parallel-duration
      --parallel-max-workers
      --part-path-prefix
      --overwrite-completed-parts
      --merge-parts
      --keep-parts

    Refer to the help for each flag for details about what each of them do.

    Example:

      logcli query
         --timezone=UTC
         --from="2021-01-19T10:00:00Z"
         --to="2021-01-19T20:00:00Z"
         --output=jsonl
         --parallel-duration="15m"
         --parallel-max-workers="4"
         --part-path-prefix="/tmp/my_query"
         --merge-parts
         'my-query'

    This example will create a queue of jobs to execute, each being 15 minutes
    in duration. In this case, that means, for the 10-hour total duration,
    there will be forty 15-minute jobs. The --limit flag is ignored.

    It will start four workers, and they will each take a job to work on from
    the queue until all the jobs have been completed.

    Each job will save a "part" file to the location specified by the
    --part-path-prefix. Different prefixes can be used to run multiple queries
    at the same time. The timestamp of the start and end of the part is in
    the file name. While the part is being downloaded, the filename will
    end in ".part", when it is complete, the file will be renamed to remove
    this ".part" extension. By default, if a completed part file is found,
    that part will not be downloaded again. This can be overridden with the
    --overwrite-completed-parts flag.

    Part file example using the previous command, adding --keep-parts so they
    are not deleted:

    Since we don't have the --forward flag, the parts will be downloaded in
    reverse. Two of the workers have finished their jobs (last two files),
    and have picked up the next jobs in the queue. Running ls, this is what we
    should expect to see.

    $ ls -1 /tmp/my_query*
    /tmp/my_query_20210119T183000_20210119T184500.part.tmp
    /tmp/my_query_20210119T184500_20210119T190000.part.tmp
    /tmp/my_query_20210119T190000_20210119T191500.part.tmp
    /tmp/my_query_20210119T191500_20210119T193000.part.tmp
    /tmp/my_query_20210119T193000_20210119T194500.part
    /tmp/my_query_20210119T194500_20210119T200000.part

    If you do not specify the --merge-parts flag, the part files will be
    downloaded, and logcli will exit, and you can process the files as you wish.
    With the flag specified, the part files will be read in order, and the
    output printed to the terminal. The lines will be printed as soon as the
    next part is complete, you don't have to wait for all the parts to download
    before getting output. The --merge-parts flag will remove the part files
    when it is done reading each of them. To change this, you can use the
    --keep-parts flag, and the part files will not be removed.

instant-query [<flags>] <query>
    Run an instant LogQL query.

    The "instant-query" command is useful for evaluating a metric query for a
    single point in time. This is equivalent to the Grafana Explore table view;
    if you want a metrics query that is used to build a Grafana graph, you
    should use the "query" command instead.

    This command does not produce useful output when querying for log lines;
    you should always use the "query" command when you are running log queries.

    For more information about log queries and metric queries, refer to the
    LogQL documentation:

    https://grafana.com/docs/loki/latest/logql/

labels [<flags>] [<label>]
    Find values for a given label.

series [<flags>] <matcher>
    Run series query.

    The "series" command will take the provided label matcher and return all the
    log streams found in the time window.

    It is possible to send an empty label matcher '{}' to return all streams.

    Use the --analyze-labels flag to get a summary of the labels found in all
    streams. This is helpful to find high cardinality labels.

fmt
    Formats a LogQL query.

stats [<flags>] <query>
    Run a stats query.

    The "stats" command will take the provided query and return statistics
    from the index on how much data is contained in the matching stream(s).
    This only works against Loki instances using the TSDB index format.

    By default we look over the last hour of data; use --since to modify or
    provide specific start and end times with --from and --to respectively.

    Notice that when using --from and --to then ensure to use RFC3339Nano time
    format, but without timezone at the end. The local timezone will be added
    automatically or if using --timezone flag.

    Example:

      logcli stats
         --timezone=UTC
         --from="2021-01-19T10:00:00Z"
         --to="2021-01-19T20:00:00Z"
         'my-query'

volume [<flags>] <query>
    Run a volume query.

    The "volume" command will take the provided label selector(s) and return
    aggregate volumes for series matching those volumes. This only works against
    Loki instances using the TSDB index format.

    By default we look over the last hour of data; use --since to modify or
    provide specific start and end times with --from and --to respectively.

    Notice that when using --from and --to then ensure to use RFC3339Nano time
    format, but without timezone at the end. The local timezone will be added
    automatically or if using --timezone flag.

    Example:

      logcli volume
         --timezone=UTC
         --from="2021-01-19T10:00:00Z"
         --to="2021-01-19T20:00:00Z"
         'my-query'

volume_range [<flags>] <query>
    Run a volume query and return timeseries data.

    The "volume_range" command will take the provided label selector(s) and
    return aggregate volumes for series matching those volumes, aggregated into
    buckets according to the step value. This only works against Loki instances
    using the TSDB index format.

    By default we look over the last hour of data; use --since to modify or
    provide specific start and end times with --from and --to respectively.

    Notice that when using --from and --to then ensure to use RFC3339Nano time
    format, but without timezone at the end. The local timezone will be added
    automatically or if using --timezone flag.

    Example:

      	logcli volume_range
      	   --timezone=UTC
      	   --from="2021-01-19T10:00:00Z"
      	   --to="2021-01-19T20:00:00Z"
           --step=1h
      	   'my-query'

detected-fields [<flags>] <query> [<field>]
    Run a query for detected fields..

delete <command> [<args> ...]
    Manage log deletion requests.

    The "detected-fields" command will return information about fields detected
    using either the "logfmt" or "json" parser against the log lines returned by
    the provided query for the provided time range.

    The "detected-fields" command will output extra information about the query
    and its results, such as the API URL, set of common labels, and set of
    excluded labels. This extra information can be suppressed with the --quiet
    flag.

    By default we look over the last hour of data; use --since to modify or
    provide specific start and end times with --from and --to respectively.

    Notice that when using --from and --to then ensure to use RFC3339Nano time
    format, but without timezone at the end. The local timezone will be added
    automatically or if using --timezone flag.

    Example:

      logcli detected-fields
         --timezone=UTC
         --from="2021-01-19T10:00:00Z"
         --to="2021-01-19T20:00:00Z"
         --output=jsonl
         'my-query'

    The output is limited to 100 fields by default; use --field-limit to
    increase. The query is limited to processing 1000 lines per subquery;
    use --line-limit to increase.
```

### `query` command reference

The output of `logcli help query`:

```shell
usage: logcli query [<flags>] <query>

Run a LogQL query.

The "query" command is useful for querying for logs. Logs can be returned in a
few output modes:

  raw: log line
  default: log timestamp + log labels + log line
  jsonl: JSON response from Loki API of log line

The output of the log can be specified with the "-o" flag, for example, "-o raw"
for the raw output format.

The "query" command will output extra information about the query and its
results, such as the API URL, set of common labels, and set of excluded labels.
This extra information can be suppressed with the --quiet flag.

By default we look over the last hour of data; use --since to modify or provide
specific start and end times with --from and --to respectively.

Notice that when using --from and --to then ensure to use RFC3339Nano time
format, but without timezone at the end. The local timezone will be added
automatically or if using --timezone flag. In default output mode the
--output-timestamp-format flag can be used to modify the output timestamp.

Example:

  logcli query
     --timezone=UTC
     --from="2021-01-19T10:00:00Z"
     --to="2021-01-19T20:00:00Z"
     --output=jsonl
     'my-query'

Example with --output-timestamp-format:

  logcli query
     --timezone=UTC
     --from="2021-01-19T10:00:00Z"
     --to="2021-01-19T20:00:00Z"
     --output-timestamp-format=rfc3339nano
     'my-query'

The output is limited to 30 entries by default; use --limit to increase.

While "query" does support metrics queries, its output contains multiple data
points between the start and end query time. This output is used to build
graphs, similar to what is seen in the Grafana Explore graph view. If you are
querying metrics and just want the most recent data point (like what is seen in
the Grafana Explore table view), then you should use the "instant-query" command
instead.

Parallelization:

You can download an unlimited number of logs in parallel, there are a few flags
which control this behaviour:

  --parallel-duration
  --parallel-max-workers
  --part-path-prefix
  --overwrite-completed-parts
  --merge-parts
  --keep-parts

Refer to the help for each flag for details about what each of them do.

Example:

  logcli query
     --timezone=UTC
     --from="2021-01-19T10:00:00Z"
     --to="2021-01-19T20:00:00Z"
     --output=jsonl
     --parallel-duration="15m"
     --parallel-max-workers="4"
     --part-path-prefix="/tmp/my_query"
     --merge-parts
     'my-query'

This example will create a queue of jobs to execute, each being 15 minutes in
duration. In this case, that means, for the 10-hour total duration, there will
be forty 15-minute jobs. The --limit flag is ignored.

It will start four workers, and they will each take a job to work on from the
queue until all the jobs have been completed.

Each job will save a "part" file to the location specified by the
--part-path-prefix. Different prefixes can be used to run multiple queries at
the same time. The timestamp of the start and end of the part is in the file
name. While the part is being downloaded, the filename will end in ".part",
when it is complete, the file will be renamed to remove this ".part" extension.
By default, if a completed part file is found, that part will not be downloaded
again. This can be overridden with the --overwrite-completed-parts flag.

Part file example using the previous command, adding --keep-parts so they are
not deleted:

Since we don't have the --forward flag, the parts will be downloaded in reverse.
Two of the workers have finished their jobs (last two files), and have picked up
the next jobs in the queue. Running ls, this is what we should expect to see.

$ ls -1 /tmp/my_query* /tmp/my_query_20210119T183000_20210119T184500.part.tmp
/tmp/my_query_20210119T184500_20210119T190000.part.tmp
/tmp/my_query_20210119T190000_20210119T191500.part.tmp
/tmp/my_query_20210119T191500_20210119T193000.part.tmp
/tmp/my_query_20210119T193000_20210119T194500.part
/tmp/my_query_20210119T194500_20210119T200000.part

If you do not specify the --merge-parts flag, the part files will be downloaded,
and logcli will exit, and you can process the files as you wish. With the flag
specified, the part files will be read in order, and the output printed to
the terminal. The lines will be printed as soon as the next part is complete,
you don't have to wait for all the parts to download before getting output.
The --merge-parts flag will remove the part files when it is done reading each
of them. To change this, you can use the --keep-parts flag, and the part files
will not be removed.


Flags:
      --[no-]help               Show context-sensitive help (also try
                                --help-long and --help-man).
      --[no-]version            Show application version.
  -q, --[no-]quiet              Suppress query metadata
      --[no-]stats              Show query statistics
  -o, --output=default          Specify output mode [default, raw, jsonl].
                                raw suppresses log labels and timestamp.
  -z, --timezone=Local          Specify the timezone to use when formatting
                                output timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                                Specify the format of timestamps in the default
                                output mode [rfc3339, rfc3339nano, rfc822z,
                                rfc1123z, stampmicro, stampmilli, stampnano,
                                unixdate]
      --cpuprofile=""           Specify the location for writing a CPU profile.
      --memprofile=""           Specify the location for writing a memory
                                profile.
      --[no-]stdin              Take input logs from stdin
      --addr="http://localhost:3100"  
                                Server address. Can also be set using LOKI_ADDR
                                env var. ($LOKI_ADDR)
      --username=""             Username for HTTP basic auth. Can also be set
                                using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""             Password for HTTP basic auth. Can also be set
                                using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""              Path to the server Certificate Authority.
                                Can also be set using LOKI_CA_CERT_PATH env var.
                                ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify    Server certificate TLS skip verify. Can also
                                be set using LOKI_TLS_SKIP_VERIFY env var.
                                ($LOKI_TLS_SKIP_VERIFY)
      --cert=""                 Path to the client certificate. Can also
                                be set using LOKI_CLIENT_CERT_PATH env var.
                                ($LOKI_CLIENT_CERT_PATH)
      --key=""                  Path to the client certificate key. Can also
                                be set using LOKI_CLIENT_KEY_PATH env var.
                                ($LOKI_CLIENT_KEY_PATH)
      --org-id=""               adds X-Scope-OrgID to API requests for
                                representing tenant ID. Useful for requesting
                                tenant data when bypassing an auth gateway.
                                Can also be set using LOKI_ORG_ID env var.
                                ($LOKI_ORG_ID)
      --query-tags=""           adds X-Query-Tags http header to API requests.
                                This header value will be part of `metrics.go`
                                statistics. Useful for tracking the query.
                                Can also be set using LOKI_QUERY_TAGS env var.
                                ($LOKI_QUERY_TAGS)
      --[no-]nocache            adds Cache-Control: no-cache http header to API
                                requests. Can also be set using LOKI_NO_CACHE
                                env var. ($LOKI_NO_CACHE)
      --bearer-token=""         adds the Authorization header to API
                                requests for authentication purposes.
                                Can also be set using LOKI_BEARER_TOKEN env var.
                                ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""    adds the Authorization header to API requests
                                for authentication purposes. Can also be
                                set using LOKI_BEARER_TOKEN_FILE env var.
                                ($LOKI_BEARER_TOKEN_FILE)
      --retries=0               How many times to retry each query when
                                getting an error response from Loki. Can also
                                be set using LOKI_CLIENT_RETRIES env var.
                                ($LOKI_CLIENT_RETRIES)
      --min-backoff=0           Minimum backoff time between retries. Can also
                                be set using LOKI_CLIENT_MIN_BACKOFF env var.
                                ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0           Maximum backoff time between retries. Can also
                                be set using LOKI_CLIENT_MAX_BACKOFF env var.
                                ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                                The authorization header used. Can also
                                be set using LOKI_AUTH_HEADER env var.
                                ($LOKI_AUTH_HEADER)
      --proxy-url=""            The http or https proxy to use when
                                making requests. Can also be set
                                using LOKI_HTTP_PROXY_URL env var.
                                ($LOKI_HTTP_PROXY_URL)
      --[no-]compress           Request that Loki compress returned
                                data in transit. Can also be set
                                using LOKI_HTTP_COMPRESSION env var.
                                ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy           Use ProxyFromEnvironment to use net/http
                                ProxyFromEnvironment configuration, eg
                                HTTP_PROXY ($LOKI_ENV_PROXY)
      --limit=30                Limit on number of entries to print. Setting it
                                to 0 will fetch all entries.
      --since=1h                Lookback window.
      --from=FROM               Start looking for logs at this absolute time
                                (inclusive)
      --to=TO                   Stop looking for logs at this absolute time
                                (exclusive)
      --step=STEP               Query resolution step width, for metric queries.
                                Evaluate the query at the specified step over
                                the time range.
      --interval=INTERVAL       Query interval, for log queries. Return entries
                                at the specified interval, ignoring those
                                between. **This parameter is experimental,
                                please see Issue 1779**
      --batch=1000              Query batch size to use until 'limit' is reached
      --parallel-duration=1h    Split the range into jobs of this length to
                                download the logs in parallel. This will
                                result in the logs being out of order.
                                Use --part-path-prefix to create a file per job
                                to maintain ordering.
      --parallel-max-workers=1  Max number of workers to start up for parallel
                                jobs. A value of 1 will not create any parallel
                                workers. When using parallel workers, limit is
                                ignored.
      --part-path-prefix=PART-PATH-PREFIX  
                                When set, each server response will be saved
                                to a file with this prefix. Creates files in
                                the format: 'prefix-utc_start-utc_end.part'.
                                Intended to be used with the parallel-* flags
                                so that you can combine the files to maintain
                                ordering based on the filename. Default is to
                                write to stdout.
      --[no-]overwrite-completed-parts  
                                Overwrites completed part files. This will
                                download the range again, and replace the
                                original completed part file. Default will skip
                                a range if it's part file is already downloaded.
      --[no-]merge-parts        Reads the part files in order and writes the
                                output to stdout. Original part files will be
                                deleted with this option.
      --[no-]keep-parts         Overrides the default behaviour of --merge-parts
                                which will delete the part files once all the
                                files have been read. This option will keep the
                                part files.
      --[no-]forward            Scan forwards through logs.
      --[no-]no-labels          Do not print any labels
      --exclude-label=EXCLUDE-LABEL ...  
                                Exclude labels given the provided key during
                                output.
      --include-label=INCLUDE-LABEL ...  
                                Include labels given the provided key during
                                output.
      --[no-]include-common-labels  
                                Include common labels in output for each log
                                line.
      --labels-length=0         Set a fixed padding to labels
      --store-config=""         Execute the current query using a configured
                                storage from a given Loki configuration file.
      --[no-]remote-schema      Execute the current query using a remote schema
                                retrieved from the configured -schema-store.
      --schema-store=""         Store used for retrieving remote schema.
      --[no-]colored-output     Show output with colored labels
  -t, --[no-]tail               Tail the logs
  -f, --[no-]follow             Alias for --tail
      --delay-for=0             Delay in tailing by number of seconds to
                                accumulate logs for re-ordering

Args:
  <query>  eg '{foo="bar",baz=~".*blip"} |~ ".*error.*"'
```

### `instant-query` command reference

The output of `logcli help instant-query`:

```shell
usage: logcli instant-query [<flags>] <query>

Run an instant LogQL query.

The "instant-query" command is useful for evaluating a metric query for a
single point in time. This is equivalent to the Grafana Explore table view;
if you want a metrics query that is used to build a Grafana graph, you should
use the "query" command instead.

This command does not produce useful output when querying for log lines;
you should always use the "query" command when you are running log queries.

For more information about log queries and metric queries, refer to the LogQL
documentation:

https://grafana.com/docs/loki/latest/logql/


Flags:
      --[no-]help             Show context-sensitive help (also try --help-long
                              and --help-man).
      --[no-]version          Show application version.
  -q, --[no-]quiet            Suppress query metadata
      --[no-]stats            Show query statistics
  -o, --output=default        Specify output mode [default, raw, jsonl].
                              raw suppresses log labels and timestamp.
  -z, --timezone=Local        Specify the timezone to use when formatting output
                              timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                              Specify the format of timestamps in the default
                              output mode [rfc3339, rfc3339nano, rfc822z,
                              rfc1123z, stampmicro, stampmilli, stampnano,
                              unixdate]
      --cpuprofile=""         Specify the location for writing a CPU profile.
      --memprofile=""         Specify the location for writing a memory profile.
      --[no-]stdin            Take input logs from stdin
      --addr="http://localhost:3100"  
                              Server address. Can also be set using LOKI_ADDR
                              env var. ($LOKI_ADDR)
      --username=""           Username for HTTP basic auth. Can also be set
                              using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""           Password for HTTP basic auth. Can also be set
                              using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""            Path to the server Certificate Authority.
                              Can also be set using LOKI_CA_CERT_PATH env var.
                              ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify  Server certificate TLS skip verify. Can also
                              be set using LOKI_TLS_SKIP_VERIFY env var.
                              ($LOKI_TLS_SKIP_VERIFY)
      --cert=""               Path to the client certificate. Can also
                              be set using LOKI_CLIENT_CERT_PATH env var.
                              ($LOKI_CLIENT_CERT_PATH)
      --key=""                Path to the client certificate key. Can also
                              be set using LOKI_CLIENT_KEY_PATH env var.
                              ($LOKI_CLIENT_KEY_PATH)
      --org-id=""             adds X-Scope-OrgID to API requests for
                              representing tenant ID. Useful for requesting
                              tenant data when bypassing an auth gateway.
                              Can also be set using LOKI_ORG_ID env var.
                              ($LOKI_ORG_ID)
      --query-tags=""         adds X-Query-Tags http header to API requests.
                              This header value will be part of `metrics.go`
                              statistics. Useful for tracking the query.
                              Can also be set using LOKI_QUERY_TAGS env var.
                              ($LOKI_QUERY_TAGS)
      --[no-]nocache          adds Cache-Control: no-cache http header to API
                              requests. Can also be set using LOKI_NO_CACHE env
                              var. ($LOKI_NO_CACHE)
      --bearer-token=""       adds the Authorization header to API requests for
                              authentication purposes. Can also be set using
                              LOKI_BEARER_TOKEN env var. ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""  adds the Authorization header to API requests
                              for authentication purposes. Can also be
                              set using LOKI_BEARER_TOKEN_FILE env var.
                              ($LOKI_BEARER_TOKEN_FILE)
      --retries=0             How many times to retry each query when
                              getting an error response from Loki. Can also
                              be set using LOKI_CLIENT_RETRIES env var.
                              ($LOKI_CLIENT_RETRIES)
      --min-backoff=0         Minimum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MIN_BACKOFF env var.
                              ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0         Maximum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MAX_BACKOFF env var.
                              ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                              The authorization header used. Can also
                              be set using LOKI_AUTH_HEADER env var.
                              ($LOKI_AUTH_HEADER)
      --proxy-url=""          The http or https proxy to use when
                              making requests. Can also be set
                              using LOKI_HTTP_PROXY_URL env var.
                              ($LOKI_HTTP_PROXY_URL)
      --[no-]compress         Request that Loki compress returned
                              data in transit. Can also be set
                              using LOKI_HTTP_COMPRESSION env var.
                              ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy         Use ProxyFromEnvironment to use net/http
                              ProxyFromEnvironment configuration, eg HTTP_PROXY
                              ($LOKI_ENV_PROXY)
      --limit=30              Limit on number of entries to print. Setting it to
                              0 will fetch all entries.
      --now=NOW               Time at which to execute the instant query.
      --[no-]forward          Scan forwards through logs.
      --[no-]no-labels        Do not print any labels
      --exclude-label=EXCLUDE-LABEL ...  
                              Exclude labels given the provided key during
                              output.
      --include-label=INCLUDE-LABEL ...  
                              Include labels given the provided key during
                              output.
      --[no-]include-common-labels  
                              Include common labels in output for each log line.
      --labels-length=0       Set a fixed padding to labels
      --store-config=""       Execute the current query using a configured
                              storage from a given Loki configuration file.
      --[no-]remote-schema    Execute the current query using a remote schema
                              retrieved from the configured -schema-store.
      --schema-store=""       Store used for retrieving remote schema.
      --[no-]colored-output   Show output with colored labels

Args:
  <query>  eg 'rate({foo="bar"} |~ ".*error.*" [5m])'
```

### `labels` command reference

The output of `logcli help labels`:

```shell
usage: logcli labels [<flags>] [<label>]

Find values for a given label.


Flags:
      --[no-]help             Show context-sensitive help (also try --help-long
                              and --help-man).
      --[no-]version          Show application version.
  -q, --[no-]quiet            Suppress query metadata
      --[no-]stats            Show query statistics
  -o, --output=default        Specify output mode [default, raw, jsonl].
                              raw suppresses log labels and timestamp.
  -z, --timezone=Local        Specify the timezone to use when formatting output
                              timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                              Specify the format of timestamps in the default
                              output mode [rfc3339, rfc3339nano, rfc822z,
                              rfc1123z, stampmicro, stampmilli, stampnano,
                              unixdate]
      --cpuprofile=""         Specify the location for writing a CPU profile.
      --memprofile=""         Specify the location for writing a memory profile.
      --[no-]stdin            Take input logs from stdin
      --addr="http://localhost:3100"  
                              Server address. Can also be set using LOKI_ADDR
                              env var. ($LOKI_ADDR)
      --username=""           Username for HTTP basic auth. Can also be set
                              using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""           Password for HTTP basic auth. Can also be set
                              using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""            Path to the server Certificate Authority.
                              Can also be set using LOKI_CA_CERT_PATH env var.
                              ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify  Server certificate TLS skip verify. Can also
                              be set using LOKI_TLS_SKIP_VERIFY env var.
                              ($LOKI_TLS_SKIP_VERIFY)
      --cert=""               Path to the client certificate. Can also
                              be set using LOKI_CLIENT_CERT_PATH env var.
                              ($LOKI_CLIENT_CERT_PATH)
      --key=""                Path to the client certificate key. Can also
                              be set using LOKI_CLIENT_KEY_PATH env var.
                              ($LOKI_CLIENT_KEY_PATH)
      --org-id=""             adds X-Scope-OrgID to API requests for
                              representing tenant ID. Useful for requesting
                              tenant data when bypassing an auth gateway.
                              Can also be set using LOKI_ORG_ID env var.
                              ($LOKI_ORG_ID)
      --query-tags=""         adds X-Query-Tags http header to API requests.
                              This header value will be part of `metrics.go`
                              statistics. Useful for tracking the query.
                              Can also be set using LOKI_QUERY_TAGS env var.
                              ($LOKI_QUERY_TAGS)
      --[no-]nocache          adds Cache-Control: no-cache http header to API
                              requests. Can also be set using LOKI_NO_CACHE env
                              var. ($LOKI_NO_CACHE)
      --bearer-token=""       adds the Authorization header to API requests for
                              authentication purposes. Can also be set using
                              LOKI_BEARER_TOKEN env var. ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""  adds the Authorization header to API requests
                              for authentication purposes. Can also be
                              set using LOKI_BEARER_TOKEN_FILE env var.
                              ($LOKI_BEARER_TOKEN_FILE)
      --retries=0             How many times to retry each query when
                              getting an error response from Loki. Can also
                              be set using LOKI_CLIENT_RETRIES env var.
                              ($LOKI_CLIENT_RETRIES)
      --min-backoff=0         Minimum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MIN_BACKOFF env var.
                              ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0         Maximum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MAX_BACKOFF env var.
                              ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                              The authorization header used. Can also
                              be set using LOKI_AUTH_HEADER env var.
                              ($LOKI_AUTH_HEADER)
      --proxy-url=""          The http or https proxy to use when
                              making requests. Can also be set
                              using LOKI_HTTP_PROXY_URL env var.
                              ($LOKI_HTTP_PROXY_URL)
      --[no-]compress         Request that Loki compress returned
                              data in transit. Can also be set
                              using LOKI_HTTP_COMPRESSION env var.
                              ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy         Use ProxyFromEnvironment to use net/http
                              ProxyFromEnvironment configuration, eg HTTP_PROXY
                              ($LOKI_ENV_PROXY)
      --since=1h              Lookback window.
      --from=FROM             Start looking for labels at this absolute time
                              (inclusive)
      --to=TO                 Stop looking for labels at this absolute time
                              (exclusive)

Args:
  [<label>]  The name of the label.
```

### `series` command reference

The output of `logcli help series`:

```shell
usage: logcli series [<flags>] <matcher>

Run series query.

The "series" command will take the provided label matcher and return all the log
streams found in the time window.

It is possible to send an empty label matcher '{}' to return all streams.

Use the --analyze-labels flag to get a summary of the labels found in all
streams. This is helpful to find high cardinality labels.


Flags:
      --[no-]help             Show context-sensitive help (also try --help-long
                              and --help-man).
      --[no-]version          Show application version.
  -q, --[no-]quiet            Suppress query metadata
      --[no-]stats            Show query statistics
  -o, --output=default        Specify output mode [default, raw, jsonl].
                              raw suppresses log labels and timestamp.
  -z, --timezone=Local        Specify the timezone to use when formatting output
                              timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                              Specify the format of timestamps in the default
                              output mode [rfc3339, rfc3339nano, rfc822z,
                              rfc1123z, stampmicro, stampmilli, stampnano,
                              unixdate]
      --cpuprofile=""         Specify the location for writing a CPU profile.
      --memprofile=""         Specify the location for writing a memory profile.
      --[no-]stdin            Take input logs from stdin
      --addr="http://localhost:3100"  
                              Server address. Can also be set using LOKI_ADDR
                              env var. ($LOKI_ADDR)
      --username=""           Username for HTTP basic auth. Can also be set
                              using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""           Password for HTTP basic auth. Can also be set
                              using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""            Path to the server Certificate Authority.
                              Can also be set using LOKI_CA_CERT_PATH env var.
                              ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify  Server certificate TLS skip verify. Can also
                              be set using LOKI_TLS_SKIP_VERIFY env var.
                              ($LOKI_TLS_SKIP_VERIFY)
      --cert=""               Path to the client certificate. Can also
                              be set using LOKI_CLIENT_CERT_PATH env var.
                              ($LOKI_CLIENT_CERT_PATH)
      --key=""                Path to the client certificate key. Can also
                              be set using LOKI_CLIENT_KEY_PATH env var.
                              ($LOKI_CLIENT_KEY_PATH)
      --org-id=""             adds X-Scope-OrgID to API requests for
                              representing tenant ID. Useful for requesting
                              tenant data when bypassing an auth gateway.
                              Can also be set using LOKI_ORG_ID env var.
                              ($LOKI_ORG_ID)
      --query-tags=""         adds X-Query-Tags http header to API requests.
                              This header value will be part of `metrics.go`
                              statistics. Useful for tracking the query.
                              Can also be set using LOKI_QUERY_TAGS env var.
                              ($LOKI_QUERY_TAGS)
      --[no-]nocache          adds Cache-Control: no-cache http header to API
                              requests. Can also be set using LOKI_NO_CACHE env
                              var. ($LOKI_NO_CACHE)
      --bearer-token=""       adds the Authorization header to API requests for
                              authentication purposes. Can also be set using
                              LOKI_BEARER_TOKEN env var. ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""  adds the Authorization header to API requests
                              for authentication purposes. Can also be
                              set using LOKI_BEARER_TOKEN_FILE env var.
                              ($LOKI_BEARER_TOKEN_FILE)
      --retries=0             How many times to retry each query when
                              getting an error response from Loki. Can also
                              be set using LOKI_CLIENT_RETRIES env var.
                              ($LOKI_CLIENT_RETRIES)
      --min-backoff=0         Minimum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MIN_BACKOFF env var.
                              ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0         Maximum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MAX_BACKOFF env var.
                              ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                              The authorization header used. Can also
                              be set using LOKI_AUTH_HEADER env var.
                              ($LOKI_AUTH_HEADER)
      --proxy-url=""          The http or https proxy to use when
                              making requests. Can also be set
                              using LOKI_HTTP_PROXY_URL env var.
                              ($LOKI_HTTP_PROXY_URL)
      --[no-]compress         Request that Loki compress returned
                              data in transit. Can also be set
                              using LOKI_HTTP_COMPRESSION env var.
                              ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy         Use ProxyFromEnvironment to use net/http
                              ProxyFromEnvironment configuration, eg HTTP_PROXY
                              ($LOKI_ENV_PROXY)
      --since=1h              Lookback window.
      --from=FROM             Start looking for logs at this absolute time
                              (inclusive)
      --to=TO                 Stop looking for logs at this absolute time
                              (exclusive)
      --[no-]analyze-labels   Printout a summary of labels including count of
                              label value combinations, useful for debugging
                              high cardinality series

Args:
  <matcher>  eg '{foo="bar",baz=~".*blip"}'

```

### `stats` command reference

The output of `logcli help stats`:

```shell
usage: logcli stats [<flags>] <query>

Run a stats query.

The "stats" command will take the provided query and return statistics from the
index on how much data is contained in the matching stream(s). This only works
against Loki instances using the TSDB index format.

By default we look over the last hour of data; use --since to modify or provide
specific start and end times with --from and --to respectively.

Notice that when using --from and --to then ensure to use RFC3339Nano time
format, but without timezone at the end. The local timezone will be added
automatically or if using --timezone flag.

Example:

  logcli stats
     --timezone=UTC
     --from="2021-01-19T10:00:00Z"
     --to="2021-01-19T20:00:00Z"
     'my-query'


Flags:
      --[no-]help             Show context-sensitive help (also try --help-long
                              and --help-man).
      --[no-]version          Show application version.
  -q, --[no-]quiet            Suppress query metadata
      --[no-]stats            Show query statistics
  -o, --output=default        Specify output mode [default, raw, jsonl].
                              raw suppresses log labels and timestamp.
  -z, --timezone=Local        Specify the timezone to use when formatting output
                              timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                              Specify the format of timestamps in the default
                              output mode [rfc3339, rfc3339nano, rfc822z,
                              rfc1123z, stampmicro, stampmilli, stampnano,
                              unixdate]
      --cpuprofile=""         Specify the location for writing a CPU profile.
      --memprofile=""         Specify the location for writing a memory profile.
      --[no-]stdin            Take input logs from stdin
      --addr="http://localhost:3100"  
                              Server address. Can also be set using LOKI_ADDR
                              env var. ($LOKI_ADDR)
      --username=""           Username for HTTP basic auth. Can also be set
                              using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""           Password for HTTP basic auth. Can also be set
                              using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""            Path to the server Certificate Authority.
                              Can also be set using LOKI_CA_CERT_PATH env var.
                              ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify  Server certificate TLS skip verify. Can also
                              be set using LOKI_TLS_SKIP_VERIFY env var.
                              ($LOKI_TLS_SKIP_VERIFY)
      --cert=""               Path to the client certificate. Can also
                              be set using LOKI_CLIENT_CERT_PATH env var.
                              ($LOKI_CLIENT_CERT_PATH)
      --key=""                Path to the client certificate key. Can also
                              be set using LOKI_CLIENT_KEY_PATH env var.
                              ($LOKI_CLIENT_KEY_PATH)
      --org-id=""             adds X-Scope-OrgID to API requests for
                              representing tenant ID. Useful for requesting
                              tenant data when bypassing an auth gateway.
                              Can also be set using LOKI_ORG_ID env var.
                              ($LOKI_ORG_ID)
      --query-tags=""         adds X-Query-Tags http header to API requests.
                              This header value will be part of `metrics.go`
                              statistics. Useful for tracking the query.
                              Can also be set using LOKI_QUERY_TAGS env var.
                              ($LOKI_QUERY_TAGS)
      --[no-]nocache          adds Cache-Control: no-cache http header to API
                              requests. Can also be set using LOKI_NO_CACHE env
                              var. ($LOKI_NO_CACHE)
      --bearer-token=""       adds the Authorization header to API requests for
                              authentication purposes. Can also be set using
                              LOKI_BEARER_TOKEN env var. ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""  adds the Authorization header to API requests
                              for authentication purposes. Can also be
                              set using LOKI_BEARER_TOKEN_FILE env var.
                              ($LOKI_BEARER_TOKEN_FILE)
      --retries=0             How many times to retry each query when
                              getting an error response from Loki. Can also
                              be set using LOKI_CLIENT_RETRIES env var.
                              ($LOKI_CLIENT_RETRIES)
      --min-backoff=0         Minimum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MIN_BACKOFF env var.
                              ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0         Maximum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MAX_BACKOFF env var.
                              ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                              The authorization header used. Can also
                              be set using LOKI_AUTH_HEADER env var.
                              ($LOKI_AUTH_HEADER)
      --proxy-url=""          The http or https proxy to use when
                              making requests. Can also be set
                              using LOKI_HTTP_PROXY_URL env var.
                              ($LOKI_HTTP_PROXY_URL)
      --[no-]compress         Request that Loki compress returned
                              data in transit. Can also be set
                              using LOKI_HTTP_COMPRESSION env var.
                              ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy         Use ProxyFromEnvironment to use net/http
                              ProxyFromEnvironment configuration, eg HTTP_PROXY
                              ($LOKI_ENV_PROXY)
      --since=1h              Lookback window.
      --from=FROM             Start looking for logs at this absolute time
                              (inclusive)
      --to=TO                 Stop looking for logs at this absolute time
                              (exclusive)

Args:
  <query>  eg '{foo="bar",baz=~".*blip"} |~ ".*error.*"'
```

### `volume` command reference

The output of `logcli help volume`:

```shell
usage: logcli volume [<flags>] <query>

Run a volume query.

The "volume" command will take the provided label selector(s) and return
aggregate volumes for series matching those volumes. This only works against
Loki instances using the TSDB index format.

By default we look over the last hour of data; use --since to modify or provide
specific start and end times with --from and --to respectively.

Notice that when using --from and --to then ensure to use RFC3339Nano time
format, but without timezone at the end. The local timezone will be added
automatically or if using --timezone flag.

Example:

  logcli volume
     --timezone=UTC
     --from="2021-01-19T10:00:00Z"
     --to="2021-01-19T20:00:00Z"
     'my-query'


Flags:
      --[no-]help               Show context-sensitive help (also try
                                --help-long and --help-man).
      --[no-]version            Show application version.
  -q, --[no-]quiet              Suppress query metadata
      --[no-]stats              Show query statistics
  -o, --output=default          Specify output mode [default, raw, jsonl].
                                raw suppresses log labels and timestamp.
  -z, --timezone=Local          Specify the timezone to use when formatting
                                output timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                                Specify the format of timestamps in the default
                                output mode [rfc3339, rfc3339nano, rfc822z,
                                rfc1123z, stampmicro, stampmilli, stampnano,
                                unixdate]
      --cpuprofile=""           Specify the location for writing a CPU profile.
      --memprofile=""           Specify the location for writing a memory
                                profile.
      --[no-]stdin              Take input logs from stdin
      --addr="http://localhost:3100"  
                                Server address. Can also be set using LOKI_ADDR
                                env var. ($LOKI_ADDR)
      --username=""             Username for HTTP basic auth. Can also be set
                                using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""             Password for HTTP basic auth. Can also be set
                                using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""              Path to the server Certificate Authority.
                                Can also be set using LOKI_CA_CERT_PATH env var.
                                ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify    Server certificate TLS skip verify. Can also
                                be set using LOKI_TLS_SKIP_VERIFY env var.
                                ($LOKI_TLS_SKIP_VERIFY)
      --cert=""                 Path to the client certificate. Can also
                                be set using LOKI_CLIENT_CERT_PATH env var.
                                ($LOKI_CLIENT_CERT_PATH)
      --key=""                  Path to the client certificate key. Can also
                                be set using LOKI_CLIENT_KEY_PATH env var.
                                ($LOKI_CLIENT_KEY_PATH)
      --org-id=""               adds X-Scope-OrgID to API requests for
                                representing tenant ID. Useful for requesting
                                tenant data when bypassing an auth gateway.
                                Can also be set using LOKI_ORG_ID env var.
                                ($LOKI_ORG_ID)
      --query-tags=""           adds X-Query-Tags http header to API requests.
                                This header value will be part of `metrics.go`
                                statistics. Useful for tracking the query.
                                Can also be set using LOKI_QUERY_TAGS env var.
                                ($LOKI_QUERY_TAGS)
      --[no-]nocache            adds Cache-Control: no-cache http header to API
                                requests. Can also be set using LOKI_NO_CACHE
                                env var. ($LOKI_NO_CACHE)
      --bearer-token=""         adds the Authorization header to API
                                requests for authentication purposes.
                                Can also be set using LOKI_BEARER_TOKEN env var.
                                ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""    adds the Authorization header to API requests
                                for authentication purposes. Can also be
                                set using LOKI_BEARER_TOKEN_FILE env var.
                                ($LOKI_BEARER_TOKEN_FILE)
      --retries=0               How many times to retry each query when
                                getting an error response from Loki. Can also
                                be set using LOKI_CLIENT_RETRIES env var.
                                ($LOKI_CLIENT_RETRIES)
      --min-backoff=0           Minimum backoff time between retries. Can also
                                be set using LOKI_CLIENT_MIN_BACKOFF env var.
                                ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0           Maximum backoff time between retries. Can also
                                be set using LOKI_CLIENT_MAX_BACKOFF env var.
                                ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                                The authorization header used. Can also
                                be set using LOKI_AUTH_HEADER env var.
                                ($LOKI_AUTH_HEADER)
      --proxy-url=""            The http or https proxy to use when
                                making requests. Can also be set
                                using LOKI_HTTP_PROXY_URL env var.
                                ($LOKI_HTTP_PROXY_URL)
      --[no-]compress           Request that Loki compress returned
                                data in transit. Can also be set
                                using LOKI_HTTP_COMPRESSION env var.
                                ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy           Use ProxyFromEnvironment to use net/http
                                ProxyFromEnvironment configuration, eg
                                HTTP_PROXY ($LOKI_ENV_PROXY)
      --since=1h                Lookback window.
      --from=FROM               Start looking for logs at this absolute time
                                (inclusive)
      --to=TO                   Stop looking for logs at this absolute time
                                (exclusive)
      --limit=30                Limit on number of series to return volumes for.
      --targetLabels=TARGETLABELS ...  
                                List of labels to aggregate results into.
      --[no-]aggregateByLabels  Whether to aggregate results by label name only.

Args:
  <query>  eg '{foo="bar",baz=~".*blip"}
```

### `volume_range` command reference

The output of `logcli help volume_range`:

```shell
usage: logcli volume_range [<flags>] <query>

Run a volume query and return timeseries data.

The "volume_range" command will take the provided label selector(s) and return
aggregate volumes for series matching those volumes, aggregated into buckets
according to the step value. This only works against Loki instances using the
TSDB index format.

By default we look over the last hour of data; use --since to modify or provide
specific start and end times with --from and --to respectively.

Notice that when using --from and --to then ensure to use RFC3339Nano time
format, but without timezone at the end. The local timezone will be added
automatically or if using --timezone flag.

Example:

  	logcli volume_range
  	   --timezone=UTC
  	   --from="2021-01-19T10:00:00Z"
  	   --to="2021-01-19T20:00:00Z"
       --step=1h
  	   'my-query'


Flags:
      --[no-]help               Show context-sensitive help (also try
                                --help-long and --help-man).
      --[no-]version            Show application version.
  -q, --[no-]quiet              Suppress query metadata
      --[no-]stats              Show query statistics
  -o, --output=default          Specify output mode [default, raw, jsonl].
                                raw suppresses log labels and timestamp.
  -z, --timezone=Local          Specify the timezone to use when formatting
                                output timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                                Specify the format of timestamps in the default
                                output mode [rfc3339, rfc3339nano, rfc822z,
                                rfc1123z, stampmicro, stampmilli, stampnano,
                                unixdate]
      --cpuprofile=""           Specify the location for writing a CPU profile.
      --memprofile=""           Specify the location for writing a memory
                                profile.
      --[no-]stdin              Take input logs from stdin
      --addr="http://localhost:3100"  
                                Server address. Can also be set using LOKI_ADDR
                                env var. ($LOKI_ADDR)
      --username=""             Username for HTTP basic auth. Can also be set
                                using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""             Password for HTTP basic auth. Can also be set
                                using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""              Path to the server Certificate Authority.
                                Can also be set using LOKI_CA_CERT_PATH env var.
                                ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify    Server certificate TLS skip verify. Can also
                                be set using LOKI_TLS_SKIP_VERIFY env var.
                                ($LOKI_TLS_SKIP_VERIFY)
      --cert=""                 Path to the client certificate. Can also
                                be set using LOKI_CLIENT_CERT_PATH env var.
                                ($LOKI_CLIENT_CERT_PATH)
      --key=""                  Path to the client certificate key. Can also
                                be set using LOKI_CLIENT_KEY_PATH env var.
                                ($LOKI_CLIENT_KEY_PATH)
      --org-id=""               adds X-Scope-OrgID to API requests for
                                representing tenant ID. Useful for requesting
                                tenant data when bypassing an auth gateway.
                                Can also be set using LOKI_ORG_ID env var.
                                ($LOKI_ORG_ID)
      --query-tags=""           adds X-Query-Tags http header to API requests.
                                This header value will be part of `metrics.go`
                                statistics. Useful for tracking the query.
                                Can also be set using LOKI_QUERY_TAGS env var.
                                ($LOKI_QUERY_TAGS)
      --[no-]nocache            adds Cache-Control: no-cache http header to API
                                requests. Can also be set using LOKI_NO_CACHE
                                env var. ($LOKI_NO_CACHE)
      --bearer-token=""         adds the Authorization header to API
                                requests for authentication purposes.
                                Can also be set using LOKI_BEARER_TOKEN env var.
                                ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""    adds the Authorization header to API requests
                                for authentication purposes. Can also be
                                set using LOKI_BEARER_TOKEN_FILE env var.
                                ($LOKI_BEARER_TOKEN_FILE)
      --retries=0               How many times to retry each query when
                                getting an error response from Loki. Can also
                                be set using LOKI_CLIENT_RETRIES env var.
                                ($LOKI_CLIENT_RETRIES)
      --min-backoff=0           Minimum backoff time between retries. Can also
                                be set using LOKI_CLIENT_MIN_BACKOFF env var.
                                ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0           Maximum backoff time between retries. Can also
                                be set using LOKI_CLIENT_MAX_BACKOFF env var.
                                ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                                The authorization header used. Can also
                                be set using LOKI_AUTH_HEADER env var.
                                ($LOKI_AUTH_HEADER)
      --proxy-url=""            The http or https proxy to use when
                                making requests. Can also be set
                                using LOKI_HTTP_PROXY_URL env var.
                                ($LOKI_HTTP_PROXY_URL)
      --[no-]compress           Request that Loki compress returned
                                data in transit. Can also be set
                                using LOKI_HTTP_COMPRESSION env var.
                                ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy           Use ProxyFromEnvironment to use net/http
                                ProxyFromEnvironment configuration, eg
                                HTTP_PROXY ($LOKI_ENV_PROXY)
      --since=1h                Lookback window.
      --from=FROM               Start looking for logs at this absolute time
                                (inclusive)
      --to=TO                   Stop looking for logs at this absolute time
                                (exclusive)
      --limit=30                Limit on number of series to return volumes for.
      --targetLabels=TARGETLABELS ...  
                                List of labels to aggregate results into.
      --[no-]aggregateByLabels  Whether to aggregate results by label name only.
      --step=1h                 Query resolution step width, roll up volumes
                                into buckets cover step time each.

Args:
  <query>  eg '{foo="bar",baz=~".*blip"}
```

### `detected-fields` command reference

The output of `logcli help detected-fields`:

```shell
usage: logcli detected-fields [<flags>] <query> [<field>]

Run a query for detected fields..

The "detected-fields" command will return information about fields detected
using either the "logfmt" or "json" parser against the log lines returned by the
provided query for the provided time range.

The "detected-fields" command will output extra information about the query
and its results, such as the API URL, set of common labels, and set of excluded
labels. This extra information can be suppressed with the --quiet flag.

By default we look over the last hour of data; use --since to modify or provide
specific start and end times with --from and --to respectively.

Notice that when using --from and --to then ensure to use RFC3339Nano time
format, but without timezone at the end. The local timezone will be added
automatically or if using --timezone flag.

Example:

  logcli detected-fields
     --timezone=UTC
     --from="2021-01-19T10:00:00Z"
     --to="2021-01-19T20:00:00Z"
     --output=jsonl
     'my-query'

The output is limited to 100 fields by default; use --field-limit to increase.
The query is limited to processing 1000 lines per subquery; use --line-limit to
increase.


Flags:
      --[no-]help             Show context-sensitive help (also try --help-long
                              and --help-man).
      --[no-]version          Show application version.
  -q, --[no-]quiet            Suppress query metadata
      --[no-]stats            Show query statistics
  -o, --output=default        Specify output mode [default, raw, jsonl].
                              raw suppresses log labels and timestamp.
  -z, --timezone=Local        Specify the timezone to use when formatting output
                              timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                              Specify the format of timestamps in the default
                              output mode [rfc3339, rfc3339nano, rfc822z,
                              rfc1123z, stampmicro, stampmilli, stampnano,
                              unixdate]
      --cpuprofile=""         Specify the location for writing a CPU profile.
      --memprofile=""         Specify the location for writing a memory profile.
      --[no-]stdin            Take input logs from stdin
      --addr="http://localhost:3100"  
                              Server address. Can also be set using LOKI_ADDR
                              env var. ($LOKI_ADDR)
      --username=""           Username for HTTP basic auth. Can also be set
                              using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""           Password for HTTP basic auth. Can also be set
                              using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""            Path to the server Certificate Authority.
                              Can also be set using LOKI_CA_CERT_PATH env var.
                              ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify  Server certificate TLS skip verify. Can also
                              be set using LOKI_TLS_SKIP_VERIFY env var.
                              ($LOKI_TLS_SKIP_VERIFY)
      --cert=""               Path to the client certificate. Can also
                              be set using LOKI_CLIENT_CERT_PATH env var.
                              ($LOKI_CLIENT_CERT_PATH)
      --key=""                Path to the client certificate key. Can also
                              be set using LOKI_CLIENT_KEY_PATH env var.
                              ($LOKI_CLIENT_KEY_PATH)
      --org-id=""             adds X-Scope-OrgID to API requests for
                              representing tenant ID. Useful for requesting
                              tenant data when bypassing an auth gateway.
                              Can also be set using LOKI_ORG_ID env var.
                              ($LOKI_ORG_ID)
      --query-tags=""         adds X-Query-Tags http header to API requests.
                              This header value will be part of `metrics.go`
                              statistics. Useful for tracking the query.
                              Can also be set using LOKI_QUERY_TAGS env var.
                              ($LOKI_QUERY_TAGS)
      --[no-]nocache          adds Cache-Control: no-cache http header to API
                              requests. Can also be set using LOKI_NO_CACHE env
                              var. ($LOKI_NO_CACHE)
      --bearer-token=""       adds the Authorization header to API requests for
                              authentication purposes. Can also be set using
                              LOKI_BEARER_TOKEN env var. ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""  adds the Authorization header to API requests
                              for authentication purposes. Can also be
                              set using LOKI_BEARER_TOKEN_FILE env var.
                              ($LOKI_BEARER_TOKEN_FILE)
      --retries=0             How many times to retry each query when
                              getting an error response from Loki. Can also
                              be set using LOKI_CLIENT_RETRIES env var.
                              ($LOKI_CLIENT_RETRIES)
      --min-backoff=0         Minimum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MIN_BACKOFF env var.
                              ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0         Maximum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MAX_BACKOFF env var.
                              ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                              The authorization header used. Can also
                              be set using LOKI_AUTH_HEADER env var.
                              ($LOKI_AUTH_HEADER)
      --proxy-url=""          The http or https proxy to use when
                              making requests. Can also be set
                              using LOKI_HTTP_PROXY_URL env var.
                              ($LOKI_HTTP_PROXY_URL)
      --[no-]compress         Request that Loki compress returned
                              data in transit. Can also be set
                              using LOKI_HTTP_COMPRESSION env var.
                              ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy         Use ProxyFromEnvironment to use net/http
                              ProxyFromEnvironment configuration, eg HTTP_PROXY
                              ($LOKI_ENV_PROXY)
      --limit=100             Limit on number of fields or values to return.
      --line-limit=1000       Limit the number of lines each subquery is allowed
                              to process.
      --since=1h              Lookback window.
      --from=FROM             Start looking for logs at this absolute time
                              (inclusive)
      --to=TO                 Stop looking for logs at this absolute time
                              (exclusive)
      --step=10s              Query resolution step width, for metric queries.
                              Evaluate the query at the specified step over the
                              time range.

Args:
  <query>    eg '{foo="bar",baz=~".*blip"} |~ ".*error.*"'
  [<field>]  The name of the field.
```

### `delete` command reference

The output of `logcli help delete`:

```shell
usage: logcli delete <command> [<args> ...]

Manage log deletion requests.


Flags:
      --[no-]help             Show context-sensitive help (also try --help-long
                              and --help-man).
      --[no-]version          Show application version.
  -q, --[no-]quiet            Suppress query metadata
      --[no-]stats            Show query statistics
  -o, --output=default        Specify output mode [default, raw, jsonl].
                              raw suppresses log labels and timestamp.
  -z, --timezone=Local        Specify the timezone to use when formatting output
                              timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                              Specify the format of timestamps in the default
                              output mode [rfc3339, rfc3339nano, rfc822z,
                              rfc1123z, stampmicro, stampmilli, stampnano,
                              unixdate]
      --cpuprofile=""         Specify the location for writing a CPU profile.
      --memprofile=""         Specify the location for writing a memory profile.
      --[no-]stdin            Take input logs from stdin
      --addr="http://localhost:3100"  
                              Server address. Can also be set using LOKI_ADDR
                              env var. ($LOKI_ADDR)
      --username=""           Username for HTTP basic auth. Can also be set
                              using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""           Password for HTTP basic auth. Can also be set
                              using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""            Path to the server Certificate Authority.
                              Can also be set using LOKI_CA_CERT_PATH env var.
                              ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify  Server certificate TLS skip verify. Can also
                              be set using LOKI_TLS_SKIP_VERIFY env var.
                              ($LOKI_TLS_SKIP_VERIFY)
      --cert=""               Path to the client certificate. Can also
                              be set using LOKI_CLIENT_CERT_PATH env var.
                              ($LOKI_CLIENT_CERT_PATH)
      --key=""                Path to the client certificate key. Can also
                              be set using LOKI_CLIENT_KEY_PATH env var.
                              ($LOKI_CLIENT_KEY_PATH)
      --org-id=""             adds X-Scope-OrgID to API requests for
                              representing tenant ID. Useful for requesting
                              tenant data when bypassing an auth gateway.
                              Can also be set using LOKI_ORG_ID env var.
                              ($LOKI_ORG_ID)
      --query-tags=""         adds X-Query-Tags http header to API requests.
                              This header value will be part of `metrics.go`
                              statistics. Useful for tracking the query.
                              Can also be set using LOKI_QUERY_TAGS env var.
                              ($LOKI_QUERY_TAGS)
      --[no-]nocache          adds Cache-Control: no-cache http header to API
                              requests. Can also be set using LOKI_NO_CACHE env
                              var. ($LOKI_NO_CACHE)
      --bearer-token=""       adds the Authorization header to API requests for
                              authentication purposes. Can also be set using
                              LOKI_BEARER_TOKEN env var. ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""  adds the Authorization header to API requests
                              for authentication purposes. Can also be
                              set using LOKI_BEARER_TOKEN_FILE env var.
                              ($LOKI_BEARER_TOKEN_FILE)
      --retries=0             How many times to retry each query when
                              getting an error response from Loki. Can also
                              be set using LOKI_CLIENT_RETRIES env var.
                              ($LOKI_CLIENT_RETRIES)
      --min-backoff=0         Minimum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MIN_BACKOFF env var.
                              ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0         Maximum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MAX_BACKOFF env var.
                              ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                              The authorization header used. Can also
                              be set using LOKI_AUTH_HEADER env var.
                              ($LOKI_AUTH_HEADER)
      --proxy-url=""          The http or https proxy to use when
                              making requests. Can also be set
                              using LOKI_HTTP_PROXY_URL env var.
                              ($LOKI_HTTP_PROXY_URL)
      --[no-]compress         Request that Loki compress returned
                              data in transit. Can also be set
                              using LOKI_HTTP_COMPRESSION env var.
                              ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy         Use ProxyFromEnvironment to use net/http
                              ProxyFromEnvironment configuration, eg HTTP_PROXY
                              ($LOKI_ENV_PROXY)

Subcommands:
delete create [<flags>] <query>
    Create a new log deletion request.

    The "delete create" command creates a new log deletion request for the
    specified query and time range.

    Example:

      logcli delete create '{job="app"}' --from="2023-01-01T00:00:00Z" --to="2023-01-02T00:00:00Z"

delete list
    List existing log deletion requests.

    The "delete list" command lists all existing log deletion requests for the
    tenant.

    Example:

      logcli delete list
      logcli delete list --output=json

delete cancel --request-id=REQUEST-ID [<flags>]
    Cancel a log deletion request.

    The "delete cancel" command cancels an existing log deletion request by ID.

    Example:

      logcli delete cancel --request-id="abc123"
      logcli delete cancel --request-id="abc123" --force
```

### `delete create` command reference

The output of `logcli help delete create`:

```shell
usage: logcli delete create [<flags>] <query>

Create a new log deletion request.

The "delete create" command creates a new log deletion request for the specified
query and time range.

Example:

  logcli delete create '{job="app"}' --from="2023-01-01T00:00:00Z" --to="2023-01-02T00:00:00Z"


Flags:
      --[no-]help             Show context-sensitive help (also try --help-long
                              and --help-man).
      --[no-]version          Show application version.
  -q, --[no-]quiet            Suppress query metadata
      --[no-]stats            Show query statistics
  -o, --output=default        Specify output mode [default, raw, jsonl].
                              raw suppresses log labels and timestamp.
  -z, --timezone=Local        Specify the timezone to use when formatting output
                              timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                              Specify the format of timestamps in the default
                              output mode [rfc3339, rfc3339nano, rfc822z,
                              rfc1123z, stampmicro, stampmilli, stampnano,
                              unixdate]
      --cpuprofile=""         Specify the location for writing a CPU profile.
      --memprofile=""         Specify the location for writing a memory profile.
      --[no-]stdin            Take input logs from stdin
      --addr="http://localhost:3100"  
                              Server address. Can also be set using LOKI_ADDR
                              env var. ($LOKI_ADDR)
      --username=""           Username for HTTP basic auth. Can also be set
                              using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""           Password for HTTP basic auth. Can also be set
                              using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""            Path to the server Certificate Authority.
                              Can also be set using LOKI_CA_CERT_PATH env var.
                              ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify  Server certificate TLS skip verify. Can also
                              be set using LOKI_TLS_SKIP_VERIFY env var.
                              ($LOKI_TLS_SKIP_VERIFY)
      --cert=""               Path to the client certificate. Can also
                              be set using LOKI_CLIENT_CERT_PATH env var.
                              ($LOKI_CLIENT_CERT_PATH)
      --key=""                Path to the client certificate key. Can also
                              be set using LOKI_CLIENT_KEY_PATH env var.
                              ($LOKI_CLIENT_KEY_PATH)
      --org-id=""             adds X-Scope-OrgID to API requests for
                              representing tenant ID. Useful for requesting
                              tenant data when bypassing an auth gateway.
                              Can also be set using LOKI_ORG_ID env var.
                              ($LOKI_ORG_ID)
      --query-tags=""         adds X-Query-Tags http header to API requests.
                              This header value will be part of `metrics.go`
                              statistics. Useful for tracking the query.
                              Can also be set using LOKI_QUERY_TAGS env var.
                              ($LOKI_QUERY_TAGS)
      --[no-]nocache          adds Cache-Control: no-cache http header to API
                              requests. Can also be set using LOKI_NO_CACHE env
                              var. ($LOKI_NO_CACHE)
      --bearer-token=""       adds the Authorization header to API requests for
                              authentication purposes. Can also be set using
                              LOKI_BEARER_TOKEN env var. ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""  adds the Authorization header to API requests
                              for authentication purposes. Can also be
                              set using LOKI_BEARER_TOKEN_FILE env var.
                              ($LOKI_BEARER_TOKEN_FILE)
      --retries=0             How many times to retry each query when
                              getting an error response from Loki. Can also
                              be set using LOKI_CLIENT_RETRIES env var.
                              ($LOKI_CLIENT_RETRIES)
      --min-backoff=0         Minimum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MIN_BACKOFF env var.
                              ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0         Maximum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MAX_BACKOFF env var.
                              ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                              The authorization header used. Can also
                              be set using LOKI_AUTH_HEADER env var.
                              ($LOKI_AUTH_HEADER)
      --proxy-url=""          The http or https proxy to use when
                              making requests. Can also be set
                              using LOKI_HTTP_PROXY_URL env var.
                              ($LOKI_HTTP_PROXY_URL)
      --[no-]compress         Request that Loki compress returned
                              data in transit. Can also be set
                              using LOKI_HTTP_COMPRESSION env var.
                              ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy         Use ProxyFromEnvironment to use net/http
                              ProxyFromEnvironment configuration, eg HTTP_PROXY
                              ($LOKI_ENV_PROXY)
      --since=1h              Lookback window.
      --from=FROM             Start time for deletion (inclusive)
      --to=TO                 End time for deletion (exclusive)
      --max-interval=MAX-INTERVAL  
                              Maximum time interval for delete request

Args:
  <query>  LogQL query to match log lines for deletion (e.g. '{job="app"}')
```

### `delete list` command reference

The output of `logcli help delete list`:

```shell
usage: logcli delete list

List existing log deletion requests.

The "delete list" command lists all existing log deletion requests for the
tenant.

Example:

  logcli delete list
  logcli delete list --output=json


Flags:
      --[no-]help             Show context-sensitive help (also try --help-long
                              and --help-man).
      --[no-]version          Show application version.
  -q, --[no-]quiet            Suppress query metadata
      --[no-]stats            Show query statistics
  -o, --output=default        Specify output mode [default, raw, jsonl].
                              raw suppresses log labels and timestamp.
  -z, --timezone=Local        Specify the timezone to use when formatting output
                              timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                              Specify the format of timestamps in the default
                              output mode [rfc3339, rfc3339nano, rfc822z,
                              rfc1123z, stampmicro, stampmilli, stampnano,
                              unixdate]
      --cpuprofile=""         Specify the location for writing a CPU profile.
      --memprofile=""         Specify the location for writing a memory profile.
      --[no-]stdin            Take input logs from stdin
      --addr="http://localhost:3100"  
                              Server address. Can also be set using LOKI_ADDR
                              env var. ($LOKI_ADDR)
      --username=""           Username for HTTP basic auth. Can also be set
                              using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""           Password for HTTP basic auth. Can also be set
                              using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""            Path to the server Certificate Authority.
                              Can also be set using LOKI_CA_CERT_PATH env var.
                              ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify  Server certificate TLS skip verify. Can also
                              be set using LOKI_TLS_SKIP_VERIFY env var.
                              ($LOKI_TLS_SKIP_VERIFY)
      --cert=""               Path to the client certificate. Can also
                              be set using LOKI_CLIENT_CERT_PATH env var.
                              ($LOKI_CLIENT_CERT_PATH)
      --key=""                Path to the client certificate key. Can also
                              be set using LOKI_CLIENT_KEY_PATH env var.
                              ($LOKI_CLIENT_KEY_PATH)
      --org-id=""             adds X-Scope-OrgID to API requests for
                              representing tenant ID. Useful for requesting
                              tenant data when bypassing an auth gateway.
                              Can also be set using LOKI_ORG_ID env var.
                              ($LOKI_ORG_ID)
      --query-tags=""         adds X-Query-Tags http header to API requests.
                              This header value will be part of `metrics.go`
                              statistics. Useful for tracking the query.
                              Can also be set using LOKI_QUERY_TAGS env var.
                              ($LOKI_QUERY_TAGS)
      --[no-]nocache          adds Cache-Control: no-cache http header to API
                              requests. Can also be set using LOKI_NO_CACHE env
                              var. ($LOKI_NO_CACHE)
      --bearer-token=""       adds the Authorization header to API requests for
                              authentication purposes. Can also be set using
                              LOKI_BEARER_TOKEN env var. ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""  adds the Authorization header to API requests
                              for authentication purposes. Can also be
                              set using LOKI_BEARER_TOKEN_FILE env var.
                              ($LOKI_BEARER_TOKEN_FILE)
      --retries=0             How many times to retry each query when
                              getting an error response from Loki. Can also
                              be set using LOKI_CLIENT_RETRIES env var.
                              ($LOKI_CLIENT_RETRIES)
      --min-backoff=0         Minimum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MIN_BACKOFF env var.
                              ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0         Maximum backoff time between retries. Can also
                              be set using LOKI_CLIENT_MAX_BACKOFF env var.
                              ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                              The authorization header used. Can also
                              be set using LOKI_AUTH_HEADER env var.
                              ($LOKI_AUTH_HEADER)
      --proxy-url=""          The http or https proxy to use when
                              making requests. Can also be set
                              using LOKI_HTTP_PROXY_URL env var.
                              ($LOKI_HTTP_PROXY_URL)
      --[no-]compress         Request that Loki compress returned
                              data in transit. Can also be set
                              using LOKI_HTTP_COMPRESSION env var.
                              ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy         Use ProxyFromEnvironment to use net/http
                              ProxyFromEnvironment configuration, eg HTTP_PROXY
                              ($LOKI_ENV_PROXY)
```

### `delete cancel` command reference

The output of `logcli help delete cancel`:

```shell
usage: logcli delete cancel --request-id=REQUEST-ID [<flags>]

Cancel a log deletion request.

The "delete cancel" command cancels an existing log deletion request by ID.

Example:

  logcli delete cancel --request-id="abc123"
  logcli delete cancel --request-id="abc123" --force


Flags:
      --[no-]help              Show context-sensitive help (also try --help-long
                               and --help-man).
      --[no-]version           Show application version.
  -q, --[no-]quiet             Suppress query metadata
      --[no-]stats             Show query statistics
  -o, --output=default         Specify output mode [default, raw, jsonl].
                               raw suppresses log labels and timestamp.
  -z, --timezone=Local         Specify the timezone to use when formatting
                               output timestamps [Local, UTC]
      --output-timestamp-format=rfc3339  
                               Specify the format of timestamps in the default
                               output mode [rfc3339, rfc3339nano, rfc822z,
                               rfc1123z, stampmicro, stampmilli, stampnano,
                               unixdate]
      --cpuprofile=""          Specify the location for writing a CPU profile.
      --memprofile=""          Specify the location for writing a memory
                               profile.
      --[no-]stdin             Take input logs from stdin
      --addr="http://localhost:3100"  
                               Server address. Can also be set using LOKI_ADDR
                               env var. ($LOKI_ADDR)
      --username=""            Username for HTTP basic auth. Can also be set
                               using LOKI_USERNAME env var. ($LOKI_USERNAME)
      --password=""            Password for HTTP basic auth. Can also be set
                               using LOKI_PASSWORD env var. ($LOKI_PASSWORD)
      --ca-cert=""             Path to the server Certificate Authority.
                               Can also be set using LOKI_CA_CERT_PATH env var.
                               ($LOKI_CA_CERT_PATH)
      --[no-]tls-skip-verify   Server certificate TLS skip verify. Can also
                               be set using LOKI_TLS_SKIP_VERIFY env var.
                               ($LOKI_TLS_SKIP_VERIFY)
      --cert=""                Path to the client certificate. Can also
                               be set using LOKI_CLIENT_CERT_PATH env var.
                               ($LOKI_CLIENT_CERT_PATH)
      --key=""                 Path to the client certificate key. Can also
                               be set using LOKI_CLIENT_KEY_PATH env var.
                               ($LOKI_CLIENT_KEY_PATH)
      --org-id=""              adds X-Scope-OrgID to API requests for
                               representing tenant ID. Useful for requesting
                               tenant data when bypassing an auth gateway.
                               Can also be set using LOKI_ORG_ID env var.
                               ($LOKI_ORG_ID)
      --query-tags=""          adds X-Query-Tags http header to API requests.
                               This header value will be part of `metrics.go`
                               statistics. Useful for tracking the query.
                               Can also be set using LOKI_QUERY_TAGS env var.
                               ($LOKI_QUERY_TAGS)
      --[no-]nocache           adds Cache-Control: no-cache http header to API
                               requests. Can also be set using LOKI_NO_CACHE env
                               var. ($LOKI_NO_CACHE)
      --bearer-token=""        adds the Authorization header to API requests for
                               authentication purposes. Can also be set using
                               LOKI_BEARER_TOKEN env var. ($LOKI_BEARER_TOKEN)
      --bearer-token-file=""   adds the Authorization header to API requests
                               for authentication purposes. Can also be
                               set using LOKI_BEARER_TOKEN_FILE env var.
                               ($LOKI_BEARER_TOKEN_FILE)
      --retries=0              How many times to retry each query when
                               getting an error response from Loki. Can also
                               be set using LOKI_CLIENT_RETRIES env var.
                               ($LOKI_CLIENT_RETRIES)
      --min-backoff=0          Minimum backoff time between retries. Can also
                               be set using LOKI_CLIENT_MIN_BACKOFF env var.
                               ($LOKI_CLIENT_MIN_BACKOFF)
      --max-backoff=0          Maximum backoff time between retries. Can also
                               be set using LOKI_CLIENT_MAX_BACKOFF env var.
                               ($LOKI_CLIENT_MAX_BACKOFF)
      --auth-header="Authorization"  
                               The authorization header used. Can also
                               be set using LOKI_AUTH_HEADER env var.
                               ($LOKI_AUTH_HEADER)
      --proxy-url=""           The http or https proxy to use when
                               making requests. Can also be set
                               using LOKI_HTTP_PROXY_URL env var.
                               ($LOKI_HTTP_PROXY_URL)
      --[no-]compress          Request that Loki compress returned
                               data in transit. Can also be set
                               using LOKI_HTTP_COMPRESSION env var.
                               ($LOKI_HTTP_COMPRESSION)
      --[no-]envproxy          Use ProxyFromEnvironment to use net/http
                               ProxyFromEnvironment configuration, eg HTTP_PROXY
                               ($LOKI_ENV_PROXY)
      --request-id=REQUEST-ID  ID of the delete request to cancel
      --[no-]force             Force cancellation of partially completed request
```

### Use `--stdin` to query locally

You can use the logcli `–stdin` argument to run a command against a log file on your local machine, instead of a Loki instance. This lets you use LogQL to query a local log file without having to load the file into Loki, for example if you have downloaded a log file and want to query it outside of Loki.

If you have log files in your local machine, and just want to run some LogQL queries against those log files, `--stdin` flag can help.

You may use `stdin` flag to do the following:

- Use as a quick way to test or validate a LogQL expression against some log data.
- Learn the basics of LogQL with just local log files and the `logcli` tool (without needing to set up Loki servers, Grafana, etc.).
- Enable troubleshooting by letting you run queries without accessing a Loki instance.
- Use LogQL to parse and extract data from a local log file without ingesting the data into Loki.
- Enable discussion on public forums, for example submitting questions and answers, and sharing LogQL expressions.

#### Notes on `stdin` usage

1. The `--limit` flag doesn't have any meaning when using `--stdin` (use pager like `less` for that).
1. Be aware there are no **labels** when using `--stdin`. So the stream selector in the query is optional, for example, just `|="timeout"|logfmt|level="error"` is same as `{foo="bar"}|="timeout|logfmt|level="error"`.

{{< admonition type="note" >}}
Currently `stdin` doesn't support any type of metric queries.
{{< /admonition >}}

#### `stdin` examples

- Line filter - `cat mylog.log | logcli --stdin query '|="too many open connections"'`
- Label matcher - `echo 'msg="timeout happened" level="warning"' | logcli --stdin query '|logfmt|level="warning"'`
- Different parsers (logfmt, json, pattern, regexp) - `cat mylog.log | logcli --stdin query '|pattern <ip> - - <_> "<method> <uri> <_>" <status> <size> <_> "<agent>" <_>'`
- Line formatters - `cat mylog.log | logcli --stdin query '|logfmt|line_format "{{.query}} {{.duration}}"'`

## Batching

logcli sends queries to Loki in such a way that query results arrive in batches.

The `--limit` option for a `logcli query` command limits the total number of log lines that will be returned for a single query.
When not set, `--limit` defaults to 30.
The limit protects the user from overwhelming Loki in cases where the specified query would have returned a large number of log lines.
The limit also protects the user from unexpectedly large responses.

Larger result sets can be batched for easier consumption. Use the `--batch` option to control the number of log line results that are returned in each batch.
When not set, `--batch` defaults to 1000.

Setting a `--limit` value larger than the `--batch` value will cause the
requests from logcli to Loki to be batched.

When you run a query in Loki, it will return up to a certain number of log lines. By default, this limit is 5000 lines. You can configure this server limit with the `limits_config.max_entries_limit_per_query` in Loki's configuration.

Batching lets you query for a results set that is larger than this server-side limit, as long as the `--batch` value is less than the server limit.

Query metadata is output to `stderr` for each batch.
To suppress the output of the query metadata, set the `--quiet` option on the `logcli query` command line.

## logcli example queries

Here are some examples of logcli.

Find all values for a label.

```bash
logcli labels job
```

```bash
https://logs-dev-ops-tools1.grafana.net/api/prom/label/job/values
loki-ops/consul
loki-ops/loki-gw
```

Print all labels and their unique values. This command is especially useful for finding [high-cardinality labels](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/#cardinality) in the index.

```bash
logcli series '{cluster="vinson"}' --analyze-labels
```

```bash
2024/10/31 13:46:25 https://logs-prod-008.grafana.net/loki/api/v1/series?end=1730382385746344416&match=%7Bcluster%3D%22vinson%22%7D&start=1730378785746344416
Total Streams:  10
Unique Labels:  10

Label Name       Unique Values  Found In Streams
service_name        8          10
pod                 7          7
job                 6          10
app_kubernetes_io_name  6          6
container           5          7
namespace           3          10
stream              2          7
flags               1          7
instance            1          3
cluster             1          10
```

Get all logs for a given stream

```bash
logcli query '{job="loki-ops/consul"}'
```

```bash
https://logs-dev-ops-tools1.grafana.net/api/prom/query?query=%7Bjob%3D%22loki-ops%2Fconsul%22%7D&limit=30&start=1529928228&end=1529931828&direction=backward&regexp=
Common labels: {job="loki-ops/consul", namespace="loki-ops"}
2018-06-25T12:52:09Z {instance="consul-8576459955-pl75w"} 2018/06/25 12:52:09 [INFO] raft: Snapshot to 475409 complete
2018-06-25T12:52:09Z {instance="consul-8576459955-pl75w"} 2018/06/25 12:52:09 [INFO] raft: Compacting logs from 456973 to 465169
```

Print all log streams for the given stream selector. This example shows all known label combinations that match your query.

```bash
logcli series -q --match='{namespace="loki",container_name="loki"}'
```

```bash
{app="loki", container_name="loki", controller_revision_hash="loki-57c9df47f4", filename="/var/log/pods/loki_loki-0_8ed03ded-bacb-4b13-a6fe-53a445a15887/loki/0.log", instance="loki-0", job="loki/loki", name="loki", namespace="loki", release="loki", statefulset_kubernetes_io_pod_name="loki-0", stream="stderr"}
```

Delete all logs for a specific job within a time range:

```bash
logcli delete create '{job="app"}' --from="2023-01-01T00:00:00Z" --to="2023-01-02T00:00:00Z"
```

List all deletion requests:

```bash
logcli delete list
```

Cancel a specific deletion request:

```bash
logcli delete cancel --request-id="abc123"
```

## Troubleshoot logcli

Make sure that the version of Logcli you are using matches your Loki version.
You can check your logcli version with the following command:

```bash
logcli –version
```

If you experience timeouts, you can update the following setting in your `logcli-config.yaml` file.

```yaml
limits_config:
  query_timeout: 10m
```

# LogCLI tutorial

<!-- INTERACTIVE page intro.md START -->

# LogCLI tutorial

This [LogCLI](https://grafana.com/docs/loki/<LOKI_VERSION>/query/logcli/) tutorial will walk you through the following concepts:

* Querying logs
* Meta Queries against your Loki instance
* Queries against static log files

<!-- INTERACTIVE ignore START -->

## Prerequisites

Before you begin, you need to have the following:
* Docker
* Docker-compose
* LogCLI installed on your machine (see [LogCLI installation](https://grafana.com/docs/loki/<LOKI_VERSION>/query/logcli/getting-started#installation))

{{< admonition type="tip" >}}
Alternatively, you can try out this example in our interactive learning environment: [LogCLI Tutorial](https://killercoda.com/grafana-labs/course/loki/logcli-tutorial).

It's a fully configured environment with all the dependencies already installed.

![Interactive](/media/docs/loki/loki-ile.svg)

Provide feedback, report bugs, and raise issues in the [Grafana Killercoda repository](https://github.com/grafana/killercoda).
{{< /admonition >}}

<!-- INTERACTIVE ignore END -->

## Scenario

You are a site manager for a new logistics company. The company uses structured logs to keep track of every shipment sent and received. The payload format looks like this:

```json
{"timestamp": "2024-11-22T13:22:56.377884", "state": "New York", "city": "Buffalo", "package_id": "PKG34245", "package_type": "Documents", "package_size": "Medium", "package_status": "error", "note": "Out for delivery", "sender": {"name": "Sender27", "address": "144 Elm St, Buffalo, New York"}, "receiver": {"name": "Receiver4", "address": "260 Cedar Blvd, New York City, New York"}}
```

The logs are processed from Grafana Alloy to extract labels and structured metadata before they're stored in Loki. You have been tasked with monitoring the logs using the LogCLI and build a report on the overall health of the shipments.

<!-- INTERACTIVE page intro.md END -->

<!-- INTERACTIVE page step1.md START -->

## Setup

To get started, we need to clone the [Alloy Scenario](https://github.com/grafana/alloy-scenarios) repository and start the mail-house example:

1. Clone the repository:
    ```bash
    git clone https://github.com/grafana/alloy-scenarios.git
    ```
1. Start the mail-house example:
    ```bash
    docker compose -f alloy-scenarios/mail-house/docker-compose.yml up -d
    ```

This will start the mail-house example and expose the Loki instance at [`http://localhost:3100`](http://localhost:3100). We have also included a Grafana instance to verify the LogCLI results which can be accessed at [http://localhost:3000](http://localhost:3000).

### Connecting LogCLI to Loki

To connect LogCLI to the Loki instance, you need to set the `LOKI_ADDR` environment variable:

{{< admonition type="tip" >}}

If you are running this example against your own Loki instance and have configured authentication, you will also need to set the `LOKI_USERNAME` and `LOKI_PASSWORD` environment variables.

{{< /admonition >}}

```bash
export LOKI_ADDR=http://localhost:3100
```

Now let's verify the connection by running the following command:

```bash
logcli labels
```

This should return an output similar to the following:

```console
http://localhost:3100/loki/api/v1/labels?end=1732282703894072000&start=1732279103894072000
package_size
service_name
state
```

This confirms that LogCLI is connected to the Loki instance and we now know that the logs contain the following labels: `package_size`, `service_name`, and `state`. Let's run some queries against Loki to better understand our package logistics.

<!-- INTERACTIVE page step1.md END -->

<!-- INTERACTIVE page step2.md START -->

## Querying logs

As part of our role within the logistics company, we need to build a report on the overall health of the shipments. Unfortunately, we only have access to a console and cannot use Grafana to visualize the data. We can use LogCLI to query the logs and build the report.

### Find all critical packages

To find all critical packages in the last hour (the default lookback time), we can run the following query:

```bash
logcli query '{service_name="Delivery World"} | package_status="critical"'
```

This will return all logs where the `service_name` is `Delivery World` and the `package_status` is `critical`. The output will look similar to the following:

```console
http://localhost:3100/loki/api/v1/query_range?direction=BACKWARD&end=1732617594381712000&limit=30&query=%7Bservice_name%3D%22Delivery+World%22%7D+%7C+package_status%3D%22critical%22&start=1732613994381712000
Common labels: {package_status="critical", service_name="Delivery World"}
2024-11-26T10:39:52Z {package_id="PKG79755", package_size="Small", state="Texas"}       {"timestamp": "2024-11-26T10:39:52.521602Z", "state": "Texas", "city": "Dallas", "package_id": "PKG79755", "package_type": "Clothing", "package_size": "Small", "package_status": "critical", "note": "In transit", "sender": {"name": "Sender38", "address": "906 Maple Ave, Dallas, Texas"}, "receiver": {"name": "Receiver41", "address": "455 Pine Rd, Dallas, Texas"}}
2024-11-26T10:39:50Z {package_id="PKG34018", package_size="Large", state="Illinois"}    {"timestamp": "2024-11-26T10:39:50.510841Z", "state": "Illinois", "city": "Chicago", "package_id": "PKG34018", "package_type": "Clothing", "package_size": "Large", "package_status": "critical", "note": "Delayed due to weather", "sender": {"name": "Sender22", "address": "758 Elm St, Chicago, Illinois"}, "receiver": {"name": "Receiver10", "address": "441 Cedar Blvd, Naperville, Illinois"}}
```

Lets suppose we want to look back for the last 24 hours, we can use the `--since` flag to specify the time range:

```bash
logcli query --since 24h '{service_name="Delivery World"} | package_status="critical"' 
```

This will query all logs for the `package_status` `critical` in the last 24 hours. However it will not return all of the logs, but only the first 30 logs. We can use the `--limit` flag to specify the number of logs to return:

```bash
logcli query --since 24h --limit 100 '{service_name="Delivery World"} | package_status="critical"' 
```

### Metric queries

We can also use LogCLI to query logs based on metrics. For instance as part of the site report we want to count the total number of packages sent from California in the last 24 hours in 1 hour intervals. We can use the following query:

```bash
logcli query --since 24h 'sum(count_over_time({state="California"}[1h]))'
```

This will return a JSON object containing a list of timestamps (Unix format) and the number of packages sent from California in 1 hour intervals. Since we summing the count of logs over time, we will see the total number of logs steadily increase over time. The output will look similar to the following:

```console
[
  {
    "metric": {},
    "values": [
      [
        1733913765,
        "46"
      ],
      [
        1733914110,
        "114"
      ],
      [
        1733914455,
        "179"
      ],
      [
        1733914800,
        "250"
      ],
      [
        1733915145,
        "318"
      ],
      [
        1733915490,
        "392"
      ],
      [
        1733915835,
        "396"
      ]
    ]
  }
]
```

We can take this a step further and filter the logs based on the `package_type` label. For instance, we can count the number of documents sent from California in the last 24 hours in 1 hour intervals:
  
```bash
logcli query --since 24h  'sum(count_over_time({state="California"}| json | package_type= "Documents" [1h]))'
```

This will return a similar JSON object above but will only show a trend of the number of documents sent from California in 1 hour intervals.

### Instant metric queries

Instant metric queries are a subset of metric queries that return the value of the metric at a specific point in time. This can be useful for quickly understanding an aggregate state of the stored logs. 

For instance, we can use the following query to get the number of packages sent from California in the last 5 minutes:

```bash
logcli instant-query 'sum(count_over_time({state="California"}[5m]))'
```

This will return a result similar to the following:

```console
[
  {
    "metric": {},
    "value": [
      1732702998.725,
      "58"
    ]
  }
]
```

### Writing query results to a file

Another useful feature of LogCLI is the ability to write the query results to a file. This can be useful for downloading the results of our inventory report:

First we need to create a directory to store the logs:
```bash
mkdir -p ./inventory
```

Next we can run the following query to write the logs to the `./inventory` directory:

```bash
  logcli query \
     --timezone=UTC \
     --output=jsonl \
     --parallel-duration="12h" \
     --parallel-max-workers="4" \
     --part-path-prefix="./inventory/inv" \
     --since=24h \
     '{service_name="Delivery World"}'
```

This will write all logs for the `service_name` `Delivery World` in the last 24 hours to the `./inventory` directory. The logs will be split into two files, each containing 12 hours of logs. Note that we do not need to specify `--limit` as this is overridden by the `--parallel-duration` flag. 

<!-- INTERACTIVE page step2.md END -->

<!-- INTERACTIVE page step3.md START -->

## Meta queries

As site managers, it's essential to maintain good data hygiene and ensure Loki operates efficiently. Understanding the labels and log volume in your logs plays a key role in this process. Beyond querying logs, LogCLI also supports meta queries on your Loki instance. Meta queries don't return log data but provide insights into the structure of your logs and the performance of your queries. The following examples demonstrate some of the core meta queries we run internally to better understand how a Loki instance is performing.

### Checking series cardinality

One of the most important aspects of keeping Loki healthy is to monitor the series cardinality. This is the number of unique series in your logs. A high series cardinality can lead to performance issues and increased storage costs. We can use LogCLI to check the series cardinality of our logs.

To start let's print how many unique series we have in our logs:

```bash
logcli series '{}'
```

This will return a list of all the unique series in our logs. The output will look similar to the following:

```console
{package_size="Small", service_name="Delivery World", state="Florida"}
{package_size="Medium", service_name="Delivery World", state="Florida"}
{package_size="Small", service_name="Delivery World", state="California"}
{package_size="Large", service_name="Delivery World", state="New York"}
{package_size="Small", service_name="Delivery World", state="Illinois"}
{package_size="Large", service_name="Delivery World", state="Florida"}
{package_size="Medium", service_name="Delivery World", state="Illinois"}
{package_size="Large", service_name="Delivery World", state="Texas"}
{package_size="Medium", service_name="Delivery World", state="California"}
{package_size="Medium", service_name="Delivery World", state="Texas"}
{package_size="Small", service_name="Delivery World", state="Texas"}
{package_size="Large", service_name="Delivery World", state="Illinois"}
{package_size="Small", service_name="Delivery World", state="New York"}
{package_size="Medium", service_name="Delivery World", state="New York"}
{package_size="Large", service_name="Delivery World", state="California"}
```

We can further improve this query by adding `--analyze-labels`:
```bash
logcli series '{}' --analyze-labels
```

This will return a summary of the unique values for each label in our logs. The output will look similar to the following:

```console
Label Name    Unique Values  Found In Streams
state         5              15
package_size  3              15
service_name  1              15
```

### Detected fields

Another useful feature of LogCLI is the ability to detect fields in your logs. This can be useful for understanding the structure of your logs and the keys that are present. This will let us detect keys which could be promoted to labels or to structured metadata. 

```bash
logcli detected-fields --since 24h '{service_name="Delivery World"}'
```

This will return a list of all the keys detected in our logs. The output will look similar to the following:

```console
label: city                   type: string  cardinality: 15
label: detected_level         type: string  cardinality: 3
label: note                   type: string  cardinality: 7
label: package_id             type: string  cardinality: 994
label: package_size_extracted type: string  cardinality: 3
label: package_status         type: string  cardinality: 4
label: package_type           type: string  cardinality: 5
label: receiver_address       type: string  cardinality: 991
label: receiver_name          type: string  cardinality: 100
label: sender_address         type: string  cardinality: 991
label: sender_name            type: string  cardinality: 100
label: state_extracted        type: string  cardinality: 5
label: timestamp              type: string  cardinality: 1000
```

You can now see why we opted to keep `package_id` in structured metadata and `package_size` as a label. Package ID has a high cardinality and is unique to each log entry, making it a good candidate for structured metadata since we potentially may need to query for it directly. Package size, on the other hand, has a low cardinality, making it a good candidate for a label.


### Checking query performance

Another important aspect of keeping Loki healthy is to monitor the query performance. We can use LogCLI to check the query performance of our logs.

{{< admonition type="note" >}}
The LogCLI can only return statistics for queries that touch object storage. In this example we force the Loki ingesters to flush chunks every 5 minutes which isn't recommended for production use. When running this demo if you don't see any statistics returned, try running the command again after a few minutes.
{{< /admonition >}}

To start lets print the query performance of our logs:

```bash
logcli stats --since 24h '{service_name="Delivery World"}'
```

This will provide a JSON object containing statistics on the amount of data queried. The output will look similar to the following:

```console
http://localhost:3100/loki/api/v1/index/stats?end=1732639430272850000&query=%7Bservice_name%3D%22Delivery+World%22%7D&start=1732553030272850000
{
  bytes: 12MB
  chunks: 63
  streams: 15
  entries: 29529
}
```

This will return the total number of bytes queried, the number of chunks queried, the number of streams queried, and the number of entries queried. If we narrow down the query by specifying a secondary label, we can see the performance of the query:

```bash
logcli stats --since 24h '{service_name="Delivery World", package_size="Large"}'
```

This will return the statistics for the logs where the `service_name` is `Delivery World` and the `package_size` is `Large`. The output will look similar to the following:

```console
{
  bytes: 4.2MB
  chunks: 22
  streams: 5
  entries: 10198
}
```

As you can see, we touched far fewer streams and entries by narrowing down the query.


### Checking the log volume

We may also want to check the log volume in our logs. This can be useful for understanding the amount of data being ingested into Loki. We can use LogCLI to check the log volume in our logs.

```bash
logcli volume --since 24h '{service_name="Delivery World"}'
```

This returns the total number of logs ingested for the label `Delivery World` in the last 24 hours. The output will look similar to the following:

```console
[
  {
    "metric": {
      "service_name": "Delivery World"
    },
    "value": [
      1732640292.354,
      "11669299"
    ]
  }
]
```

The result includes the timestamp and the total number of logs ingested.

We can also return the log volume over time by using `volume_range`:

```bash
logcli volume_range --since 24h --step=1h '{service_name="Delivery World"}'
```

This will provide a JSON object containing the log volume for the label `Delivery World` in the last 24 hours. `--step` will aggregate the log volume into 1 hour buckets. Note that if there are no logs for a specific hour, the log volume for that hour will not return a value.

We can even aggregate the log volume into buckets based on a specific labels value:

```bash
logcli volume_range --since 24h --step=1h --targetLabels='state' '{service_name="Delivery World"}' 
```
This will provide a similar JSON object but will aggregate the log volume into buckets based on the `state` label value.

<!-- INTERACTIVE page step3.md END -->

<!-- INTERACTIVE page step4.md START -->

## Queries against static log files

In addition to querying logs from Loki, LogCLI also supports querying static log files. This can be useful for querying logs that are not stored in Loki. Earlier in the tutorial we stored the logs in the `./inventory` directory. Lets run a similar query but pipe it into a log file:

```bash
  logcli query \
     --timezone=UTC \
     --parallel-duration="12h" \
     --parallel-max-workers="4" \
     --part-path-prefix="./inventory/inv" \
     --since=24h \
     --merge-parts \
     --output=raw \
     '{service_name="Delivery World"}' > ./inventory/complete.log
```

Next lets run a query against the static log file:

```bash
cat ./inventory/complete.log |  logcli --stdin query '{service_name="Delivery World"} | json | package_status="critical"'
```

Note that since we are querying a static log file, labels are not automatically detected:
* `{service_name="Delivery World"}` is optional in this case but is recommended for clarity.
* `json` is required to parse the log file as JSON. This lets us extract the `package_status` field.

For example, suppose we try to query the log file without the `json` filter:

```bash
cat ./inventory/complete.log | logcli --stdin query '{service_name="Delivery World"} | package_status="critical"'
```

This will return no results as the `package_status` field is not detected.

<!-- INTERACTIVE page step4.md END -->

<!-- INTERACTIVE page finish.md START -->

## Conclusion

In this tutorial as site manager for a logistics company, we have successfully used LogCLI to query logs and build a report on the overall health of the shipments. We have also used meta queries to better understand our data cleanliness and query performance. The LogCLI is a powerful tool for understanding your logs and how they are stored in Loki, as you continue to scale your solution remember to keep LogCLI in mind to monitor cardinality and query performance.

<!-- INTERACTIVE page finish.md END -->

# Reference

# Grafana Loki configuration parameters

Grafana Loki is configured in a YAML file (usually referred to as `loki.yaml` )
which contains information on the Loki server and its individual components,
depending on which mode Loki is launched in.

Configuration examples can be found in the [Configuration Examples](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/examples/configuration-examples/) document.

<!-- The shared `configuration.md` file is generated from `/docs/templates/configuration.template`. To make changes to the included content, modify the template file and run `make doc` from root directory to regenerate the shared file. -->

{{< docs/shared lookup="configuration.md" source="loki" version="<LOKI_VERSION>" >}}

# Loki HTTP API

Loki exposes an HTTP API for pushing, querying, and tailing log data, as well
as for viewing and managing cluster information.

## Authentication

Authentication and authorization are not part of the Loki API itself. How you authenticate depends on how Loki is deployed:

- **Self-hosted, single-tenant Loki**: Loki doesn't enforce authentication. If you need to restrict access, put a reverse proxy (such as NGINX or Envoy) in front of Loki to handle authentication separately.
- **Self-hosted, multi-tenant Loki**: Set the `X-Scope-OrgID` header on each request to identify the tenant. For details and examples, refer to [Multi-tenant queries](#multi-tenant-queries).
- **Grafana Enterprise Logs (GEL)**: Use HTTP Basic Authentication, with the tenant name as the username and an access policy token as the password. For an example, refer to [Multi-tenant queries](#multi-tenant-queries).
- **Grafana Cloud**: Use HTTP Basic Authentication, with your Loki instance ID (the **User** value from the Cloud Portal) as the username and a [Cloud Access Policy token](https://grafana.com/docs/grafana-cloud/security-and-account-management/authentication-and-permissions/access-policies/) as the password:

  ```bash
  curl -u "<LOKI_INSTANCE_ID>:<CLOUD_ACCESS_POLICY_TOKEN>" \
    -G -s "<LOKI_URL>/loki/api/v1/query" \
    --data-urlencode 'query=sum(rate({job="varlogs"}[10m])) by (level)' | jq
  ```

  For step-by-step instructions on finding your Loki instance ID, URL, and creating an access policy token, refer to [Authenticate to query Cloud Logs](https://grafana.com/docs/grafana-cloud/send-data/logs/authenticate-to-query-logs/) in the Grafana Cloud documentation.

## Endpoints

### Ingest endpoints

These endpoints are exposed by the `distributor`, `write`, and `all` components:

- [`POST /loki/api/v1/push`](#ingest-logs)
- [`POST /otlp/v1/logs`](#ingest-logs-using-otlp)

A [list of clients](../../send-data/) can be found in the clients documentation.

### Query endpoints

{{< admonition type="note" >}}
Requests sent to the query endpoints must use valid LogQL syntax. For more information, see the [LogQL](../../query/) section of the documentation.
{{< /admonition >}}

{{< admonition type="tip" >}}
For Python examples of these endpoints, see [Query Loki with Python](../python-client-examples/).
{{< /admonition >}}

These HTTP endpoints are exposed by the `querier`, `query-frontend`, `read`, and `all` components:

- [`GET /loki/api/v1/query`](#query-logs-at-a-single-point-in-time)
- [`GET /loki/api/v1/query_range`](#query-logs-within-a-range-of-time)
- [`GET /loki/api/v1/labels`](#query-labels)
- [`GET /loki/api/v1/label/<name>/values`](#query-label-values)
- [`GET /loki/api/v1/series`](#query-streams)
- [`GET /loki/api/v1/index/stats`](#query-log-statistics)
- [`GET /loki/api/v1/index/volume`](#query-log-volume)
- [`GET /loki/api/v1/index/volume_range`](#query-log-volume)
- [`GET /loki/api/v1/patterns`](#patterns-detection)
- [`GET /loki/api/v1/tail`](#stream-logs)

### Status endpoints

These HTTP endpoints are exposed by all components and return the status of the component:

- [`GET /ready`](#readiness-probe)
- [`GET /log_level`](#change-log-level)
- [`GET /metrics`](#prometheus-metrics)
- [`GET /config`](#show-current-configuration)
- [`GET /loki/api/v1/exposed-config`](#show-exposed-configuration)
- [`GET /services`](#list-running-services)
- [`GET /loki/api/v1/status/buildinfo`](#show-build-information)

### Ring endpoints

These HTTP endpoints are exposed by their respective component that is part of the ring URL prefix:

- [`GET /distributor/ring`](#distributor-ring-status)
- [`GET /indexgateway/ring`](#index-gateway-ring-status)
- [`GET /ruler/ring`](#ruler-ring-status)
- [`GET /compactor/ring`](#compactor-ring-status)

### Flush/shutdown endpoints

These HTTP endpoints are exposed by the `ingester`, `write`, and `all` components for flushing chunks and/or shutting down.

- [`POST /flush`](#flush-in-memory-chunks-to-backing-store)
- [`POST /flush/tenant`](#flush-in-memory-chunks-for-a-tenant-experimental)
- [`POST /ingester/prepare_shutdown`](#prepare-ingester-shutdown)
- [`POST /ingester/shutdown`](#flush-in-memory-chunks-and-shut-down)

### Index gateway endpoints

These HTTP endpoints are exposed by the `index-gateway`, `backend`, and `all` components:

- [`PUT /sync-indexes`](#sync-indexes-from-object-storage)
- [`GET /sync-indexes`](#sync-indexes-from-object-storage)

### Rule endpoints

These HTTP endpoints are exposed by the `ruler` component:

- [`GET /loki/api/v1/rules`](#list-rule-groups)
- [`GET /loki/api/v1/rules/{namespace}`](#get-rule-groups-by-namespace)
- [`GET /loki/api/v1/rules/{namespace}/{groupName}`](#get-rule-group)
- [`POST /loki/api/v1/rules/{namespace}`](#set-rule-group)
- [`DELETE /loki/api/v1/rules/{namespace}/{groupName}`](#delete-rule-group)
- [`DELETE /loki/api/v1/rules/{namespace}`](#delete-namespace)
- [`GET /api/prom/rules`](#list-rule-groups)
- [`GET /api/prom/rules/{namespace}`](#get-rule-groups-by-namespace)
- [`GET /api/prom/rules/{namespace}/{groupName}`](#get-rule-group)
- [`POST /api/prom/rules/{namespace}`](#set-rule-group)
- [`DELETE /api/prom/rules/{namespace}/{groupName}`](#delete-rule-group)
- [`DELETE /api/prom/rules/{namespace}`](#delete-namespace)
- [`GET /prometheus/api/v1/rules`](#list-rules)
- [`GET /prometheus/api/v1/alerts`](#list-alerts)

API endpoints starting with `/api/prom` are [Prometheus API-compatible](https://prometheus.io/docs/prometheus/latest/querying/api/) and the result formats can be used interchangeably.

### Log deletion endpoints

These endpoints are exposed by the `compactor`, `backend`, and `all` components:

- [`POST /loki/api/v1/delete`](#request-log-deletion)
- [`GET /loki/api/v1/delete`](#list-log-deletion-requests)
- [`DELETE /loki/api/v1/delete`](#request-cancellation-of-a-delete-request)

### Other endpoints

These HTTP endpoints are exposed by all individual components:

- [`GET /loki/api/v1/format_query`](#format-a-logql-query)

### Deprecated endpoints

{{< admonition type="note" >}}
The following endpoints are deprecated.While they still exist and work, they should not be used for new deployments.
Existing deployments should upgrade to use the supported endpoints.
{{< /admonition >}}

| Deprecated | Replacement |
| ---------- | ----------- |
| `POST /api/prom/push` | [`POST /loki/api/v1/push`](#ingest-logs) |
| `GET /api/prom/tail` | [`GET /loki/api/v1/tail`](#stream-logs) |
| `GET /api/prom/query` | [`GET /loki/api/v1/query`](#query-logs-at-a-single-point-in-time) |
| `GET /api/prom/label` | [`GET /loki/api/v1/labels`](#query-labels) |
| `GET /api/prom/label/<name>/values` | [`GET /loki/api/v1/label/<name>/values`](#query-label-values) |
| `GET /api/prom/series` | [`GET /loki/api/v1/series`](#query-streams) |

## Format

### Matrix, vector, and stream

Some Loki API endpoints return a result of a matrix, a vector, or a stream:

- **Matrix**: a table of values where each row represents a different label set
and the columns are each sample values for that row over the queried time.
Matrix types are only returned when running a query that computes some value.

- **Instant Vector**: denoted in the type as just `vector`, an Instant Vector
represents the latest value of a calculation for a given labelset. Instant
Vectors are only returned when doing a query against a single point in
time.

- **Stream**: a Stream is a set of all values (logs) for a given label set over the
queried time range. Streams are the only type that will result in log lines
being returned.

### Timestamps

The API accepts several formats for timestamps:

- All epoch values will be interpreted as a Unix timestamp in nanoseconds.
- A floating point number is a Unix timestamp with fractions of a second.
- A string in `RFC3339` and `RFC3339Nano` format, as supported by Go's [time](https://pkg.go.dev/time) package.

{{< admonition type="note" >}}
When using `/api/v1/push`, you must send the timestamp as a string and not a number, otherwise the endpoint will return a 400 error.
{{< /admonition >}}

### Statistics

Query endpoints such as `/loki/api/v1/query` and `/loki/api/v1/query_range` return a set of statistics about the query execution. Those statistics allow users to understand the amount of data processed and at which speed.

The example below show all possible statistics returned with their respective description.

```json
{
  "status": "success",
  "data": {
    "resultType": "streams",
    "result": [],
    "stats": {
      "ingester": {
        "compressedBytes": 0, // Total bytes of compressed chunks (blocks) processed by ingesters
        "decompressedBytes": 0, // Total bytes decompressed and processed by ingesters
        "decompressedLines": 0, // Total lines decompressed and processed by ingesters
        "headChunkBytes": 0, // Total bytes read from ingesters head chunks
        "headChunkLines": 0, // Total lines read from ingesters head chunks
        "totalBatches": 0, // Total batches sent by ingesters
        "totalChunksMatched": 0, // Total chunks matched by ingesters
        "totalDuplicates": 0, // Total of duplicates found by ingesters
        "totalLinesSent": 0, // Total lines sent by ingesters
        "totalReached": 0 // Amount of ingesters reached.
      },
      "store": {
        "compressedBytes": 0, // Total bytes of compressed chunks (blocks) processed by the store
        "decompressedBytes": 0, // Total bytes decompressed and processed by the store
        "decompressedLines": 0, // Total lines decompressed and processed by the store
        "chunksDownloadTime": 0, // Total time spent downloading chunks in seconds (float)
        "totalChunksRef": 0, // Total chunks found in the index for the current query
        "totalChunksDownloaded": 0, // Total of chunks downloaded
        "totalDuplicates": 0 // Total of duplicates removed from replication
      },
      "summary": {
        "bytesProcessedPerSecond": 0, // Total of bytes processed per second
        "execTime": 0, // Total execution time in seconds (float)
        "linesProcessedPerSecond": 0, // Total lines processed per second
        "queueTime": 0, // Total queue time in seconds (float)
        "totalBytesProcessed": 0, // Total amount of bytes processed overall for this request
        "totalLinesProcessed": 0 // Total amount of lines processed overall for this request
      }
    }
  }
}
```

### Processed-byte response header

Successful `/loki/api/v1/query`, `/loki/api/v1/query_range`, `/loki/api/v1/detected_fields`,
and `/loki/api/v1/detected_field/<name>/values` responses include
`X-Loki-Query-Bytes-Processed`: a non-negative decimal integer matching the final response's
`totalBytesProcessed` statistic, regardless of encoding or compression.

The total includes available statistics; missing contributions count as zero. A value of `0`
can mean no bytes were processed or no statistics were available. Failed queries and older
servers may omit the header; an absent header does not mean zero usage.

## Ingest logs

```bash
POST /loki/api/v1/push
```

`/loki/api/v1/push` is the endpoint used to send log entries to Loki. The default
behavior is for the POST body to be a [Snappy](https://github.com/google/snappy)-compressed [Protocol Buffer](https://github.com/protocolbuffers/protobuf) message:

- [Protocol Buffer definition](https://github.com/grafana/loki/blob/main/pkg/push/push.proto)
- [Go client library](https://github.com/grafana/loki/blob/main/clients/pkg/promtail/client/client.go)

These POST requests require the `Content-Type` HTTP header to be `application/x-protobuf`.

Alternatively, if the `Content-Type` header is set to `application/json`, a JSON post body can be sent in the following format:

```json
{
  "streams": [
    {
      "stream": {
        "label": "value"
      },
      "values": [
          [ "<unix epoch in nanoseconds>", "<log line>" ],
          [ "<unix epoch in nanoseconds>", "<log line>" ]
      ]
    }
  ]
}
```

{{< admonition type="note" >}}
Loki validates JSON push payloads strictly: a field name that doesn't belong to the schema shown above causes the request to fail with HTTP 400 and an error message that contains `found unknown field: <field>`. This also applies to the deprecated `/api/prom/push` endpoint, which uses a different JSON schema (`labels` and `entries` instead of `stream` and `values`). Each endpoint only accepts the fields in its own schema, so sending one endpoint's payload format to the other now returns an error instead of silently dropping data. This validation doesn't apply to the default Protocol Buffer encoding.
{{< /admonition >}}

You can set `Content-Encoding: gzip` request header and post gzipped JSON.

You can optionally attach [structured metadata](../../get-started/labels/structured-metadata/) to each log line by adding a JSON object to the end of the log line array.
The JSON object must be a valid JSON object with string keys and string values. The JSON object should not contain any nested object.
The JSON object must be set immediately after the log line. Here is an example of a log entry with some structured metadata attached:

```json
"values": [
    [ "<unix epoch in nanoseconds>", "<log line>", {"trace_id": "0242ac120002", "user_id": "superUser123"}]
]
```

In microservices mode, `/loki/api/v1/push` is exposed by the distributor.

If [`block_ingestion_until`](/docs/loki/<LOKI_VERSION>/configuration/#limits_config) is configured and push requests are blocked, the endpoint will return the status code configured in `block_ingestion_status_code` (`260` by default)
along with an error message. If the configured status code is `200`, no error message will be returned.

### Examples

The following cURL command pushes a stream with the label "foo=bar2" and a single log line "fizzbuzz" using JSON encoding:

```bash
curl -H "Content-Type: application/json" \
  -s -X POST "http://localhost:3100/loki/api/v1/push" \
  --data-raw '{"streams": [{ "stream": { "foo": "bar2" }, "values": [ [ "1570818238000000000", "fizzbuzz" ] ] }]}'
```

## Ingest logs using OTLP

```bash
POST /otlp/v1/logs
```

`/otlp/v1/logs` lets the OpenTelemetry Collector send logs to Loki using `otlphttp` protocol.

For information on how to configure Loki, refer to the [OTel Collector topic](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/otel/).

<!-- vale Google.Will = NO -->
{{< admonition type="note" >}}
When configuring the OpenTelemetry Collector, you must use `endpoint: http://<LOKI_ADDR>/otlp`, as the collector automatically completes the endpoint.  Entering the full endpoint will generate an error.
{{< /admonition >}}
<!-- vale Google.Will = YES -->

## Query logs at a single point in time

```bash
GET /loki/api/v1/query
```

`/loki/api/v1/query` allows for doing queries against a single point in time.
This type of query is often referred to as an instant query. Instant queries are only used for metric type LogQL queries
and will return a 400 (Bad Request) in case a log type query is provided.
The endpoint accepts the following query parameters in the URL:

- `query`: The [LogQL](../../query/) query to perform. Requests that do not use valid LogQL syntax will return errors.
- `limit`: The max number of entries to return. It defaults to `100`. Only applies to query types which produce a stream (log lines) response.
- `time`: The evaluation time for the query as a nanosecond Unix epoch or another [supported format](#timestamps). Defaults to now.
- `direction`: Determines the sort order of logs. Supported values are `forward` or `backward`. Defaults to `backward`.

In microservices mode, `/loki/api/v1/query` is exposed by the querier and the query frontend.

Response format:

```json
{
  "status": "success",
  "data": {
    "resultType": "vector" | "streams",
    "result": [<vector value>] | [<stream value>],
    "stats" : [<statistics>]
  }
}
```

where `<vector value>` is:

```json
{
  "metric": {
    <label key-value pairs>
  },
  "value": [
    <number: second unix epoch>,
    <string: value>
  ]
}
```

and `<stream value>` is:

```json
{
  "stream": {
    <label key-value pairs>
  },
  "values": [
    [
      <string: nanosecond unix epoch>,
      <string: log line>
    ],
    ...
  ]
}
```

The items in the `values` array are sorted by timestamp.
The most recent item is first when using `direction=backward`.
The oldest item is first when using `direction=forward`.

Parquet can be request as a response format by setting the `Accept` header to `application/vnd.apache.parquet`.

The schema is the following for streams:

| column_name |       column_type        |
|-------------|--------------------------|
| timestamp   | TIMESTAMP WITH TIME ZONE |
| labels      | MAP(VARCHAR, VARCHAR)    |
| line        |VARCHAR                   |

and for metrics:

| column_name |       column_type        |
|-------------|--------------------------|
| timestamp   | TIMESTAMP WITH TIME ZONE |
| labels      | MAP(VARCHAR, VARCHAR)    |
| value       | DOUBLE                   |

See [statistics](#statistics) for information about the statistics returned by Loki.

### Examples

This example cURL command

```bash
curl -G -s  "http://localhost:3100/loki/api/v1/query" \
  --data-urlencode 'query=sum(rate({job="varlogs"}[10m])) by (level)' | jq
```

gave this response:

```json
{
  "status": "success",
  "data": {
    "resultType": "vector",
    "result": [
      {
        "metric": {},
        "value": [
          1588889221,
          "1267.1266666666666"
        ]
      },
      {
        "metric": {
          "level": "warn"
        },
        "value": [
          1588889221,
          "37.77166666666667"
        ]
      },
      {
        "metric": {
          "level": "info"
        },
        "value": [
          1588889221,
          "37.69"
        ]
      }
    ],
    "stats": {
      ...
    }
  }
}
```

#### Multi-tenant queries

If your cluster has
[Grafana Loki Multi-Tenancy](../../operations/multi-tenancy/) enabled,
set the `X-Scope-OrgID` header to identify the tenant you want to query.
Here is the same example query for the single tenant called `Tenant1`:

```bash
curl -H 'X-Scope-OrgID:Tenant1' \
  -G -s "http://localhost:3100/loki/api/v1/query" \
  --data-urlencode 'query=sum(rate({job="varlogs"}[10m])) by (level)' | jq
```

To query against the three tenants `Tenant1`, `Tenant2`, and `Tenant3`,
specify the tenant names separated by the pipe (`|`) character:

```bash
curl -H 'X-Scope-OrgID:Tenant1|Tenant2|Tenant3' \
  -G -s "http://localhost:3100/loki/api/v1/query" \
  --data-urlencode 'query=sum(rate({job="varlogs"}[10m])) by (level)' | jq
```

The same example query for Grafana Enterprise Logs
uses Basic Authentication and specifies the tenant names as a `user`.
The tenant names are separated by the pipe (`|`) character.
The password in this example is an access policy token that has been
defined in the `API_TOKEN` environment variable:

```bash
curl -u "Tenant1|Tenant2|Tenant3:$API_TOKEN" \
  -G -s "http://localhost:3100/loki/api/v1/query" \
  --data-urlencode 'query=sum(rate({job="varlogs"}[10m])) by (level)' | jq
```

To query against your hosted log tenant in Grafana Cloud, use HTTP Basic Authentication with your Loki instance ID and an access policy token, as described in [Authentication](#authentication) above.

## Query logs within a range of time

```bash
GET /loki/api/v1/query_range
```

`/loki/api/v1/query_range` is used to do a query over a range of time.
This type of query is often referred to as a range query. Range queries are used for both log and metric type LogQL queries.
It accepts the following query parameters in the URL:

- `query`: The [LogQL](../../query/) query to perform.
- `limit`: The max number of entries to return. It defaults to `100`. Only applies to query types which produce a stream (log lines) response.
- `start`: The start time for the query as a nanosecond Unix epoch or another [supported format](#timestamps). Defaults to one hour ago. Loki returns results with timestamp greater or equal to this value.
- `end`: The end time for the query as a nanosecond Unix epoch or another [supported format](#timestamps). Defaults to now. Loki returns results with timestamp lower than this value.
- `since`: A `duration` used to calculate `start` relative to `end`. If `end` is in the future, `start` is calculated as this duration before now. Any value specified for `start` supersedes this parameter.
- `step`: Query resolution step width in `duration` format or float number of seconds. `duration` refers to Prometheus duration strings of the form `[0-9]+[smhdwy]`. For example, 5m refers to a duration of 5 minutes. Defaults to a dynamic value based on `start` and `end`. Only applies to query types which produce a matrix response.
- `interval`: Only return entries at (or greater than) the specified interval, can be a `duration` format or float number of seconds. Only applies to queries which produce a stream response. Not to be confused with `step`, see the explanation under [Step versus interval](#step-versus-interval).
- `direction`: Determines the sort order of logs. Supported values are `forward` or `backward`. Defaults to `backward.`

In microservices mode, `/loki/api/v1/query_range` is exposed by the querier and the query frontend.

### Step versus interval

Use the `step` parameter when making metric queries to Loki, or queries which return a matrix response. It is evaluated in exactly the same way Prometheus evaluates `step`. First the query will be evaluated at `start` and then evaluated again at `start + step` and again at `start + step + step` until `end` is reached. The result will be a matrix of the query result evaluated at each step.

Use the `interval` parameter when making log queries to Loki, or queries which return a stream response. It is evaluated by returning a log entry at `start`, then the next entry will be returned an entry with timestampe >= `start + interval`, and again at `start + interval + interval` and so on until `end` is reached. It does not fill missing entries.

Response format:

```json
{
  "status": "success",
  "data": {
    "resultType": "matrix" | "streams",
    "result": [<matrix value>] | [<stream value>]
    "stats" : [<statistics>]
  }
}
```

where `<matrix value>` is:

```json
{
  "metric": {
    <label key-value pairs>
  },
  "values": [
    [
      <number: second unix epoch>,
      <string: value>
    ],
    ...
  ]
}
```

The items in the `values` array are sorted by timestamp, and the oldest item is first.

And `<stream value>` is:

```json
{
  "stream": {
    <label key-value pairs>
  },
  "values": [
    [
      <string: nanosecond unix epoch>,
      <string: log line>
    ],
    ...
  ]
}
```

The items in the `values` array are sorted by timestamp.
The most recent item is first when using `direction=backward`.
The oldest item is first when using `direction=forward`.

Parquet can be request as a response format by setting the `Accept` header to `application/vnd.apache.parquet`.

The schema is the following for streams:

| column_name |       column_type        |
|-------------|--------------------------|
| timestamp   | TIMESTAMP WITH TIME ZONE |
| labels      | MAP(VARCHAR, VARCHAR)    |
| line        |VARCHAR                   |

and for metrics:

| column_name |       column_type        |
|-------------|--------------------------|
| timestamp   | TIMESTAMP WITH TIME ZONE |
| labels      | MAP(VARCHAR, VARCHAR)    |
| value       | DOUBLE                   |

See [statistics](#statistics) for information about the statistics returned by Loki.

### Examples

This example cURL command

```bash
curl -G -s  "http://localhost:3100/loki/api/v1/query_range" \
  --data-urlencode 'query=sum(rate({job="varlogs"}[10m])) by (level)' \
  --data-urlencode 'step=300' | jq
```

gave this response:

```json
{
  "status": "success",
  "data": {
    "resultType": "matrix",
    "result": [
      {
       "metric": {
          "level": "info"
        },
        "values": [
          [
            1588889221,
            "137.95"
          ],
          [
            1588889221,
            "467.115"
          ],
          [
            1588889221,
            "658.8516666666667"
          ]
        ]
      },
      {
        "metric": {
          "level": "warn"
        },
        "values": [
          [
            1588889221,
            "137.27833333333334"
          ],
          [
            1588889221,
            "467.69"
          ],
          [
            1588889221,
            "660.6933333333334"
          ]
        ]
      }
    ],
    "stats": {
      ...
    }
  }
}
```

This example cURL command

```bash
curl -G -s "http://localhost:3100/loki/api/v1/query_range" \
  --data-urlencode 'query={job="varlogs"}' | jq
```

gave this response:

```json
{
  "status": "success",
  "data": {
    "resultType": "streams",
    "result": [
      {
        "stream": {
          "filename": "/var/log/myproject.log",
          "job": "varlogs",
          "level": "info"
        },
        "values": [
          [
            "1569266497240578000",
            "foo"
          ],
          [
            "1569266492548155000",
            "bar"
          ]
        ]
      }
    ],
    "stats": {
      ...
    }
  }
}
```

## Query labels

```bash
GET /loki/api/v1/labels
```

`/loki/api/v1/labels` retrieves the list of known labels within a given time span.
Loki may use a larger time span than the one specified.
It accepts the following query parameters in the URL:

- `start`: The start time for the query as a nanosecond Unix epoch. Defaults to 6 hours ago.
- `end`: The end time for the query as a nanosecond Unix epoch. Defaults to now.
- `since`: A `duration` used to calculate `start` relative to `end`. If `end` is in the future, `start` is calculated as this duration before now. Any value specified for `start` supersedes this parameter.
- `query`: Log stream selector that selects the streams to match and return label names. Example: `{app="myapp", environment="dev"}`

In microservices mode, `/loki/api/v1/labels` is exposed by the querier.

Response format:

```bash
{
  "status": "success",
  "data": [
    <label string>,
    ...
  ]
}
```

### Examples

This example cURL command

```bash
curl -G -s  "http://localhost:3100/loki/api/v1/labels" | jq
```

gave this response:

```json
{
  "status": "success",
  "data": [
    "foo",
    "bar",
    "baz"
  ]
}
```

## Query label values

```bash
GET /loki/api/v1/label/<name>/values
```

`/loki/api/v1/label/<name>/values` retrieves the list of known values for a given
label within a given time span. Loki may use a larger time span than the one specified.
It accepts the following query parameters in the URL:

- `start`: The start time for the query as a nanosecond Unix epoch. Defaults to 6 hours ago.
- `end`: The end time for the query as a nanosecond Unix epoch. Defaults to now.
- `since`: A `duration` used to calculate `start` relative to `end`. If `end` is in the future, `start` is calculated as this duration before now. Any value specified for `start` supersedes this parameter.
- `query`: Log stream selector that selects the streams to match and return label values for `<name>`. Example: `{app="myapp", environment="dev"}`

In microservices mode, `/loki/api/v1/label/<name>/values` is exposed by the querier.

Response format:

```json
{
  "status": "success",
  "data": [
    <label value>,
    ...
  ]
}
```

### Examples

This example cURL command

```bash
curl -G -s  "http://localhost:3100/loki/api/v1/label/foo/values" | jq
```

gave this response:

```json
{
  "status": "success",
  "data": [
    "cat",
    "dog",
    "axolotl"
  ]
}
```

## Query streams

The Series API is available under the following:

- `GET /loki/api/v1/series`
- `POST /loki/api/v1/series`

This endpoint returns the list of streams (unique set of labels) that match a certain given selector.

URL query parameters:

- `match[]=<selector>`: Repeated log stream selector argument that selects the streams to return. At least one `match[]` argument must be provided.
- `start=<nanosecond Unix epoch>`: Start timestamp.
- `end=<nanosecond Unix epoch>`: End timestamp.
- `since`: A `duration` used to calculate `start` relative to `end`. If `end` is in the future, `start` is calculated as this duration before now. Any value specified for `start` supersedes this parameter.

You can URL-encode these parameters directly in the request body by using the POST method and `Content-Type: application/x-www-form-urlencoded` header. This is useful when specifying a large or dynamic number of stream selectors that may breach server-side URL character limits.

In microservices mode, these endpoints are exposed by the querier.

### Examples

This example cURL command

```bash
curl -s "http://localhost:3100/loki/api/v1/series" \
  --data-urlencode 'match[]={container_name=~"prometheus.*", component="server"}' \
  --data-urlencode 'match[]={app="loki"}' | jq '.'
```

gave this response:

```json
{
  "status": "success",
  "data": [
    {
      "container_name": "loki",
      "app": "loki",
      "stream": "stderr",
      "filename": "/var/log/pods/default_loki-stack-0_50835643-1df0-11ea-ba79-025000000001/loki/0.log",
      "name": "loki",
      "job": "default/loki",
      "controller_revision_hash": "loki-stack-757479754d",
      "statefulset_kubernetes_io_pod_name": "loki-stack-0",
      "release": "loki-stack",
      "namespace": "default",
      "instance": "loki-stack-0"
    },
    {
      "chart": "prometheus-9.3.3",
      "container_name": "prometheus-server-configmap-reload",
      "filename": "/var/log/pods/default_loki-stack-prometheus-server-696cc9ddff-87lmq_507b1db4-1df0-11ea-ba79-025000000001/prometheus-server-configmap-reload/0.log",
      "instance": "loki-stack-prometheus-server-696cc9ddff-87lmq",
      "pod_template_hash": "696cc9ddff",
      "app": "prometheus",
      "component": "server",
      "heritage": "Tiller",
      "job": "default/prometheus",
      "namespace": "default",
      "release": "loki-stack",
      "stream": "stderr"
    },
    {
      "app": "prometheus",
      "component": "server",
      "filename": "/var/log/pods/default_loki-stack-prometheus-server-696cc9ddff-87lmq_507b1db4-1df0-11ea-ba79-025000000001/prometheus-server/0.log",
      "release": "loki-stack",
      "namespace": "default",
      "pod_template_hash": "696cc9ddff",
      "stream": "stderr",
      "chart": "prometheus-9.3.3",
      "container_name": "prometheus-server",
      "heritage": "Tiller",
      "instance": "loki-stack-prometheus-server-696cc9ddff-87lmq",
      "job": "default/prometheus"
    }
  ]
}
```

## Query log statistics

```bash
GET /loki/api/v1/index/stats
```

The `/loki/api/v1/index/stats` endpoint can be used to query the index for the number of `streams`, `chunks`, `entries`, and `bytes` that a query resolves to.

URL query parameters:

- `query`: The [LogQL](../../query/) matchers to check (that is, `{job="foo", env!="dev"}`)
- `start=<nanosecond Unix epoch>`: Start timestamp.
- `end=<nanosecond Unix epoch>`: End timestamp.

You can URL-encode these parameters directly in the request body by using the POST method and `Content-Type: application/x-www-form-urlencoded` header. This is useful when specifying a large or dynamic number of stream selectors that may breach server-side URL character limits.

Response:

```json
{
  "streams": 100,
  "chunks": 1000,
  "entries": 5000,
  "bytes": 100000
}
```

It is an approximation with the following caveats:

- It does not include data from the ingesters.
- It is a probabilistic technique.
- Streams/chunks which span multiple period configurations may be counted twice.

These make it generally more helpful for larger queries.
It can be used for better understanding the throughput requirements and data topology for a list of matchers over a period of time.

## Query log volume

```bash
GET /loki/api/v1/index/volume
GET /loki/api/v1/index/volume_range
```

{{< admonition type="note" >}}
This feature is controlled by `volume_enabled`, which defaults to `true`. Set it to `false` to disable it.
{{< /admonition >}}

The `/loki/api/v1/index/volume` and `/loki/api/v1/index/volume_range` endpoints can be used to query the index for volume information about label and label-value combinations. This is helpful in exploring the logs Loki has ingested to find high or low volume streams. The `volume` endpoint returns results for a single point in time, the time the query was processed. Each datapoint represents an aggregation of the matching label or series over the requested time period, returned in a Prometheus style vector response. The `volume_range` endoint returns a series of datapoints over a range of time, in Prometheus style matrix response, for each matching set of labels or series. The number of timestamps returned when querying `volume_range` will be determined by the provided `step` parameter and the requested time range.

The `query` should be a valid LogQL stream selector, for example `{job="foo", env=~".+"}`. By default, these endpoints will aggregate into series consisting of all matches for labels included in the query. For example, assuming you have the streams `{job="foo", env="prod", team="alpha"}`, `{job="bar", env="prod", team="beta"}`, `{job="foo", env="dev", team="alpha"}`, and `{job="bar", env="dev", team="beta"}` in your system. The query `{job="foo", env=~".+"}` would return the two metric series `{job="foo", env="dev"}` and `{job="foo", env="prod"}`, each with datapoints representing the accumulate values of chunks for the streams matching that selector, which in this case would be the streams `{job="foo", env="dev", team="alpha"}` and `{job="foo", env="prod", team="alpha"}`, respectively.

There are two parameters which can affect the aggregation strategy. First, a comma-separated list of `targetLabels` can be provided, allowing volumes to be aggregated by the speficied `targetLabels` only. This is useful for negations. For example, if you said `{team="alpha", env!="dev"}`, the default behavior would include `env` in the aggregation set. However, maybe you're looking for all non-dev jobs for team alpha, and you don't care which env those are in (other than caring that they're not dev jobs). To achieve this, you could specify `targetLabels=team,job`, resulting in a single metric series (in this case) of `{team="alpha", job="foo}`.

The other way to change aggregations is with the `aggregateBy` parameter. The default value for this is `series`, which aggregates into combinations of matching key-value pairs. Alternately this can be specified as `labels`, which will aggregate into labels only. In this case, the response will have a metric series with a label name matching each label, and a label value of `""`. This is useful for exploring logs at a high level. For example, if you wanted to know what percentage of your logs had a `team` label, you could query your logs with `aggregateBy=labels` and a query with either an exact or regex match on `team`, or by including `team` in the list of `targetLabels`.

URL query parameters:

- `query`: The [LogQL](../../query/) matchers to check (that is, `{job="foo", env=~".+"}`). This parameter is required.
- `start=<nanosecond Unix epoch>`: Start timestamp. This parameter is required.
- `end=<nanosecond Unix epoch>`: End timestamp. This parameter is required.
- `limit`: How many metric series to return. The parameter is optional, the default is `100`.
- `step`: Query resolution step width in `duration` format or float number of seconds. `duration` refers to Prometheus duration strings of the form `[0-9]+[smhdwy]`. For example, 5m refers to a duration of 5 minutes. Defaults to a dynamic value based on `start` and `end`. This parameter is optional, and only applicable when querying the `volume_range` endpoint, which will always return a Prometheus style matrix response. The default step configured for range queries will be used when not provided.
- `targetLabels`: A comma separated list of labels to aggregate into. This parameter is optional. When not provided, volumes will be aggregated into the matching labels or label-value pairs.
- `aggregateBy`: Whether to aggregate into labels or label-value pairs. This parameter is optional, the default is label-value pairs.

You can URL-encode these parameters directly in the request body by using the POST method and `Content-Type: application/x-www-form-urlencoded` header. This is useful when specifying a large or dynamic number of stream selectors that may breach server-side URL character limits.

## Patterns detection

```bash
GET /loki/api/v1/patterns
```

{{< admonition type="note" >}}
You must configure

```yaml
pattern_ingester:
  enabled: true
```

to enable this feature.
{{< /admonition >}}

The `/loki/api/v1/patterns` endpoint can be used to query loki for patterns detected in the logs. This helps understand the structure of the logs Loki has ingested.

The `query` should be a valid LogQL stream selector, for example `{job="foo", env=~".+"}`. The result is aggregated by the `pattern` from all matching streams.

For each pattern detected, the response includes the pattern itself and the number of samples for each pattern at each timestamp.

For example, if you have the following logs:

```log
ts=2024-03-30T23:03:40 caller=grpc_logging.go:66 level=info method=/cortex.Ingester/Push duration=200ms msg=gRPC
ts=2024-03-30T23:03:41 caller=grpc_logging.go:66 level=info method=/cortex.Ingester/Push duration=500ms msg=gRPC
```

The pattern detected might be:

```log
ts=<_> caller=grpc_logging.go:66 level=info method=/cortex.Ingester/Push duration=<_> msg=gRPC
```

URL query parameters:

- `query`: The [LogQL](../../query/) matchers to check (that is, `{job="foo", env=~".+"}`). This parameter is required.
- `start=<nanosecond Unix epoch>`: Start timestamp. This parameter is required.
- `end=<nanosecond Unix epoch>`: End timestamp. This parameter is required.
- `step=<duration string or float number of seconds>`: Step between samples for occurrences of this pattern. This parameter is optional.

### Examples

This example cURL command

```bash
curl -s "http://localhost:3100/loki/api/v1/patterns" \
  --data-urlencode 'query={app="loki"}' | jq
```

gave this response:

```json
{
  "status": "success",
  "data": [
    {
      "pattern": "<_> caller=grpc_logging.go:66 <_> level=error method=/cortex.Ingester/Push <_> msg=gRPC err=\"connection refused to object store\"",
      "samples": [
        [
          1711839260,
          1
        ],
        [
          1711839270,
          2
        ],
        [
          1711839280,
          1
        ]
      ]
    },
    {
      "pattern": "<_> caller=grpc_logging.go:66 <_> level=info method=/cortex.Ingester/Push <_> msg=gRPC",
      "samples": [
        [
          1711839260,
          105
        ],
        [
          1711839270,
          222
        ],
        [
          1711839280,
          196
        ]
      ]
    }
  ]
}
```

The result is a list of patterns detected in the logs, with the number of samples for each pattern at each timestamp.
The pattern format is the same as the [LogQL](../../query/) pattern filter and parser and can be used in queries for filtering matching logs.
Each sample is a tuple of timestamp (second) and count.

## Detected fields

```bash
GET /loki/api/v1/detected_fields
POST /loki/api/v1/detected_fields
```

The `/loki/api/v1/detected_fields` endpoint returns fields that Loki has detected in log lines matching the given stream selector, along with the inferred type, estimated cardinality, and the parser that was used to extract the field.
This endpoint is useful for discovering the structure of your logs without running a full log query.

URL query parameters:

- `query`: The [LogQL](../../query/) stream selector to match. Example: `{app="myapp", environment="dev"}`. This parameter is required.
- `start=<nanosecond Unix epoch>`: Start timestamp. This parameter is optional. If omitted, it defaults to `end - since`.
- `end=<nanosecond Unix epoch>`: End timestamp. This parameter is optional. If omitted, it defaults to the current server time.
- `since=<duration>`: Relative time range (for example, `1h`, `5m`). This parameter is optional and is used when `start` is omitted to compute `start = end - since`. If both `start` and `since` are omitted, `since` defaults to `1h`.
- `step=<duration string or float number of seconds>`: Step between sample windows. This parameter is optional.
- `line_limit=<integer>`: Maximum number of log lines to scan per shard. Defaults to 100. This parameter is optional.
- `limit=<integer>`: Maximum number of fields to return. Defaults to 1000. The query parameter `field_limit` is also accepted as an alias for backwards compatibility. This parameter is optional.

You can URL-encode these parameters directly in the request body by using the POST method and `Content-Type: application/x-www-form-urlencoded` header.

Response format:

```bash
{
  "fields": [
    {
      "label": <string>,
      "type": <string|int|float|boolean|duration|bytes>,
      "cardinality": <integer>,
      "parsers": [<string>],
      "jsonPath": <string, optional>,
      "sketch": <object, optional>
    }
  ],
  "limit": <integer>,
  "stats": <statistics object>
}
```

The `stats` object uses the same [statistics structure](#statistics) as regular queries and
reports the underlying log scan. It is included even when no fields or values are returned.
The `limit` parameter limits returned metadata, not the reported scan statistics.

### Examples

This example cURL command

```bash
curl -H 'X-Scope-OrgID: <TENANT_ID>' -G -s "http://localhost:3100/loki/api/v1/detected_fields" \
  --data-urlencode 'query={app="myapp"}' \
  --data-urlencode 'start=1609459200000000000' \
  --data-urlencode 'end=1609462800000000000' | jq
```

gave this response (other statistics fields omitted for brevity):

```json
{
  "fields": [
    {
      "label": "level",
      "type": "string",
      "cardinality": 3,
      "parsers": ["logfmt"]
    },
    {
      "label": "duration",
      "type": "duration",
      "cardinality": 152,
      "parsers": ["logfmt"]
    },
    {
      "label": "status",
      "type": "int",
      "cardinality": 5,
      "parsers": ["logfmt"]
    }
  ],
  "limit": 1000,
  "stats": {
    "summary": {
      "totalBytesProcessed": 1024
    }
  }
}
```

## Detected field values

```bash
GET /loki/api/v1/detected_field/{name}/values
POST /loki/api/v1/detected_field/{name}/values
```

The `/loki/api/v1/detected_field/{name}/values` endpoint returns the values observed for a specific detected field matching the given stream selector.
`{name}` is the name of the field to retrieve values for.

URL query parameters:

- `query`: The [LogQL](../../query/) stream selector to match. Example: `{app="myapp", environment="dev"}`. This parameter is required.
- `start=<nanosecond Unix epoch>`: Start timestamp. This parameter is optional. If omitted, it defaults to `end - since`.
- `end=<nanosecond Unix epoch>`: End timestamp. This parameter is optional. If omitted, it defaults to the current server time.
- `since=<duration>`: Relative time range (for example, `1h`, `5m`). This parameter is optional and is used when `start` is omitted to compute `start = end - since`. If both `start` and `since` are omitted, `since` defaults to `1h`.
- `step=<duration string or float number of seconds>`: Step between sample windows. This parameter is optional.
- `line_limit=<integer>`: Maximum number of log lines to scan per shard. Defaults to 100. This parameter is optional.
- `limit=<integer>`: Maximum number of values to return. Defaults to 1000. The query parameter `field_limit` is also accepted as an alias. This parameter is optional.

You can URL-encode these parameters directly in the request body by using the POST method and `Content-Type: application/x-www-form-urlencoded` header.

Response format:

```bash
{
  "values": [
    <string>,
    ...
  ],
  "limit": <integer>,
  "stats": <statistics object>
}
```

The `stats` object uses the same [statistics structure](#statistics) as regular queries and
reports the underlying log scan. It is included even when no fields or values are returned.
The `limit` parameter limits returned metadata, not the reported scan statistics.

### Examples

This example cURL command

```bash
curl -H 'X-Scope-OrgID: <TENANT_ID>' -G -s "http://localhost:3100/loki/api/v1/detected_field/level/values" \
  --data-urlencode 'query={app="myapp"}' \
  --data-urlencode 'start=1609459200000000000' \
  --data-urlencode 'end=1609462800000000000' | jq
```

gave this response (other statistics fields omitted for brevity):

```json
{
  "values": [
    "debug",
    "info",
    "warn",
    "error"
  ],
  "limit": 1000,
  "stats": {
    "summary": {
      "totalBytesProcessed": 1024
    }
  }
}
```

## Stream logs

```bash
GET /loki/api/v1/tail
```

`/loki/api/v1/tail` is a WebSocket endpoint that streams log messages based on a query to the client.
It accepts the following query parameters in the URL:

- `query`: The [LogQL](../../query/) query to perform.
- `delay_for`: The number of seconds to delay retrieving logs to let slow
  loggers catch up. Defaults to 0 and cannot be larger than 5.
- `limit`: The max number of entries to return. It defaults to `100`.
- `start`: The start time for the query as a nanosecond Unix epoch. Defaults to one hour ago.

In microservices mode, `/loki/api/v1/tail` is exposed by the querier.

{{< admonition type="note" >}}
The `tail` endpoint is designed for near real-time observation of a log stream. The initial lookback from `start` to the current time is best-effort and isn't guaranteed to return every matching log line, so this endpoint isn't suitable for retrieving complete log history. To reliably retrieve a large or complete range of logs, make repeated calls to [`/loki/api/v1/query_range`](#query-logs-within-a-range-of-time).
{{< /admonition >}}

Response format (streamed):

```json
{
  "streams": [
    {
      "stream": {
        <label key-value pairs>
      },
      "values": [
        [
          <string: nanosecond unix epoch>,
          <string: log line>
        ]
      ]
    }
  ],
  "dropped_entries": [
    {
      "labels": {
        <label key-value pairs>
      },
      "timestamp": "<nanosecond unix epoch>"
    }
  ]
}
```

## Readiness probe

```bash
GET /ready
```

`/ready` returns HTTP 200 when the Loki instance is ready to accept traffic. If
running Loki on Kubernetes, `/ready` can be used as a readiness probe.

In microservices mode, the `/ready` endpoint is exposed by all components.

## Change log level

```bash
GET /log_level
POST /log_level
```

`/log_level` a `GET` returns the current log level and a `POST` lets you change the log level of a Loki process **at runtime**.
This can be useful for accessing debugging information during an incident. Caution should be used when running at the `debug` log level, as this produces a large volume of data.

The endpoint accepts the following query parameters in the URL:

- `log_level`: A valid log level that can be passed as a URL param (`?log_level=<level>`) or as a form value in case of `POST`. Valid levels: [debug, info, warn, error]

In microservices mode, the `/log_level` endpoint is exposed by all components.

## Prometheus metrics

```bash
GET /metrics
```

`/metrics` returns exposed Prometheus metrics. See
[Observing Loki](../../operations/meta-monitoring/)
for a list of exported metrics.

In microservices mode, the `/metrics` endpoint is exposed by all components.

## Show current configuration

```bash
GET /config
```

`/config` exposes the current configuration. The optional `mode` query parameter can be used to
modify the output. If it has the value `diff` only the differences between the default configuration
and the current are returned. A value of `defaults` returns the default configuration.

In microservices mode, the `/config` endpoint is exposed by all components.

## Show exposed configuration

```bash
GET /loki/api/v1/exposed-config
```

`/loki/api/v1/exposed-config` returns a fixed, user-configured subset of `/config`, set with
`-config.exposed-fields`. It's meant for cases where `/config` itself isn't safe to expose directly,
such as through a datasource proxy. Empty by default, meaning the endpoint returns `{}`.

In microservices mode, the `/loki/api/v1/exposed-config` endpoint is exposed by all components.

## List running services

```bash
GET /services
```

`/services` returns a list of all running services and their current states.

Services can have the following states:

- **New**: Service is new, not running yet (initial state)
- **Starting**: Service is starting; if starting succeeds, service enters **Running** state
- **Running**: Service is fully running now; when service stops running, it enters **Stopping** state
- **Stopping**: Service is shutting down
- **Terminated**: Service has stopped successfully (terminal state)
- **Failed**: Service has failed in **Starting**, **Running** or **Stopping** state (terminal state)

## Show build information

```bash
GET /loki/api/v1/status/buildinfo
```

`/loki/api/v1/status/buildinfo` exposes the build information in a JSON object. The fields are `version`, `revision`, `branch`, `buildDate`, `buildUser`, and `goVersion`.

## Flush in-memory chunks to backing store

```bash
POST /flush
```

`/flush` triggers a flush of all in-memory chunks held by the ingesters to the
backing store. By default the flush is queued and the request returns without 
waiting for it to complete.

**URL query parameters:**

- `flush_index` (experimental):
  If set to `true`, the request waits for the chunks to be flushed, and then also forces the ingester's in-memory index to be built and shipped to the backing store.
  The flushed chunks are then immediately referenceable, rather than waiting for the next periodic index rotation.
  Waiting for the flush can take a long time, so set a generous client timeout.
  An ingester runs only one `flush_index` request at a time, across `/flush` and `/flush/tenant`.
  If another one is in progress, the request returns `429 Too Many Requests` without flushing anything, and you can retry it later.
  A bare `flush_index` is treated as `true`.
  If omitted, the index is not flushed and the request returns as soon as the flushes are queued.
  Requests without `flush_index` are never rejected because of an index flush in progress.

In microservices mode, the `/flush` endpoint is exposed by the ingester.

## Flush in-memory chunks for a tenant (experimental)

```bash
POST /flush/tenant
```

This endpoint is experimental and may change or be removed.

`/flush/tenant` triggers a flush of the in-memory chunks held by an ingester for
a single tenant to the backing store. By default the flush is queued and the
request returns without waiting for it to complete.

**URL query parameters:**

- `tenant=<tenant ID>`:
  Required. The tenant to flush.
- `matchers=<selector>`:
  Optional log stream selector that selects the streams to flush, for example `{app="foo"}`.
  If omitted, all in-memory streams for the tenant are flushed.
- `flush_index`:
  If set to `true`, the request waits for the chunks to be flushed, and then also forces the ingester's in-memory index to be built and shipped to the backing store.
  The flushed chunks are then immediately referenceable, rather than waiting for the next periodic index rotation.
  The index is shared by all tenants on the ingester, so the index flush is not tenant-scoped.
  Waiting for the flush can take a long time, so set a generous client timeout.
  An ingester runs only one `flush_index` request at a time, across all tenants and across `/flush` and `/flush/tenant`.
  If another one is in progress, the request returns `429 Too Many Requests` without flushing anything, and you can retry it later.
  A bare `flush_index` is treated as `true`.
  If omitted, the index is not flushed and the request returns as soon as the flushes are queued.
  Requests without `flush_index` are never rejected because of an index flush in progress.

For example, `POST /flush/tenant?tenant=foo&matchers={app="bar"}&flush_index`.

In microservices mode, the `/flush/tenant` endpoint is exposed by the ingester.

## Sync indexes from object storage

```bash
PUT /sync-indexes
GET /sync-indexes
```

`/sync-indexes` lets a caller force an index-gateway to refresh its object-storage
listing cache and download any newly shipped index files on demand, instead of
waiting for the periodic resync (which re-lists through a cache that can take up
to a minute to expire). It is the second step of making a freshly flushed index
queryable across read nodes:

1. `POST /flush/tenant?tenant=<tenant>&flush_index` on the ingester ships the new index to object storage.
1. `PUT /sync-indexes` on the index-gateway downloads it.
1. `POST /loki/api/v1/cache/generation_numbers/increase` on the compactor
   invalidates any cached query results so the new data is reflected.

This endpoint is not tenant-scoped; it refreshes the listing for all tables the
index-gateway already tracks.

A `PUT` request triggers a sync asynchronously and returns immediately:

- `202 Accepted` if a new sync was started.
- `409 Conflict` if a sync (manual or the periodic one) is already in progress, in
  which case the request is a no-op.

Because a `PUT` is a no-op while another sync is running, and a sync can be
long-running on a gateway that tracks many tables, callers should not assume a
single `PUT` ran the sync: re-issue the `PUT` until it is accepted, then poll
`GET` until the sync completes.

A `GET` request reports the sync status of each synced index as a JSON array, with
one entry per index (the index-gateway runs an independent sync per schema period):

```json
[
  {
    "name": "s3_2024-04-01",
    "in_progress": true,
    "last_trigger": "manual",
    "current_duration": "12s",
    "last_duration": "1m30s"
  },
  {
    "name": "s3_2024-10-01",
    "in_progress": false,
    "last_trigger": "periodic",
    "last_duration": "5s"
  },
  {
    "name": "s3_2025-04-01",
    "in_progress": false,
    "last_trigger": "never_triggered",
    "last_duration": "0s"
  }
]
```

Each entry reports (durations are Go duration strings, e.g. `"1m30s"`):

- `name`: identifies the index (the schema period's store name).
- `in_progress`: whether a sync (manual or periodic) is currently running for this index.
- `last_trigger`: what triggered the current or most recent sync — `manual` or `periodic`, or `never_triggered` if no sync has run yet.
- `current_duration`: how long the in-progress sync has been running (present only when `in_progress` is `true`).
- `last_duration`: how long the previous completed sync took (`"0s"` if none has completed yet).

If the configured index store does not support on-demand syncing, both `PUT` and `GET` return `503 Service Unavailable`.

In microservices mode, the `/sync-indexes` endpoint is exposed by the index-gateway.

## Prepare ingester shutdown

```bash
GET, POST, DELETE /ingester/prepare_shutdown
```

This endpoint is used to tell the ingester to release all resources on receiving the next `SIGTERM` or `SIGINT` signal.

A `POST` request to the `/ingester/prepare_shutdown` endpoint configures the ingester for a full shutdown and returns immediately.
Only when the ingester process is stopped with `SIGINT` or `SIGTERM`, it will unregister from the ring, and in-memory data will be flushed to long-term storage.
This endpoint supersedes any YAML configurations and isn't necessary if the ingester is already configured to unregister from the ring or to flush on shutdown.

A `GET` request to the `/ingester/prepare_shutdown` endpoint returns the status of this configuration, either `set` or `unset`.

A `DELETE` request to the `/ingester/prepare_shutdown` endpoint reverts the configuration of the ingester to its previous state
(with respect to unregistering on shutdown and flushing of in-memory data to long-term storage).

This API endpoint is usually used by Kubernetes-specific scale down automations such as the
[rollout-operator](https://github.com/grafana/rollout-operator).

## Flush in-memory chunks and shut down

```bash
GET, POST /ingester/shutdown
```

`/ingester/shutdown` triggers a shutdown of the ingester and notably will _always_ flush any in memory chunks it holds.
This is helpful for scaling down WAL-enabled ingesters where we want to ensure old WAL directories are not orphaned,
but instead flushed to our chunk backend.

It accepts three URL query parameters `flush`, `delete_ring_tokens`, and `terminate`.

**URL query parameters:**

- `flush=<bool>`:
  Flag to control whether to flush any in-memory chunks the ingester holds. Defaults to `true`.
- `delete_ring_tokens=<bool>`:
  Flag to control whether to delete the file that contains the ingester ring tokens of the instance if the `-ingester.token-file-path` is specified. Defaults to `false`.
- `terminate=<bool>`:
  Flag to control whether to terminate the Loki process after service shutdown. Defaults to `true`.

This handler, in contrast to the deprecated `/ingester/flush_shutdown` handler, terminates the Loki process by default.
This behaviour can be changed by setting the `terminate` query parameter to `false`.

In microservices mode, the `/ingester/shutdown` endpoint is exposed by the ingester.

## Distributor ring status

```bash
GET /distributor/ring
```

Displays a web page with the distributor hash ring status, including the state, health, and last heartbeat time of each distributor.

## Index gateway ring status

```bash
GET /indexgateway/ring
```

Displays a web page with the index gateway hash ring status, including the state, health, and last heartbeat time of each index gateway.

## Ruler

The ruler API endpoints require to configure a backend object storage to store the recording rules and alerts. The ruler API uses the concept of a "namespace" when creating rule groups. This is a stand-in for the name of the rule file in Prometheus. Rule groups must be named uniquely within a namespace.

{{< admonition type="note" >}}
This feature is controlled by `enable_api`, which defaults to `true`. Set it to `false` to disable it.
{{< /admonition >}}

### Ruler ring status

```bash
GET /ruler/ring
```

Displays a web page with the ruler hash ring status, including the state, health, and last heartbeat time of each ruler.

### List rule groups

```bash
GET /loki/api/v1/rules
```

List all rules configured for the authenticated tenant. This endpoint returns a YAML dictionary with all the rule groups for each namespace and `200` status code on success.

#### Example response

```yaml
---
<namespace1>:
- name: <string>
  interval: <duration;optional>
  rules:
  - alert: <string>
    expr: <string>
    for: <duration>
    annotations:
      <annotation_name>: <string>
    labels:
      <label_name>: <string>
- name: <string>
  interval: <duration;optional>
  rules:
  - alert: <string>
    expr: <string>
    for: <duration>
    annotations:
      <annotation_name>: <string>
    labels:
      <label_name>: <string>
<namespace2>:
- name: <string>
  interval: <duration;optional>
  rules:
  - alert: <string>
    expr: <string>
    for: <duration>
    annotations:
      <annotation_name>: <string>
    labels:
      <label_name>: <string>
```

### Get rule groups by namespace

```bash
GET /loki/api/v1/rules/{namespace}
```

Returns the rule groups defined for a given namespace.

#### Example response

```yaml
name: <string>
interval: <duration;optional>
rules:
  - alert: <string>
    expr: <string>
    for: <duration>
    annotations:
      <annotation_name>: <string>
    labels:
      <label_name>: <string>
```

### Get rule group

```bash
GET /loki/api/v1/rules/{namespace}/{groupName}
```

Returns the rule group matching the request namespace and group name.

### Set rule group

```bash
POST /loki/api/v1/rules/{namespace}
```

Creates or updates a rule group. This endpoint expects a request with `Content-Type: application/yaml` header and the rules **YAML** definition in the request body, and returns `202` on success.

#### Example request

Request headers:

- `Content-Type: application/yaml`

Request body:

```yaml
name: <string>
interval: <duration;optional>
rules:
  - alert: <string>
    expr: <string>
    for: <duration>
    annotations:
      <annotation_name>: <string>
    labels:
      <label_name>: <string>
```

### Delete rule group

```bash
DELETE /loki/api/v1/rules/{namespace}/{groupName}

```

Deletes a rule group by namespace and group name. This endpoints returns `202` on success.

### Delete namespace

```bash
DELETE /loki/api/v1/rules/{namespace}
```

Deletes all the rule groups in a namespace (including the namespace itself). This endpoint returns `202` on success.

### List rules

```bash
GET /prometheus/api/v1/rules?type={alert|record}&file={}&rule_group={}&rule_name={}
```

Prometheus-compatible rules endpoint to list alerting and recording rules that are currently loaded.

The `type` parameter is optional. If set, only the specified type of rule is returned.

The `file`, `rule_group` and `rule_name` parameters are optional, and can accept multiple values. If set, the response content is filtered accordingly.

For more information, refer to the [Prometheus rules](https://prometheus.io/docs/prometheus/latest/querying/api/#rules) documentation.

### List alerts

```bash
GET /prometheus/api/v1/alerts
```

Prometheus-compatible rules endpoint to list all active alerts.

For more information, refer to the Prometheus [alerts](https://prometheus.io/docs/prometheus/latest/querying/api/#alerts) documentation.

## Compactor

### Compactor ring status

```bash
GET /compactor/ring
```

Displays a web page with the compactor hash ring status, including the state, health, and last heartbeat time of each compactor.

### Request log deletion

```bash
POST /loki/api/v1/delete
PUT /loki/api/v1/delete
```

Create a new delete request for the authenticated tenant.
The [log entry deletion](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/logs-deletion/) documentation has configuration details.

Log entry deletion is supported when the TSDB index is configured for the index store. It is also supported on the deprecated BoltDB Shipper index, but BoltDB Shipper is being removed in Loki 4.0, so new deployments should use TSDB.

Query parameters:

- `query=<series_selector>`: query argument that identifies the streams from which to delete with optional line filters.
- `start=<rfc3339 | unix_seconds_timestamp>`: A timestamp that identifies the start of the time window within which entries will be deleted. This parameter is required.
- `end=<rfc3339 | unix_seconds_timestamp>`: A timestamp that identifies the end of the time window within which entries will be deleted. If not specified, defaults to the current time.
- `max_interval=<duration>`: The maximum time period the delete request can span. If the request is larger than this value, it is split into several requests of <= `max_interval`. Valid time units are `s`, `m`, and `h`.

A 204 response indicates success.

The query parameter can also include filter operations. For example `query={foo="bar"} |= "other"` will filter out lines that contain the string "other" for the streams matching the stream selector `{foo="bar"}`.

#### Examples

URL encode the `query` parameter. This sample form of a cURL command URL encodes `query={foo="bar"}`:

```bash
curl -g -X POST \
  'http://127.0.0.1:3100/loki/api/v1/delete?query={foo="bar"}&start=1591616227&end=1591619692' \
  -H 'X-Scope-OrgID: 1'
```

The same example deletion request for Grafana Enterprise Logs uses Basic Authentication and specifies the tenant name as a user; `Tenant1` is the tenant name in this example. The password in this example is an access policy token that has been defined in the API_TOKEN environment variable. The token must be for an access policy with `logs:delete` scope for the tenant specified in the user field:

```bash
curl -u "Tenant1:$API_TOKEN" \
  -g -X POST \
  'http://127.0.0.1:3100/loki/api/v1/delete?query={foo="bar"}&start=1591616227&end=1591619692'
```

### List log deletion requests

```bash
GET /loki/api/v1/delete
```

List the existing delete requests for the authenticated tenant.
The [log entry deletion](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/logs-deletion/) documentation has configuration details.

Log entry deletion is supported when the TSDB index is configured for the index store. It is also supported on the deprecated BoltDB Shipper index, but BoltDB Shipper is being removed in Loki 4.0, so new deployments should use TSDB.

List the existing delete requests using the following API:

```bash
GET /loki/api/v1/delete
```

This endpoint returns both processed and unprocessed deletion requests. It does not list canceled requests, as those requests will have been removed from storage.

Query parameters:

- `start=<rfc3339 | unix_seconds_timestamp>`: Optional. A timestamp that identifies the start of the time range. Only deletion requests that overlap with this time range will be returned. Must be provided together with `end`.
- `end=<rfc3339 | unix_seconds_timestamp>`: Optional. A timestamp that identifies the end of the time range. Only deletion requests that overlap with this time range will be returned. Must be provided together with `start`.

#### Examples

Example cURL command:

```bash
curl -X GET \
  <COMPACTOR_ADDR>/loki/api/v1/delete \
  -H 'X-Scope-OrgID: <ORG_ID>'
```

The same example deletion request for Grafana Enterprise Logs uses Basic Authentication and specifies the tenant name as a user; `Tenant1` is the tenant name in this example. The password in this example is an access policy token that has been defined in the API_TOKEN environment variable. The token must be for an access policy with `logs:delete` scope for the tenant specified in the user field.

```bash
curl -u "Tenant1:$API_TOKEN" \
  -X GET \
  <COMPACTOR_ADDR>/loki/api/v1/delete
```

### Request cancellation of a delete request

```bash
DELETE /loki/api/v1/delete
```

Remove a delete request for the authenticated tenant.
The [log entry deletion](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/logs-deletion/) documentation has configuration details.

Loki allows cancellation of delete requests until the requests are picked up for processing. It is controlled by the `delete_request_cancel_period` YAML configuration or the equivalent command line option when invoking Loki. To cancel a delete request that has been picked up for processing or is partially complete, pass the `force=true` query parameter to the API.

Log entry deletion is supported when the TSDB index is configured for the index store. It is also supported on the deprecated BoltDB Shipper index, but BoltDB Shipper is being removed in Loki 4.0, so new deployments should use TSDB.

Cancel a delete request using this compactor endpoint:

```bash
DELETE /loki/api/v1/delete
```

Query parameters:

- `request_id=<request_id>`: Identifies the delete request to cancel; IDs are found using the `delete` endpoint.
- `force=<boolean>`: When the `force` query parameter is true, partially completed delete requests will be canceled.
  {{< admonition type="note" >}}
  some data from the request may still be deleted and the deleted request will be listed as 'processed'.
  {{< /admonition >}}

A 204 response indicates success.

#### Examples

Example cURL command:

```bash
curl -X DELETE \
  '<COMPACTOR_ADDR>/loki/api/v1/delete?request_id=<REQUEST_ID>' \
  -H 'X-Scope-OrgID: <TENANT_ID>'
```

The same example deletion cancellation request for Grafana Enterprise Logs uses Basic Authentication and specifies the tenant name as a user; `Tenant1` is the tenant name in this example. The password in this example is an access policy token that has been defined in the API_TOKEN environment variable. The token must be for an access policy with `logs:delete` scope for the tenant specified in the user field.

```bash
curl -u "Tenant1:$API_TOKEN" \
  -X DELETE \
  '<COMPACTOR_ADDR>/loki/api/v1/delete?request_id=<REQUEST_ID>'
```

## Format a LogQL query

```bash
GET /loki/api/v1/format_query
POST /loki/api/v1/format_query
```

The endpoint accepts the following query parameters in the URL:

- `query`: A LogQL query string. Can be passed as URL param (`?query=<query>`) in case of both `GET` and `POST`. Or as form value in case of `POST`.

The `/loki/api/v1/format_query` endpoint lets you format LogQL queries. It returns an error if the passed LogQL is invalid. It is exposed by all Loki components and helps to improve readability and the debugging experience of LogQL queries.

The following example formats the expression LogQL `{foo=   "bar"}` into

```json
{
   "status" : "success",
   "data" : "{foo=\"bar\"}"
}
```

# Loki network ports

Loki components communicate with each other over the network, and clients such as Grafana and Grafana Alloy reach Loki over HTTP.
Use this page to plan firewall rules, Kubernetes NetworkPolicies, or service mesh configuration for a Loki deployment.

All port numbers on this page are defaults.
If you change a port in your configuration, use your value instead.

## Ports that Loki listens on

Every Loki process opens the HTTP and gRPC ports.
Components that share [hash rings](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/hash-rings/) through memberlist also open the memberlist port.

| Port | Protocol | Configuration parameter | Purpose |
| --- | --- | --- | --- |
| 3100 | HTTP over TCP | `server.http_listen_port` | The [Loki HTTP API](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-http-api/) for pushing and querying logs, the `/metrics` endpoint, and the `/ready` health check endpoint. |
| 9095 | gRPC over TCP | `server.grpc_listen_port` | Requests between Loki components, such as distributors sending logs to ingesters. |
| 7946 | TCP | `memberlist.bind_port` | Gossip messages that keep the hash rings in sync between components. |

Memberlist in Loki sends all gossip traffic over TCP, so you don't need to open UDP for port 7946.

If you enable TLS with `server.http_tls_config`, `server.grpc_tls_config`, or `memberlist.tls_enabled`, the encrypted traffic uses the same ports.
For details, refer to the [`server`](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#server) and [`memberlist`](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#memberlist) configuration blocks.

## Connections between components

The following table lists the main connections between Loki components.
In [monolithic and simple scalable modes](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/), one process runs several components, but the components still use these ports to reach each other, including across replicas.

| Source | Destination | Port | Protocol | Purpose |
| --- | --- | --- | --- | --- |
| Log collectors, such as Grafana Alloy | Distributor | 3100 | HTTP | Push logs. |
| Grafana and other query clients | Query frontend | 3100 | HTTP | Run queries. |
| Distributor | Ingester | 9095 | gRPC | Send log streams for ingestion. |
| Distributor | Pattern ingester | 9095 | gRPC | Send log lines for pattern detection, when the pattern ingester is enabled. |
| Query frontend | Query scheduler | 9095 | gRPC | Enqueue queries. |
| Query frontend | Querier | 3100 | HTTP | Proxy live tail requests, when `frontend.tail_proxy_url` is set. |
| Querier | Query scheduler | 9095 | gRPC | Pull queries to run. |
| Querier | Query frontend | 9095 | gRPC | Return query results. |
| Querier and ruler | Ingester | 9095 | gRPC | Read recent logs that aren't flushed to object storage yet. |
| Querier and ruler | Index gateway | 9095 | gRPC | Look up the index, when an index gateway is configured. |
| Querier and ruler | Compactor | 9095 or 3100 | gRPC or HTTP | Fetch delete requests, to filter deleted logs out of query results. Loki uses gRPC when `common.compactor_grpc_address` is set, and HTTP when only `common.compactor_address` is set. |
| Index gateway | Bloom gateway | 9095 | gRPC | Filter chunks with bloom filters, when bloom filters are enabled. |
| Bloom builder | Bloom planner | 9095 | gRPC | Receive bloom build tasks, when bloom filters are enabled. |
| Components that use hash rings | Each other | 7946 | TCP | Share hash ring state through memberlist. |
| Ruler | Alertmanager | Alertmanager port, usually 9093 | HTTP | Send alerts. |
| Prometheus or Grafana Alloy | All components | 3100 | HTTP | Scrape the `/metrics` endpoint. |

Loki also connects to services outside the deployment, such as object storage and caches.
Use the ports that those services expose, for example 443 for HTTPS object storage endpoints.

## Additional ports in the Helm chart

The [Loki Helm chart](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/concepts/) deploys some components that aren't part of Loki itself.
These components use the following ports:

| Component | Container port | Service port | Protocol | Helm value |
| --- | --- | --- | --- | --- |
| Gateway (NGINX) | 8080 | 80 | HTTP | `gateway.containerPort`, `gateway.service.port` |
| Chunks cache and results cache (Memcached) | 11211 | 11211 | Memcached protocol over TCP | `chunksCache.port`, `resultsCache.port` |
| Memcached exporter | 9150 | 9150 | HTTP | None |
| [Loki Canary](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/loki-canary/) | 3500 | 3500 | HTTP | None |

When the gateway is enabled, clients send push and query requests to the gateway Service on port 80.
The gateway forwards them to the Loki components on port 3100.

Some Services, such as the query frontend Service, also expose port 9096, named `grpclb`.
This port forwards to the gRPC container port, so it doesn't open another port in the pod.

The chart can create NetworkPolicies for this traffic.
To enable them, set `networkPolicy.enabled: true`.
For the related values, such as `networkPolicy.alertmanager.port`, refer to the [Helm chart values](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/reference/).

# Query Loki with Python

This page provides Python examples for the most common Loki HTTP API operations: querying logs, pushing log entries, and listing labels. For the full API reference including all parameters and response formats, see the [Loki HTTP API](../loki-http-api/) documentation.

## Prerequisites

Install the [requests](https://requests.readthedocs.io/) library:

```bash
pip install requests
```

If you need an async-capable client, [httpx](https://www.python-httpx.org/) provides a nearly identical API:

```bash
pip install httpx
```

## Authentication

The examples on this page connect to a local Loki instance without authentication. To use these examples with a multi-tenant or Grafana Cloud deployment, add the appropriate authentication as shown below. All functions defined on this page accept `headers`, `auth`, and `verify` parameters for this purpose.

### Multi-tenant mode

If your cluster has [multi-tenancy](../../operations/multi-tenancy/) enabled, pass the tenant ID in the `X-Scope-OrgID` header:

```python
headers = {"X-Scope-OrgID": "my-tenant"}
resp = requests.get(url, params=params, headers=headers)
```

Using the functions defined on this page:

```python
results = query_range(
    url="http://localhost:3100",
    query='{job="varlogs"}',
    start=datetime.now() - timedelta(hours=1),
    end=datetime.now(),
    headers={"X-Scope-OrgID": "my-tenant"},
)
```

To query across multiple tenants, separate tenant names with the pipe (`|`) character:

```python
headers = {"X-Scope-OrgID": "tenant1|tenant2|tenant3"}
```

### Grafana Cloud

For Grafana Cloud, use basic authentication with your Grafana Cloud user and an API token:

```python
resp = requests.get(url, params=params, auth=("<user>", "<API_TOKEN>"))
```

Using the functions defined on this page:

```python
results = query_range(
    url="https://logs-prod-us-central1.grafana.net",
    query='{job="varlogs"}',
    start=datetime.now() - timedelta(hours=1),
    end=datetime.now(),
    auth=("<user>", "<API_TOKEN>"),
)
```

You can find the **User** and **URL** values in the Loki logging service details of your [Grafana Cloud stack](https://grafana.com/docs/grafana-cloud/account-management/cloud-portal/#your-grafana-cloud-stack).

### Self-signed certificates

If your Loki instance uses a self-signed TLS certificate, you can disable certificate verification:

```python
resp = requests.get(url, params=params, verify=False)
```

For production use, pass the path to your CA bundle instead:

```python
resp = requests.get(url, params=params, verify="/path/to/ca-bundle.crt")
```

Using the functions defined on this page:

```python
results = query_range(
    url="https://loki.internal:3100",
    query='{job="varlogs"}',
    start=datetime.now() - timedelta(hours=1),
    end=datetime.now(),
    verify="/path/to/ca-bundle.crt",
)
```

## Query logs within a range of time

[`GET /loki/api/v1/query_range`](../loki-http-api/#query-logs-within-a-range-of-time) queries logs over a time range. This is the most common query operation.

### Using requests

```python
import requests
from datetime import datetime, timedelta


def query_range(
    url: str,
    query: str,
    start: datetime,
    end: datetime,
    limit: int = 1000,
    headers: dict[str, str] | None = None,
    auth: tuple[str, str] | None = None,
    verify: bool | str = True,  # False to skip TLS, or path to CA bundle
) -> list:
    """Query Loki for log entries within a time range."""
    resp = requests.get(
        f"{url}/loki/api/v1/query_range",
        params={
            "query": query,
            "start": str(int(start.timestamp() * 1e9)),  # nanoseconds
            "end": str(int(end.timestamp() * 1e9)),
            "limit": limit,
            "direction": "backward",
        },
        headers=headers,
        auth=auth,
        verify=verify,
    )
    resp.raise_for_status()
    return resp.json()["data"]["result"]


results = query_range(
    url="http://localhost:3100",
    query='{job="varlogs"} |= "error"',
    start=datetime.now() - timedelta(hours=1),
    end=datetime.now(),
)

for stream in results:
    print(f"Labels: {stream['stream']}")
    for ts, line in stream["values"]:
        print(f"  {datetime.fromtimestamp(int(ts) / 1e9)}: {line}")
```

### Using httpx

```python
import httpx
from datetime import datetime, timedelta


def query_range(
    url: str,
    query: str,
    start: datetime,
    end: datetime,
    limit: int = 1000,
    headers: dict[str, str] | None = None,
    auth: tuple[str, str] | None = None,
    verify: bool | str = True,  # False to skip TLS, or path to CA bundle; httpx also accepts ssl.SSLContext
) -> list:
    """Query Loki for log entries within a time range."""
    resp = httpx.get(
        f"{url}/loki/api/v1/query_range",
        params={
            "query": query,
            "start": str(int(start.timestamp() * 1e9)),  # nanoseconds
            "end": str(int(end.timestamp() * 1e9)),
            "limit": limit,
            "direction": "backward",
        },
        headers=headers,
        auth=auth,
        verify=verify,
    )
    resp.raise_for_status()
    return resp.json()["data"]["result"]


results = query_range(
    url="http://localhost:3100",
    query='{job="varlogs"} |= "error"',
    start=datetime.now() - timedelta(hours=1),
    end=datetime.now(),
)

for stream in results:
    print(f"Labels: {stream['stream']}")
    for ts, line in stream["values"]:
        print(f"  {datetime.fromtimestamp(int(ts) / 1e9)}: {line}")
```

## Query logs at a single point in time

[`GET /loki/api/v1/query`](../loki-http-api/#query-logs-at-a-single-point-in-time) evaluates a query at a single point in time. Use this for instant metric queries such as aggregations with `rate()`, `count_over_time()`, or `bytes_over_time()`. Log stream selectors (queries that return log lines) are not supported as instant queries; use `query_range` instead.

```python
import requests
from datetime import datetime


def query_instant(
    url: str,
    query: str,
    headers: dict[str, str] | None = None,
    auth: tuple[str, str] | None = None,
    verify: bool | str = True,  # False to skip TLS, or path to CA bundle
) -> list:
    """Run an instant metric query against Loki."""
    resp = requests.get(
        f"{url}/loki/api/v1/query",
        params={
            "query": query,
            "time": str(int(datetime.now().timestamp() * 1e9)),
        },
        headers=headers,
        auth=auth,
        verify=verify,
    )
    resp.raise_for_status()
    return resp.json()["data"]["result"]


results = query_instant(
    url="http://localhost:3100",
    query='sum(rate({job="varlogs"}[10m])) by (level)',
)

for entry in results:
    print(f"{entry['metric']}: {entry['value'][1]}")
```

## Push logs

[`POST /loki/api/v1/push`](../loki-http-api/#ingest-logs) sends log entries to Loki using the JSON format.

```python
import json
import time
import requests


def push_logs(
    url: str,
    labels: dict[str, str],
    entries: list[tuple[str, str]],
    headers: dict[str, str] | None = None,
    auth: tuple[str, str] | None = None,
    verify: bool | str = True,  # False to skip TLS, or path to CA bundle
) -> None:
    """Push log entries to Loki.

    Args:
        url: Loki base URL.
        labels: Stream labels, for example {"job": "myapp", "env": "dev"}.
        entries: List of (timestamp_ns, log_line) tuples. Use
                 str(int(time.time() * 1e9)) to get a nanosecond timestamp.
    """
    payload = {
        "streams": [
            {
                "stream": labels,
                "values": [list(e) for e in entries],
            }
        ]
    }
    req_headers = {**(headers or {}), "Content-Type": "application/json"}
    resp = requests.post(
        f"{url}/loki/api/v1/push",
        headers=req_headers,
        data=json.dumps(payload),
        auth=auth,
        verify=verify,
    )
    resp.raise_for_status()


now_ns = str(int(time.time() * 1e9))
push_logs(
    url="http://localhost:3100",
    labels={"job": "myapp", "env": "dev"},
    entries=[
        (now_ns, "application started"),
        (now_ns, "listening on port 8080"),
    ],
)
```

## Query labels and label values

[`GET /loki/api/v1/labels`](../loki-http-api/#query-labels) returns the list of known label names. [`GET /loki/api/v1/label/<name>/values`](../loki-http-api/#query-label-values) returns the values for a specific label.

```python
import requests


def get_labels(
    url: str,
    headers: dict[str, str] | None = None,
    auth: tuple[str, str] | None = None,
    verify: bool | str = True,  # False to skip TLS, or path to CA bundle
) -> list[str]:
    """List all known label names."""
    resp = requests.get(
        f"{url}/loki/api/v1/labels",
        headers=headers,
        auth=auth,
        verify=verify,
    )
    resp.raise_for_status()
    return resp.json()["data"]


def get_label_values(
    url: str,
    label: str,
    headers: dict[str, str] | None = None,
    auth: tuple[str, str] | None = None,
    verify: bool | str = True,  # False to skip TLS, or path to CA bundle
) -> list[str]:
    """List values for a specific label."""
    resp = requests.get(
        f"{url}/loki/api/v1/label/{label}/values",
        headers=headers,
        auth=auth,
        verify=verify,
    )
    resp.raise_for_status()
    return resp.json()["data"]


labels = get_labels("http://localhost:3100")
print(f"Labels: {labels}")

for label in labels:
    values = get_label_values("http://localhost:3100", label)
    print(f"  {label}: {values}")
```

## Handling errors

Loki returns standard HTTP status codes. Common errors include:

| Status | Meaning | Typical cause |
|--------|---------|---------------|
| 400 | Bad Request | Invalid LogQL syntax |
| 429 | Too Many Requests | Rate limit exceeded |
| 5xx | Server Error | Loki is unavailable or overloaded |

Use `raise_for_status()` to catch errors, and inspect the response body for details:

```python
import time
import requests


def query_with_retry(
    url: str,
    query: str,
    max_retries: int = 3,
    backoff: float = 1.0,
    headers: dict[str, str] | None = None,
    auth: tuple[str, str] | None = None,
    verify: bool | str = True,  # False to skip TLS, or path to CA bundle
) -> dict:
    """Query Loki with simple retry logic for rate limits."""
    for attempt in range(max_retries):
        resp = requests.get(
            f"{url}/loki/api/v1/query",
            params={"query": query},
            headers=headers,
            auth=auth,
            verify=verify,
        )
        if resp.status_code == 429:
            wait = backoff * (2 ** attempt)
            print(f"Rate limited, retrying in {wait}s...")
            time.sleep(wait)
            continue
        resp.raise_for_status()
        return resp.json()
    raise Exception(f"Query failed after {max_retries} retries")
```

## Common problems

- **Timestamps are in nanoseconds.** Loki expects Unix timestamps in nanoseconds, not seconds or milliseconds. Multiply `time.time()` by `1e9` and convert to a string.
- **At least one label matcher is required.** You cannot query without a stream selector. `{job="myapp"}` works; an empty selector does not.
- **The `direction` parameter changes result ordering.** Use `backward` (the default) to get the most recent entries first, or `forward` to get the oldest entries first.
- **The instant query endpoint only supports metric queries.** Log stream selectors like `{job="myapp"}` return a 400 error on `/query`. Use `/query_range` for log queries, and `/query` for aggregations like `rate()` or `count_over_time()`.
- **Use the `limit` parameter to control result size.** The default is 100 entries. For large time ranges, set a higher limit or paginate by adjusting the `start` parameter based on the last received timestamp.

# TSDB index format v2

Version 2 is Loki's initial TSDB index format. It is written for periods with schema versions `v9` through `v12` and is the closest to the Prometheus TSDB index it was derived from. See [TSDB index format](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/) for the encoding notation used on this page.

## File layout

```
+-----------------------------------------------------------+
| magic(0xBAAAD700) <4b> | version(2) <1b>                  |
+-----------------------------------------------------------+
| symbol table              (TOC.symbols)                   |
+-----------------------------------------------------------+
| series                    (TOC.series)                    |
+-----------------------------------------------------------+
| label indices             (TOC.label_indices)             |
+-----------------------------------------------------------+
| postings                  (TOC.postings)                  |
+-----------------------------------------------------------+
| label offset table        (TOC.label_offset_table)        |
+-----------------------------------------------------------+
| postings offset table     (TOC.postings_offset_table)     |
+-----------------------------------------------------------+
| fingerprint offsets table (TOC.fingerprint_offsets)       |
+-----------------------------------------------------------+
| TOC                       (last 76 bytes of the file)     |
+-----------------------------------------------------------+
```

## Symbol table

All label names and label values are deduplicated into a lexicographically sorted symbol table. Series reference a symbol by its ordinal position in this table, which keeps series entries small. The index decoder keeps the file offset of every 32nd symbol in memory and scans forward from there.

```
+-----------------------------------------------------------+
| len <4b>                                                  |
+-----------------------------------------------------------+
| #symbols <4b>                                             |
+-----------------------------------------------------------+
| symbol_0 <uvarint_str>                                    |
+-----------------------------------------------------------+
| ...                                                       |
+-----------------------------------------------------------+
| symbol_n-1 <uvarint_str>                                  |
+-----------------------------------------------------------+
| CRC32 <4b>                                                |
+-----------------------------------------------------------+
```

## Series

Series are ordered by fingerprint, then by label set. Each entry starts on a 16-byte boundary, and the series reference used by the postings is the entry offset divided by 16.

```
+-----------------------------------------------------------+
| len(entry) <uvarint>                                      |
+-----------------------------------------------------------+
| fingerprint <8b>                                          |
+-----------------------------------------------------------+
| #labels <uvarint>                                         |
+-----------------------------+-----------------------------+
| ref(name_0) <uvarint>       | ref(value_0) <uvarint>      |
+-----------------------------+-----------------------------+
| ...                         | ...                         |
+-----------------------------+-----------------------------+
| ref(name_k-1) <uvarint>     | ref(value_k-1) <uvarint>    |
+-----------------------------+-----------------------------+
| chunks                                                    |
+-----------------------------------------------------------+
| CRC32 <4b>                                                |
+-----------------------------------------------------------+
```

The stored `fingerprint` is not necessarily `hash(labels)`. Multi-tenant indices carry a synthetic `__loki_tenant__` label whose fingerprint must match the single-tenant fingerprint of the same stream, so the writer takes the fingerprint as an explicit argument.

## Chunks

Chunk metadata entries are sorted by `(min_time, max_time, checksum)` and stored inline in the series entry. `min_time` is delta encoded against the `max_time` of the previous chunk, so it is written as a signed `varint` because chunks may overlap. The first chunk's delta is relative to 0.

```
+-----------------------------------------------------------+
| #chunks <uvarint>                                         |
+-----------------------------------------------------------+
| chunk_0:                                                  |
|   min_time delta <varint>       (vs. previous max_time)   |
|   max_time - min_time <uvarint>                           |
|   KB <uvarint>                  (size, rounded to KB)     |
|   #entries <uvarint>                                      |
|   checksum <4b>                 (chunk identity)          |
+-----------------------------------------------------------+
| ...                                                       |
+-----------------------------------------------------------+
| chunk_n-1: (same fields)                                  |
+-----------------------------------------------------------+
```

`KB` and `#entries` are Loki additions used by the query frontend to plan [dynamic query sharding](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/tsdb/#dynamic-query-sharding). Because the metas form a single delta chain, computing statistics for a time range requires decoding chunks from the start of the chain.

## Label indices

One entry per label name, holding the symbol references of all values seen for that name. Entries are 4-byte aligned.

```
+-----------------------------------------------------------+
| len <4b>                                                  |
+-----------------------------------------------------------+
| #names <4b>                     (always 1)                |
+-----------------------------------------------------------+
| #entries <4b>                                             |
+-----------------------------------------------------------+
| ref(value_0) <4b>                                         |
+-----------------------------------------------------------+
| ...                                                       |
+-----------------------------------------------------------+
| CRC32 <4b>                                                |
+-----------------------------------------------------------+
```

## Postings

One sorted list of series references per label name/value pair, 4-byte aligned. The pair `("", "")` holds all series in the index.

```
+-----------------------------------------------------------+
| len <4b>                                                  |
+-----------------------------------------------------------+
| #entries <4b>                                             |
+-----------------------------------------------------------+
| series_ref_0 <4b>                                         |
+-----------------------------------------------------------+
| ...                                                       |
+-----------------------------------------------------------+
| CRC32 <4b>                                                |
+-----------------------------------------------------------+
```

## Label offset table

Maps a label name to the file offset of its label index entry.

```
+-----------------------------------------------------------+
| len <4b>                                                  |
+-----------------------------------------------------------+
| #entries <4b>                                             |
+-----------------------------------------------------------+
| entry_0:                                                  |
|   #names <uvarint>              (always 1)                |
|   name <uvarint_str>                                      |
|   offset <uvarint>                                        |
+-----------------------------------------------------------+
| ...                                                       |
+-----------------------------------------------------------+
| CRC32 <4b>                                                |
+-----------------------------------------------------------+
```

## Postings offset table

Maps a label name/value pair to the file offset of its postings list.

```
+-----------------------------------------------------------+
| len <4b>                                                  |
+-----------------------------------------------------------+
| #entries <4b>                                             |
+-----------------------------------------------------------+
| entry_0:                                                  |
|   #keys <uvarint>               (always 2)                |
|   name <uvarint_str>                                      |
|   value <uvarint_str>                                     |
|   offset <uvarint>                                        |
+-----------------------------------------------------------+
| ...                                                       |
+-----------------------------------------------------------+
| CRC32 <4b>                                                |
+-----------------------------------------------------------+
```

## Fingerprint offsets table

A Loki-specific sampled index from fingerprint to series reference, recorded for every 1024th series. It lets a querier seek directly into the series section for a fingerprint range instead of walking the postings, which is what makes fingerprint-bounded shard queries cheap.

```
+-----------------------------------------------------------+
| len <4b>                                                  |
+-----------------------------------------------------------+
| #entries <4b>                                             |
+-----------------------------+-----------------------------+
| series_ref_0 <8b>           | fingerprint_0 <8b>          |
+-----------------------------+-----------------------------+
| ...                         | ...                         |
+-----------------------------+-----------------------------+
| CRC32 <4b>                                                |
+-----------------------------------------------------------+
```

## TOC

The table of contents occupies the last 76 bytes of the file. Readers parse it first and then address every other section by offset. An offset of 0 means the section is empty.

```
+-----------------------------------------------------------+
| symbols <8b>                                              |
+-----------------------------------------------------------+
| series <8b>                                               |
+-----------------------------------------------------------+
| label_indices <8b>                                        |
+-----------------------------------------------------------+
| label_offset_table <8b>                                   |
+-----------------------------------------------------------+
| postings <8b>                                             |
+-----------------------------------------------------------+
| postings_offset_table <8b>                                |
+-----------------------------------------------------------+
| fingerprint_offsets <8b>                                  |
+-----------------------------------------------------------+
| metadata.from <8b>              (oldest chunk min_time)   |
+-----------------------------------------------------------+
| metadata.through <8b>           (newest chunk max_time)   |
+-----------------------------------------------------------+
| CRC32 <4b>                                                |
+-----------------------------------------------------------+
```

`metadata.from` and `metadata.through` are Loki additions. They expose the time range covered by the index file, so a querier can discard an entire index without reading any of its sections.

## Differences from the Prometheus index

- Series entries store an explicit 8-byte fingerprint.
- Chunk metas store `KB`, `#entries`, and the chunk checksum instead of a chunk file reference, because Loki chunks are addressed in object storage by series fingerprint and checksum.
- The index adds a fingerprint offsets table and the `from`/`through` metadata in the TOC.

# TSDB index format v3

Version 3 is written for periods with schema version `v13`. It is identical to [version 2](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v2/) except for the chunks part of a series entry, which gains page markers so that readers can skip over chunks that fall outside the queried time range. See [TSDB index format](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/) for the encoding notation used on this page.

## Differences from v2

- The chunks part of a series entry is prefixed with a markers section: `len(markers)`, `#markers`, and one marker per page of 16 chunks.
- Chunk statistics for a time range can be answered from the markers, without decoding individual chunk metas.

## File layout

```
+-----------------------------------------------------------+
| magic(0xBAAAD700) <4b> | version(3) <1b>                  |
+-----------------------------------------------------------+
| symbol table              (TOC.symbols)                   |
+-----------------------------------------------------------+
| series                    (TOC.series)                    |
+-----------------------------------------------------------+
| label indices             (TOC.label_indices)             |
+-----------------------------------------------------------+
| postings                  (TOC.postings)                  |
+-----------------------------------------------------------+
| label offset table        (TOC.label_offset_table)        |
+-----------------------------------------------------------+
| postings offset table     (TOC.postings_offset_table)     |
+-----------------------------------------------------------+
| fingerprint offsets table (TOC.fingerprint_offsets)       |
+-----------------------------------------------------------+
| TOC                       (last 76 bytes of the file)     |
+-----------------------------------------------------------+
```

## Symbol table

Unchanged from v2: a lexicographically sorted, deduplicated table of all label names and values, referenced by ordinal position.

```
+-----------------------------------------------------------+
| len <4b>                                                  |
+-----------------------------------------------------------+
| #symbols <4b>                                             |
+-----------------------------------------------------------+
| symbol_0 <uvarint_str>                                    |
+-----------------------------------------------------------+
| ...                                                       |
+-----------------------------------------------------------+
| CRC32 <4b>                                                |
+-----------------------------------------------------------+
```

## Series

The series header is unchanged from v2. Entries start on a 16-byte boundary and the series reference is the entry offset divided by 16.

```
+-----------------------------------------------------------+
| len(entry) <uvarint>                                      |
+-----------------------------------------------------------+
| fingerprint <8b>                                          |
+-----------------------------------------------------------+
| #labels <uvarint>                                         |
+-----------------------------+-----------------------------+
| ref(name_0) <uvarint>       | ref(value_0) <uvarint>      |
+-----------------------------+-----------------------------+
| ...                         | ...                         |
+-----------------------------+-----------------------------+
| chunks                          (see below, v3 layout)    |
+-----------------------------------------------------------+
| CRC32 <4b>                                                |
+-----------------------------------------------------------+
```

## Chunks

The chunks part is split into a markers section and the chunk metas. Each marker summarizes a fixed-size page of chunks: its aggregated size and entry count, its time bounds, and the byte offset at which the page starts.

```
+-----------------------------------------------------------+
| #chunks <uvarint>                                         |
+-----------------------------------------------------------+
| len(markers) <4b>               (skip to chunk metas)     |
+-----------------------------------------------------------+
| #markers <uvarint>                                        |
+-----------------------------------------------------------+
| marker_0:                                                 |
|   #chunks in page <uvarint>                               |
|   KB <4b>                       (sum over page)           |
|   #entries <4b>                 (sum over page)           |
|   offset <uvarint>              (page start, relative to  |
|                                  the first chunk meta)    |
|   min_time <varint>             (page bounds)             |
|   max_time - min_time <varint>                            |
+-----------------------------------------------------------+
| ...                                                       |
+-----------------------------------------------------------+
| marker_m-1: (same fields, may cover a partial page)       |
+-----------------------------------------------------------+
| chunk_0:                                                  |
|   min_time delta <varint>       (vs. previous max_time)   |
|   max_time - min_time <uvarint>                           |
|   KB <uvarint>                                            |
|   #entries <uvarint>                                      |
|   checksum <4b>                                           |
+-----------------------------------------------------------+
| ...                                                       |
+-----------------------------------------------------------+
| chunk_n-1: (same fields)                                  |
+-----------------------------------------------------------+
```

The chunk meta encoding itself is byte-for-byte the same as in v2.

How readers use the markers:

- A page holds 16 chunks. The page size is recoverable from each marker's `#chunks in page` field, so it can be changed without a new index version.
- Series with fewer than 64 chunks skip the markers entirely, using `len(markers)`, because a linear scan is faster at low chunk counts.
- If a page is fully contained in the queried time range, the aggregated `KB` and `#entries` of its marker are used directly and no chunk meta is decoded. Partially overlapping pages are decoded chunk by chunk, and pages that start after the query end terminate the scan.
- The `min_time` delta chain of the chunk metas does not reset at page boundaries. When a reader jumps to a page via its `offset`, it discards the first chunk's delta and uses the page's `min_time` instead.

## Remaining sections

The label indices, postings, label offset table, postings offset table, fingerprint offsets table, and TOC are identical to v2. See:

- [Label indices](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v2/#label-indices)
- [Postings](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v2/#postings)
- [Label offset table](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v2/#label-offset-table)
- [Postings offset table](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v2/#postings-offset-table)
- [Fingerprint offsets table](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v2/#fingerprint-offsets-table)
- [TOC](../v2/#toc)

# TSDB index format v4

Version 4 is written for periods with schema version `v14`. This version is experimental. It keeps the paging layout of [version 3](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v3/) and appends an ingestion timestamp to every chunk meta, which allows retention to be measured from the time a chunk was ingested rather than from the timestamps of the log lines it contains. See [TSDB index format](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/) for the encoding notation used on this page.

## Differences from v3

- Each chunk meta ends with an `ingested_at delta` field, encoded at day precision relative to `max_time`.
- The field is 0 for all chunks that were not backfilled.

## File layout

```
+-----------------------------------------------------------+
| magic(0xBAAAD700) <4b> | version(4) <1b>                  |
+-----------------------------------------------------------+
| symbol table              (TOC.symbols)                   |
+-----------------------------------------------------------+
| series                    (TOC.series)                    |
+-----------------------------------------------------------+
| label indices             (TOC.label_indices)             |
+-----------------------------------------------------------+
| postings                  (TOC.postings)                  |
+-----------------------------------------------------------+
| label offset table        (TOC.label_offset_table)        |
+-----------------------------------------------------------+
| postings offset table     (TOC.postings_offset_table)     |
+-----------------------------------------------------------+
| fingerprint offsets table (TOC.fingerprint_offsets)       |
+-----------------------------------------------------------+
| TOC                       (last 76 bytes of the file)     |
+-----------------------------------------------------------+
```

## Symbol table

Unchanged from v2 and v3: a lexicographically sorted, deduplicated table of all label names and values, referenced by ordinal position.

```
+-----------------------------------------------------------+
| len <4b>                                                  |
+-----------------------------------------------------------+
| #symbols <4b>                                             |
+-----------------------------------------------------------+
| symbol_0 <uvarint_str>                                    |
+-----------------------------------------------------------+
| ...                                                       |
+-----------------------------------------------------------+
| CRC32 <4b>                                                |
+-----------------------------------------------------------+
```

## Series

The series header is unchanged. Entries start on a 16-byte boundary and the series reference is the entry offset divided by 16.

```
+-----------------------------------------------------------+
| len(entry) <uvarint>                                      |
+-----------------------------------------------------------+
| fingerprint <8b>                                          |
+-----------------------------------------------------------+
| #labels <uvarint>                                         |
+-----------------------------+-----------------------------+
| ref(name_0) <uvarint>       | ref(value_0) <uvarint>      |
+-----------------------------+-----------------------------+
| ...                         | ...                         |
+-----------------------------+-----------------------------+
| chunks                          (see below, v4 layout)    |
+-----------------------------------------------------------+
| CRC32 <4b>                                                |
+-----------------------------------------------------------+
```

## Chunks

The markers section is identical to v3. Each chunk meta carries one additional trailing field, `ingested_at delta`.

```
+-----------------------------------------------------------+
| #chunks <uvarint>                                         |
+-----------------------------------------------------------+
| len(markers) <4b>               (skip to chunk metas)     |
+-----------------------------------------------------------+
| #markers <uvarint>                                        |
+-----------------------------------------------------------+
| marker_0:                                                 |
|   #chunks in page <uvarint>                               |
|   KB <4b>                       (sum over page)           |
|   #entries <4b>                 (sum over page)           |
|   offset <uvarint>              (page start, relative to  |
|                                  the first chunk meta)    |
|   min_time <varint>             (page bounds)             |
|   max_time - min_time <varint>                            |
+-----------------------------------------------------------+
| ...                                                       |
+-----------------------------------------------------------+
| marker_m-1: (same fields, may cover a partial page)       |
+-----------------------------------------------------------+
| chunk_0:                                                  |
|   min_time delta <varint>       (vs. previous max_time)   |
|   max_time - min_time <uvarint>                           |
|   KB <uvarint>                                            |
|   #entries <uvarint>                                      |
|   checksum <4b>                                           |
|   ingested_at delta <uvarint>   (v4 only, see below)      |
+-----------------------------------------------------------+
| ...                                                       |
+-----------------------------------------------------------+
| chunk_n-1: (same fields)                                  |
+-----------------------------------------------------------+
```

Because the field is appended to the chunk meta, a v4 chunk meta cannot be read by a v3 reader. The index version in the header selects the decoder, so a cluster only needs to know that indices written under schema `v14` cannot be read by a version of Loki that predates v4 support.

### Ingestion timestamp encoding

`ingested_at` records when a chunk was flushed to storage, not when its log lines were received. It is only recorded for backfilled chunks, that is, chunks of a stream carrying the `__backfill__="true"` label. It is stored at day precision, relative to the chunk's `max_time`:

```
day               = 86400000 ms
delta_days        = ceil_day(ingested_at) - floor_day(max_time)
ingested_at delta = zigzag(delta_days) + 1
```

- The value 0 is a sentinel meaning "no ingestion timestamp". Every non-backfilled chunk stores 0, so the field costs a single byte for the common case.
- `ingested_at` is rounded up to the next UTC day boundary. Rounding must never move the timestamp earlier, otherwise retention measured from ingestion could expire a chunk before its window elapsed. The cost is retaining a chunk for up to one day longer.
- The zigzag mapping keeps small negative deltas cheap: 0 days encodes as 0, -1 day as 1, +1 day as 2. Adding 1 keeps the encoded 0 free for the sentinel.
- Rounding is stable. Re-encoding an already rounded `ingested_at` yields the same delta, so compaction does not drift the value.

For example, if `max_time` is 2026-01-10 14:30 UTC and the chunk was ingested at 2026-01-12 08:00 UTC, ingestion rounds up to 2026-01-13 00:00 UTC, the day of `max_time` is 2026-01-10, and the stored delta is +3 days.

## Remaining sections

The label indices, postings, label offset table, postings offset table, fingerprint offsets table, and TOC are identical to v2 and v3. See:

- [Label indices](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v2/#label-indices)
- [Postings](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v2/#postings)
- [Label offset table](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v2/#label-offset-table)
- [Postings offset table](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v2/#postings-offset-table)
- [Fingerprint offsets table](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v2/#fingerprint-offsets-table)
- [TOC](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/tsdb-index-format/v2/#toc)

# Release Notes

# Release cadence

## Stable Releases

Loki releases (this includes [Promtail](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/promtail/), [Loki Canary](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/loki-canary/), etc.) use the following
naming scheme: `MAJOR`.`MINOR`.`PATCH`.

- `MAJOR` (roughly once a year): these releases include large new features and possible backwards-compatibility breaks.
- `MINOR` (roughly once a quarter): these releases include new features which generally do not break backwards-compatibility, but from time to time we might introduce _minor_ breaking changes, and we will specify these in our upgrade docs.
- `PATCH` (roughly once or twice a month): these releases include bug and security fixes which do not break backwards-compatibility.

{{< admonition type="note" >}}
While our naming scheme resembles [Semantic Versioning](https://semver.org/), at this time we do not strictly follow its
guidelines to the letter. Our goal is to provide regular releases that are as stable as possible, and we take backwards-compatibility
seriously. As with any software, always read the [release notes](https://grafana.com/docs/loki/<LOKI_VERSION>/release-notes/) and the [upgrade guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/) whenever
choosing a new version of Loki to install.
{{< /admonition >}}

New releases are based of a [weekly release](#weekly-releases) which we have vetted for stability over a number of weeks.

We strongly recommend keeping up-to-date with patch releases as they are released. We post updates of new releases in the `#loki` channel
of our [Slack community](https://grafana.com/docs/loki/<LOKI_VERSION>/community/getting-in-touch/).

You can find all of our releases [on GitHub](https://github.com/grafana/loki/releases) and on [Docker Hub](https://hub.docker.com/r/grafana/loki).

## Weekly Releases

Every Monday morning, we create a new "weekly" release from the tip of the [`main` branch](https://github.com/grafana/loki).
These releases use the following naming scheme:

<ul>
<code>weekly-k&lt;week-number&gt;</code> where <code>&lt;week-number&gt;</code> is the number of weeks since we began this process (2020-07-06).
</ul>

These weekly releases are deployed across our Grafana Cloud Logs fleet of instances. We test these releases for stability
by deploying them through development, pre-production, and production instances.

Generally these weekly releases are considered stable enough to run, but we provide zero stability guarantees and these
releases _should not be run in production_ unless you are willing to tolerate some risk.

You can find these releases on [Docker Hub](https://hub.docker.com/r/grafana/loki/tags?page=1&name=k).

### Which release will my merged PR be part of?

Once your PR is merged to `main`, you can expect it to become available in the next week's
[weekly release](#weekly-releases). To find out which stable or weekly releases a commit is included in, use the following tool:

`tools/which-release.sh`

For example, [this PR](https://github.com/grafana/loki/pull/7472) was [merged](https://github.com/grafana/loki/pull/7472#event-8431624850) into the commit named `d434e80`. Using the tool above, we can see that is part of release 2.8 and several weekly releases:

```bash
$ ./tools/which-release.sh d434e80                                 
Commit was found in the following releases:
  release-2.8.x
Commit was found in the following weekly builds:
  k136
  k137
  k138
  k139
  k140
  k141
  k142
```

# V?.?

Grafana Labs is excited to announce the release of Loki ?.?.? Here's a summary of new enhancements and important fixes:

:warning: This a placeholder for the next release. Clean up all features listed below

## Features and enhancements

## Upgrade Considerations

## Bug fixes

# v3.0

Grafana Labs and the Loki team are excited to announce the release of Loki 3.0. Here's a summary of new enhancements and important fixes.

For a full list of all changes and fixes, refer to the [CHANGELOG](https://github.com/grafana/loki/blob/release-3.0.x/CHANGELOG.md).

## Features and enhancements

{{< admonition type="note" >}}
Note that Loki 3.0 defaults to using the v13 schema. All of the latest features are built against TSDB and the v13 Schema. This version of the schema is compatible with both Loki 2.9.x and Loki 3.0. The main change is to add support for Structured Metadata which is used by the new OTLP native endpoint and is enabled by default.
{{< /admonition >}}

Key features in Loki 3.0.0 include the following:

- **Query acceleration with Bloom filters** (experimental): This is designed to speed up filter queries, with best results for queries that are looking for a specific text string like an error message or UUID. For more information, refer to [Query acceleration with Blooms](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/query-acceleration-blooms/).

- **Native OpenTelemetry Support**: A simplified ingestion pipeline (Loki Exporter no longer needed) and a more intuitive query experience for OTel logs. For more information, refer to the [OTel documentation](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/otel/).

- **Helm charts**: A major upgrade to the Loki helm chart introduces support for `Distributed` mode (also known as [microservices](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/#microservices-mode) mode), includes memcached by default, and includes several updates to configurations to improve Loki operations.

- **Pattern match filter**: LogQL now supports two new [pattern match filter operators](https://grafana.com/docs/loki/<LOKI_VERSION>/query/#pattern-match-filter-operators). You can match any word with just one control character and it is simpler and 10x faster than using regex.

- **Caching updates**: This release includes multiple updates to caching to improve performance, add new configuration options and support for new features, deprecate features no longer needed, and add automatic background checks.

- **Lambda/Promtail:** Support dropping labels ([#10755](https://github.com/grafana/loki/issues/10755)) ([ec54c72](https://github.com/grafana/loki/commit/ec54c723ebbeeda88000dde188d539ecfe05dad8)).

- **Profiling integration**: Added profiling integrations to tracing instrumentation to allow getting a profile for a single request.

- **Docs improvements**: All the Getting Started topics have been revised, including a new [Quickstart](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/quick-start/) to help new users get up and running with Loki faster. The Storage, Configuration Reference, and API documentation have been updated to reflect deprecated and removed code, configuration options, and API endpoints.

## Deprecations

One of the focuses of Loki 3.0 was cleaning up unused code and old features that had been previously deprecated but not removed. Loki 3.0 removes a number of previous deprecations and introduces some new deprecations. Some of the main areas with changes include:

- [Deprecated storage options](https://grafana.com/docs/loki/<LOKI_VERSION>/storage/) including the deprecation of the BoltDB store.

- [Deprecated configuration options](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/).

- [API endpoint deprecations](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/api/#deprecated-endpoints).

To learn more about breaking changes in this release, refer to the [Upgrade guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

{{< docs/shared source="alloy" lookup="agent-deprecation.md" version="next" >}}

## Upgrade Considerations

The path from 2.9 to 3.0 includes several breaking changes. For important upgrade guidance, refer to the [Upgrade Guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/) and the separate [Helm Upgrade Guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/upgrade-helm-to-6.x/).

## Bug fixes

### 3.0.1 (2024-08-09)

- **deps:** Bumped dependencies versions to resolve CVEs ([#13833](https://github.com/grafana/loki/pull/13833)) ([e13011d](https://github.com/grafana/loki/commit/e13011d91a77501ca4f659df9cf33f23085d3a35)).
- Fixed nil pointer dereference in bloomstore initialization ([#12869](https://github.com/grafana/loki/issues/12869)) ([167b468](https://github.com/grafana/loki/commit/167b468598bc70bbed6eed44826d3f9b85e1e0b8)), closes [#12270](https://github.com/grafana/loki/issues/12270).

### 3.0.0 (2024-04-08)

- All lifecycler configurations reference a valid IPv6 address and port combination ([#11121](https://github.com/grafana/loki/issues/11121)) ([6385b19](https://github.com/grafana/loki/commit/6385b195739bd7d4e9706faddd0de663d8e5331a)).
- **deps:** Update github.com/c2h5oh/datasize digest to 859f65c (main) ([#10820](https://github.com/grafana/loki/issues/10820)) ([c66ffd1](https://github.com/grafana/loki/commit/c66ffd125cd89f5845a75a1751186fa46d003f70)).
- **deps:** Update github.com/docker/go-plugins-helpers digest to 6eecb7b (main) ([#10826](https://github.com/grafana/loki/issues/10826)) ([fb9c496](https://github.com/grafana/loki/commit/fb9c496b21be62f56866ae0f92440085e7860a2a)).
- **deps:** Update github.com/grafana/gomemcache digest to 6947259 (main) ([#10836](https://github.com/grafana/loki/issues/10836)) ([2327789](https://github.com/grafana/loki/commit/2327789b5506d0ccc00d931195da17a2d47bf236)).
- **deps:** Update github.com/grafana/loki/pkg/push digest to 583aa28 (main) ([#10842](https://github.com/grafana/loki/issues/10842)) ([02d9418](https://github.com/grafana/loki/commit/02d9418270f4e615c1f78b0def635da7c0572ca4)).
- **deps:** Update github.com/grafana/loki/pkg/push digest to cfc4f0e (main) ([#10946](https://github.com/grafana/loki/issues/10946)) ([d27c4d2](https://github.com/grafana/loki/commit/d27c4d297dc6cce93ada98f16b962380ec933c6a)).
- **deps:** Update github.com/grafana/loki/pkg/push digest to e523809 (main) ([#11107](https://github.com/grafana/loki/issues/11107)) ([09cb9ae](https://github.com/grafana/loki/commit/09cb9ae76f4aef7dea477961c0c5424d7243bf2a)).
- **deps:** Update github.com/joncrlsn/dque digest to c2ef48c (main) ([#10947](https://github.com/grafana/loki/issues/10947)) ([1fe4885](https://github.com/grafana/loki/commit/1fe48858ae15b33646eedb85b05d6773a8bc5020)).
- **deps:** Update module google.golang.org/grpc [security] (main) ([#11031](https://github.com/grafana/loki/issues/11031)) ([0695424](https://github.com/grafana/loki/commit/0695424f7dd62435df3a9981276b40f3c5ef5641)).
- **helm:** Bump nginx-unprivilege to fix CVE ([#10754](https://github.com/grafana/loki/issues/10754)) ([dbf7dd4](https://github.com/grafana/loki/commit/dbf7dd4bac112a538a59907a8c6092504e7f4a91)).
- **helm:** Sidecar configuration for Backend ([#10603](https://github.com/grafana/loki/issues/10603)) ([c29ba97](https://github.com/grafana/loki/commit/c29ba973a0b5b7b59613d210b741d5a547ea0e83)).
- **lambda-promtail** Fix panic in lambda-promtail due to mishandling of empty DROP_LABELS env var. ([#11074](https://github.com/grafana/loki/pull/11074)).
- **loki:** Respect query matcher in ingester when getting label values ([#10375](https://github.com/grafana/loki/issues/10375)) ([85e2e52](https://github.com/grafana/loki/commit/85e2e52279ecac6dc111d5c113c54d6054d2c922)).
- **loki** Generate tsdb_shipper storage_config even if using_boltdb_shipper is false ([#11195](https://github.com/grafana/loki/pull/11195)).
- **loki** Do not reflect label names in request metrics' "route" label. ([11551](https://github.com/grafana/loki/pull/11551)).
- **loki** Fix duplicate logs from docker containers. ([#11563](https://github.com/grafana/loki/pull/11563)).
- **loki** Ruler: Fixed a panic that can be caused by concurrent read-write access of tenant configs when there are a large amount of rules. ([#11601](https://github.com/grafana/loki/pull/11601)).
- **loki** Fixed regression adding newlines to HTTP error response bodies which may break client integrations. ([#11606](https://github.com/grafana/loki/pull/11606)).
- **loki** Log results cache: compose empty response based on the request being served to avoid returning incorrect limit or direction. ([#11657](https://github.com/grafana/loki/pull/11657)).
- **loki** Fix semantics of label parsing logic of metrics and logs queries. Both only parse the first label if multiple extractions into the same label are requested. ([#11587](https://github.com/grafana/loki/pull/11587)).
- **loki** Background Cache: Fixes a bug that is causing the background queue size to be incremented twice for each enqueued item. ([#11776](https://github.com/grafana/loki/pull/11776)).
- **loki**: Parsing: String array elements were not being parsed correctly in JSON processing ([#11921](https://github.com/grafana/loki/pull/11921)).
- **promtail:** Correctly parse list of drop stage sources from YAML ([#10848](https://github.com/grafana/loki/issues/10848)) ([f51ee84](https://github.com/grafana/loki/commit/f51ee849b03c5f6b79f3e93cb7fd7811636bede2)).
- **promtail:** Prevent panic due to duplicate metric registration after reloaded ([#10798](https://github.com/grafana/loki/issues/10798)) ([47e2c58](https://github.com/grafana/loki/commit/47e2c5884f443667e64764f3fc3948f8f11abbb8)).
- **promtail**: Fix Promtail excludepath not evaluated on newly added files. ([#9831](https://github.com/grafana/loki/pull/9831)).
- **tools/lambda-promtail:** Do not evaluate empty string for drop_labels ([#11074](https://github.com/grafana/loki/issues/11074)) ([94169a0](https://github.com/grafana/loki/commit/94169a0e6b5bf96426ad21e40f9583b721f35d6c)).

# v3.1

Grafana Labs and the Loki team are excited to announce the release of Loki 3.1. Here's a summary of new enhancements and important fixes.

For a full list of all changes and fixes, refer to the [CHANGELOG](https://github.com/grafana/loki/blob/release-3.1.x/CHANGELOG.md).

## Features and enhancements

Key features in Loki 3.1.0 include the following:

- **Query acceleration with Bloom filters** (experimental): Continued work on improving the new Bloom filters feature introduced in Loki 3.0. This is designed to speed up filter queries, with best results for queries that are looking for a specific text string like an error message or UUID. For more information, refer to [Query acceleration with Blooms](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/query-acceleration-blooms/).

- **Helm:** Update Helm chart to support distributed mode and 3.0 ([#12067](https://github.com/grafana/loki/issues/12067)) ([79b876b](https://github.com/grafana/loki/commit/79b876b65d55c54f4d532e98dc24743dea8bedec)). Add pattern ingester support in SSD mode ([#12685](https://github.com/grafana/loki/issues/12685)) ([19bfef4](https://github.com/grafana/loki/commit/19bfef48cbad57468591e8214c4a5f390091f1e1)). Enable log volume endpoint by default in Helm ([#12690](https://github.com/grafana/loki/issues/12690)) ([e39677f](https://github.com/grafana/loki/commit/e39677f97b4ba27c90d9f8d2991441095e55b06e)). Allow extraObject items as multiline strings ([#12397](https://github.com/grafana/loki/issues/12397)) ([af5be90](https://github.com/grafana/loki/commit/af5be900764acfe4bff54ceef164a4f660990f8a)).Support for PVC Annotations for Non-Distributed Modes ([#12023](https://github.com/grafana/loki/issues/12023)) ([efdae3d](https://github.com/grafana/loki/commit/efdae3df14c47d627eb99e91466e0451db6e16f6)).

- **LogQL:** Support negative numbers in LogQL ([#13091](https://github.com/grafana/loki/issues/13091)) ([6df81db](https://github.com/grafana/loki/commit/6df81db978b0157ab96fa0629a311f919dad1e8a)). Improve performance of `first_over_time` and `last_over_time` queries by sharding them ([#11605](https://github.com/grafana/loki/issues/11605)) ([f66172e](https://github.com/grafana/loki/commit/f66172eed17f9418ab22615537c7b65b09de96e5)). Improve syntax parser for pattern ([#12489](https://github.com/grafana/loki/issues/12489)) ([48dae44](https://github.com/grafana/loki/commit/48dae4417cca75a40d6a3bf16b0d976714e8db81)).

- **Loki:** Add ability to disable AWS S3 dual stack endpoints usage ([#13795](https://github.com/grafana/loki/issues/13795)) ([464ac73](https://github.com/grafana/loki/commit/464ac736a6fb70b673ee3cec21049b18d353cadb)).

- **lokitool:** Add `lokitool` to replace `cortextool`. ([#12166](https://github.com/grafana/loki/issues/12166)) ([7b7d3d4](https://github.com/grafana/loki/commit/7b7d3d4cd2c979c778d3741156f0d765a9e531b2)). Introduce `index audit` to `lokitool` ([#13008](https://github.com/grafana/loki/issues/13008)) ([47f0236](https://github.com/grafana/loki/commit/47f0236ea8f33a67a0a1abf6e6d6b3582661c4ba)).

- **Explore Logs:** Explore Logs, which lets you explore your Loki data without writing LogQL queries, is now available in public preview. If you are a Grafana Cloud user, you can access Explore Logs in the Grafana Cloud main navigation menu. If you are not a Grafana Cloud user, you can install the [Explore Logs plugin](https://grafana.com/docs/grafana-cloud/visualizations/simplified-exploration/logs/access/).  For more information, refer to the [Explore Logs documentation](https://grafana.com/docs/grafana-cloud/visualizations/simplified-exploration/logs/).

- **Docs:** Added a video to the Getting Started demo and updated for Grafana Alloy. Added an interactive sandbox to the Loki Quickstart tutorial. Updated the documentation for the SSD and microservices deployment modes using the Helm charts.  Documented the new meta-monitoring Helm chart.

Other improvements include the following:

- **API:** Add step param to Patterns Query API ([#12703](https://github.com/grafana/loki/issues/12703)) ([7b8533e](https://github.com/grafana/loki/commit/7b8533e435cf9d0466d3b147b2b3e0f6b3613fe9)).
- **API:** Expose optional label matcher for label names API ([#11982](https://github.com/grafana/loki/issues/11982)) ([8084259](https://github.com/grafana/loki/commit/808425953fa8a8eca3199b3664e43ceba362747a)).
- **cache:** Add `Cache-Control: no-cache` support for Loki instant queries. ([#12896](https://github.com/grafana/loki/issues/12896)) ([88e545f](https://github.com/grafana/loki/commit/88e545fc952d6ff55c61d079db920f00abc04865)).
- **canary:** Add test to check query results with and without cache. ([#13104](https://github.com/grafana/loki/issues/13104)) ([71507a2](https://github.com/grafana/loki/commit/71507a2b640ad071d88ee894e80235f93be73c3d)).
- **distributor:** Optimize log parsing performance by using unsafe package ([#13223](https://github.com/grafana/loki/issues/13223)) ([9f31b25](https://github.com/grafana/loki/commit/9f31b25253502f035cfb6a831bcea7f778f427dd)).
- Enable log volume endpoint by default ([#12628](https://github.com/grafana/loki/issues/12628)) ([397aa56](https://github.com/grafana/loki/commit/397aa56e157cbf733da548474a4bcae773e82362)).
- **ingester:** Add backoff to flush op ([#13140](https://github.com/grafana/loki/issues/13140)) ([9767807](https://github.com/grafana/loki/commit/9767807680cb4149c7b56345c531b62105a1b976)).
- **ingester:** Add ingester_chunks_flush_failures_total ([#12925](https://github.com/grafana/loki/issues/12925)) ([cc3694e](https://github.com/grafana/loki/commit/cc3694eecddaab579d08328cdab78a7d8a7bd720)).
- **ingester:** add profile tagging to ingester ([#13068](https://github.com/grafana/loki/issues/13068)) ([00d3c7a](https://github.com/grafana/loki/commit/00d3c7a52d9f2b48fccb0cd5b105a2577b3d0305)).
- **ingester:** add recalculateOwnedStreams to check stream ownership if the ring is changed ([#13103](https://github.com/grafana/loki/issues/13103)) ([e7689b2](https://github.com/grafana/loki/commit/e7689b248dbe549b2ac61a0e335d8b5b999cc47d)).
- **ingester:** Add profile tagging to ingester ([#13068](https://github.com/grafana/loki/issues/13068)) ([00d3c7a](https://github.com/grafana/loki/commit/00d3c7a52d9f2b48fccb0cd5b105a2577b3d0305)).
- **ingester:** Include the stream we failed to create in the stream limit error message ([#12437](https://github.com/grafana/loki/issues/12437)) ([ec81991](https://github.com/grafana/loki/commit/ec81991f4d7f6d83a34dffb073d60c330c69e94d)).
- **ingester:** New stream count limiter ([#13006](https://github.com/grafana/loki/issues/13006)) ([1111595](https://github.com/grafana/loki/commit/1111595179c77f9303ebdfd362f14b1ac50044cb)).
- **Lambda-Promtail:** Parameterise the MaximumEventAgeInSeconds, LogGroupName, and IAMRoleName for Lambda-Promtail CloudFormation template ([#12728](https://github.com/grafana/loki/issues/12728)) ([8892dc8](https://github.com/grafana/loki/commit/8892dc89231ebe7b05fc1c4e0b7647f328f9c1ce)).
- loki/main.go: Log which config file path is used on startup ([#12985](https://github.com/grafana/loki/issues/12985)) ([7a3338e](https://github.com/grafana/loki/commit/7a3338ead82e4c577652ab86e9a55faf200ac05a)).
- **metamonitoring:** Add a version of the mixin dashboards for meta monitoring ([#12700](https://github.com/grafana/loki/issues/12700)) ([ec1a057](https://github.com/grafana/loki/commit/ec1a057a323ed1bd8de448e714a672b64140b691)).
- **Promtail:** Added support to install wget on Promtail docker image to support docker healthcheck ([#11711](https://github.com/grafana/loki/issues/11711)) ([ffe684c](https://github.com/grafana/loki/commit/ffe684c330bcd65f9b07a02d6f93bb475106becc)).
- **Promtail:** Support of RFC3164 aka BSD Syslog ([#12810](https://github.com/grafana/loki/issues/12810)) ([be41525](https://github.com/grafana/loki/commit/be4152576e6d8cb280fd65604199db7157981f07)).
- **querier:** Split gRPC client into two. ([#12726](https://github.com/grafana/loki/issues/12726)) ([7b6f057](https://github.com/grafana/loki/commit/7b6f0577c3277b84230f0f2deba747b01ca2b2fa)).
- **reporting:** Report CPU usage ([#12970](https://github.com/grafana/loki/issues/12970)) ([87288d3](https://github.com/grafana/loki/commit/87288d37f9e9c1e90295bf785adbc4bfdb66fb30)).
- Split detected fields queries ([#12491](https://github.com/grafana/loki/issues/12491)) ([6c33809](https://github.com/grafana/loki/commit/6c33809015bef8078b17dcb6b0701e930132f042)).
- **TSDB:** Add fast-path to `inversePostingsForMatcher` ([#12679](https://github.com/grafana/loki/issues/12679)) ([402d1d7](https://github.com/grafana/loki/commit/402d1d7c48ab4eb77835f4ebb9ef7cabf1dd7449)).

## Deprecations

One of the focuses of Loki 3.0 was cleaning up unused code and old features that had been previously deprecated but not removed. Loki 3.0 removed a number of previous deprecations and introduces some new deprecations. Some of the main areas with changes include:

- [Deprecated storage options](https://grafana.com/docs/loki/<LOKI_VERSION>/storage/) including the deprecation of the BoltDB store.

- [Deprecated configuration options](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/).

- [API endpoint deprecations](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/api/#deprecated-endpoints).

To learn more about breaking changes in this release, refer to the [Upgrade guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

{{< docs/shared source="alloy" lookup="agent-deprecation.md" version="next" >}}

## Upgrade Considerations

The path from 2.9 to 3.0 included several breaking changes. For important upgrade guidance, refer to the [Upgrade Guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/) and the separate [Helm Upgrade Guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/upgrade-helm-to-6.x/).

- **BREAKING CHANGE** Update Helm chart to support distributed mode and 3.0 ([#12067](https://github.com/grafana/loki/issues/12067)).

{{< admonition type="important" >}}
Out of an abundance of caution, we advise that users with Loki or Grafana Enterprise Logs (GEL) deployments on AWS upgrade their Helm charts or change the names of their buckets, as outlined in the solutions and mitigations section of this [blog post](https://grafana.com/blog/2024/06/27/grafana-security-update-grafana-loki-and-unintended-data-write-attempts-to-amazon-s3-buckets/).
{{< /admonition >}}

## Bug fixes

### 3.1.2 (2024-10-17)

- **config:** Copy Alibaba and IBM object storage configuration from common ([#14316](https://github.com/grafana/loki/issues/14316)) ([7184d45](https://github.com/grafana/loki/commit/7184d45d8e080874feea8bfd223dedf5f20d3836)).
- **docker-compose:** upgrade loki and grafana production image tags to 3.1.1 ([#14025](https://github.com/grafana/loki/issues/14025)) ([36fe29e](https://github.com/grafana/loki/commit/36fe29eb334d8300265ca437c0acb423a01c5041)).
- **logql:** updated JSONExpressionParser not to unescape extracted values if it is JSON object. (backport release-3.1.x) ([#14503](https://github.com/grafana/loki/issues/14503)) ([759f9c8](https://github.com/grafana/loki/commit/759f9c8525227bb1272771a40429d12e015874d9)).
- **promtail:** Revert build image to Debian Bullseye to fix libc version issue in Promtail ([#14387](https://github.com/grafana/loki/issues/14387)) ([05b6a65](https://github.com/grafana/loki/commit/05b6a65f8bf00b880f17465553b1adaf0cf56d60)).
- **storage:** have GetObject check for canceled context (backport release-3.1.x) ([#14421](https://github.com/grafana/loki/issues/14421)) ([f3d69ff](https://github.com/grafana/loki/commit/f3d69ffa960c91c0239436a32bb0aa578c0f022a)).

### 3.1.1 (2024-08-08)

- **deps:** Bumped dependencies versions to resolve CVEs ([#13789](https://github.com/grafana/loki/issues/13789)) ([34206cd](https://github.com/grafana/loki/commit/34206cd2d6290566034710ae6c2d08af8804bc91)).

### 3.1.0 (2024-07-02)

- **blooms:** Fix duplicate enqueue item problem in bloom download queue when do sync download ([#13114](https://github.com/grafana/loki/issues/13114)) ([f98ff7f](https://github.com/grafana/loki/commit/f98ff7f58400b5f5a425fae003fb959bfb8c6454)).
- **blooms:** Add a missing `continue` in fuse which may cause incorrect bloom test result ([#12650](https://github.com/grafana/loki/issues/12650)) ([0d1ebeb](https://github.com/grafana/loki/commit/0d1ebebd3afe9504506aaed0b7827318eb2d9cfe)).
- **blooms:** Allow cluster label override in bloom dashboards ([#13012](https://github.com/grafana/loki/issues/13012)) ([987e551](https://github.com/grafana/loki/commit/987e551f9e21b9a612dd0b6a3e60503ce6fe13a8)).
- **blooms:** bloomshipper no longer returns empty metas on fetch ([#13130](https://github.com/grafana/loki/issues/13130)) ([ad279e5](https://github.com/grafana/loki/commit/ad279e518cb252ef7e26283ec16540846dbd3acf)).
- **blooms:** Clean block directories recursively on startup ([#12895](https://github.com/grafana/loki/issues/12895)) ([7b77e31](https://github.com/grafana/loki/commit/7b77e310982147162777f9febfbcd98ec8a8c383)).
- **blooms:** Correctly return unfiltered chunks for series that are not mapped to any block ([#12774](https://github.com/grafana/loki/issues/12774)) ([c36b114](https://github.com/grafana/loki/commit/c36b1142c7acd6a13a3634ddbef71254040cff73)).
- **blooms:** Deduplicate filtered series and chunks ([#12791](https://github.com/grafana/loki/issues/12791)) ([3bf2d1f](https://github.com/grafana/loki/commit/3bf2d1fea08593bdf10dc8a6827998a6d8a8243c)).
- **blooms:** Disable metas cache on bloom gateway ([#12959](https://github.com/grafana/loki/issues/12959)) ([00bdd2f](https://github.com/grafana/loki/commit/00bdd2f5b703991b280317ceff0fcf2eed1847d9)).
- **blooms:** Do not fail requests when fetching metas from cache fails ([#12838](https://github.com/grafana/loki/issues/12838)) ([667076d](https://github.com/grafana/loki/commit/667076d9359c56118f1149f31a94c8a44bc171c7)).
- **blooms:** Don't break iterator conventions ([#12808](https://github.com/grafana/loki/issues/12808)) ([1665e85](https://github.com/grafana/loki/commit/1665e853a0a6aa63f535bcc5a4bb67775723cc87)).
- **blooms:** Fix `partitionSeriesByDay` function ([#12900](https://github.com/grafana/loki/issues/12900)) ([738c274](https://github.com/grafana/loki/commit/738c274a5828aab4d88079c38400ddc705c0cb5d)).
- **blooms:** Fix a regression introduced with [#12774](https://github.com/grafana/loki/issues/12774) ([#12776](https://github.com/grafana/loki/issues/12776)) ([ecefb49](https://github.com/grafana/loki/commit/ecefb495084a59d25778af520041766e087598ba)).
- **blooms:** Fix findGaps when ownership goes to MaxUInt64 and that is covered by existing meta ([#12558](https://github.com/grafana/loki/issues/12558)) ([0ee2a61](https://github.com/grafana/loki/commit/0ee2a6126ae40a1d666f500c19efd639763f1bae)).
- **blooms:** Fully deduplicate chunks from FilterChunkRef responses ([#12807](https://github.com/grafana/loki/issues/12807)) ([a0f358f](https://github.com/grafana/loki/commit/a0f358fcc8295d93ee38b67738e8d90045c50dab)).
- **blooms:** Handle not found metas gracefully ([#12853](https://github.com/grafana/loki/issues/12853)) ([37c8822](https://github.com/grafana/loki/commit/37c88220b3a7f8268c48f1bf37f4eb11cdba1b5f)).
- **blooms:** Reset error on LazyBloomIter.Seek ([#12806](https://github.com/grafana/loki/issues/12806)) ([76ba24e](https://github.com/grafana/loki/commit/76ba24e3d8ce5e3c872442ce9d64505605ef0f53)).
- **blooms:** Defer closing blocks iter after checking error from loadWorkForGap ([#12934](https://github.com/grafana/loki/issues/12934)) ([cb1f5d9](https://github.com/grafana/loki/commit/cb1f5d9fca2908bd31a3c6bef38d49fe084d2939)).
- **blooms:** Fix bloom deleter PR after merge ([#13167](https://github.com/grafana/loki/issues/13167)) ([c996349](https://github.com/grafana/loki/commit/c99634978cb189744946e6dc388f0cc4183e98f2)).
- **blooms:** Fix incorrect sorting of chunks in bloom-filtered response since `ChunkRef.Cmp` method is used in reverse ([#12999](https://github.com/grafana/loki/issues/12999)) ([670cd89](https://github.com/grafana/loki/commit/670cd89aa8ffb8b852bca05fd0adb554e93ce796)).
- **blooms:** Remove unused parameter causing lint error ([#12801](https://github.com/grafana/loki/issues/12801)) ([33e82ec](https://github.com/grafana/loki/commit/33e82ec133b133e79666f7eec7d8d69954aa2aa3)).
- `codec` not initialized in downstream roundtripper ([#12873](https://github.com/grafana/loki/issues/12873)) ([b6049f6](https://github.com/grafana/loki/commit/b6049f6792492d5753626e5845b0094199463966)).
- Change log level since this is a known case ([#13029](https://github.com/grafana/loki/issues/13029)) ([ca030a5](https://github.com/grafana/loki/commit/ca030a5c4335b0258e83aebd8779ea4d348003f3)).
- Close res body ([#12444](https://github.com/grafana/loki/issues/12444)) ([616977a](https://github.com/grafana/loki/commit/616977a942b63fb2ee7545e155abe246f6175308)).
- Correct initialization of a few slices ([#12674](https://github.com/grafana/loki/issues/12674)) ([0eba448](https://github.com/grafana/loki/commit/0eba448fc70b78ca7cd612831c9d3be116faa7a2)).
- **distributor:** add detected_level info when the info word appears on log message ([#13218](https://github.com/grafana/loki/issues/13218)) ([c9bfa3e](https://github.com/grafana/loki/commit/c9bfa3ebbf362b3d056879f0ef5f3e656f28c500)).
- **docs:** Broken link in getting started readme ([#12736](https://github.com/grafana/loki/issues/12736)) ([425a2d6](https://github.com/grafana/loki/commit/425a2d690c13592abf32f2ed2475676c3422ac51)).
- **docs:** Move Promtail configuration to the correct doc ([#12737](https://github.com/grafana/loki/issues/12737)) ([1161846](https://github.com/grafana/loki/commit/1161846e19105e2669a5b388998722c23bd0f2f4)).
- **docs:** Updated Loki OTLP Ingest Configuration ([#12648](https://github.com/grafana/loki/issues/12648)) ([ff88f3c](https://github.com/grafana/loki/commit/ff88f3c3088a235eef5153a9d6414c161797a180)).
- **Explore logs:** Make detected fields work for both json and proto ([#12682](https://github.com/grafana/loki/issues/12682)) ([f68d1f7](https://github.com/grafana/loki/commit/f68d1f7fafa1ec55e90d3a253ef2ee8bb9c2e342)).
- Fix errors reported by the race detector ([#13174](https://github.com/grafana/loki/issues/13174)) ([2b19dac](https://github.com/grafana/loki/commit/2b19dac82a97b1d75075eb87a4f7fdfed003c072)), closes [#8586](https://github.com/grafana/loki/issues/8586).
- Fix for how the loop sync is done ([#12941](https://github.com/grafana/loki/issues/12941)) ([5cd850e](https://github.com/grafana/loki/commit/5cd850e0d02151c6f9c6285189b887b4929cfa12)).
- Fix log level detection ([#12651](https://github.com/grafana/loki/issues/12651)) ([6904a65](https://github.com/grafana/loki/commit/6904a6520d3b5599404b339577c7c3311e635da9)).
- Fix setting of info log level when trying to detect level from log lines ([#12635](https://github.com/grafana/loki/issues/12635)) ([0831802](https://github.com/grafana/loki/commit/0831802a99243f9fe61f6cc8795739bf67e8d8e9)).
- **lokitool:** Fix the lokitool imports ([#12673](https://github.com/grafana/loki/issues/12673)) ([6dce988](https://github.com/grafana/loki/commit/6dce98870d8c5c7054b3444d2fe4e66dad262a53)).
- **Helm:** Fix indentation of query range values in Helm ([#12577](https://github.com/grafana/loki/issues/12577)) ([9823f20](https://github.com/grafana/loki/commit/9823f2030a294e6dc9c50d6f956a7691df5d53df)).
- **Helm:** Add missing Helm helper loki.hpa.apiVersion ([#12755](https://github.com/grafana/loki/issues/12755)) ([3070ea7](https://github.com/grafana/loki/commit/3070ea70bb05bffced6a8304f506b03ed4c8e2aa)).
- **Helm:** Add missing OTLP endpoint to nginx config ([#12709](https://github.com/grafana/loki/issues/12709)) ([8096748](https://github.com/grafana/loki/commit/8096748f1f205e766deab9438c4b2bc587facfc5)).
- **Helm:** Fixes read & backend replicas settings ([#12828](https://github.com/grafana/loki/issues/12828)) ([d751134](https://github.com/grafana/loki/commit/d7511343bcdfe77a6213599827ce0093b2949c18)).
- **Helm:** Set compactor address for distributed mode. ([#12748](https://github.com/grafana/loki/issues/12748)) ([521d40a](https://github.com/grafana/loki/commit/521d40a96a5c1c65c786c73ec374580fe767dd3b)).
- **Helm:** Fix GEL image tag, bucket name and proxy URLs ([#12878](https://github.com/grafana/loki/issues/12878)) ([67ed2f7](https://github.com/grafana/loki/commit/67ed2f7092c8c0d97ba0bec08fde7ede65faa33f)).
- **Helm:** Fix query-frontend and ruler targetPort 'http-metrics' in Service template ([#13024](https://github.com/grafana/loki/issues/13024)) ([1ab9d27](https://github.com/grafana/loki/commit/1ab9d271c354caf0ba589691e6477fb9a19039f0)).
- **Helm:** Fix queryScheduler servicemonitor ([#12753](https://github.com/grafana/loki/issues/12753)) ([8101e21](https://github.com/grafana/loki/commit/8101e21f9973b8261de0ee3eb34fa4d7b88ddaac)).
- **Helm:** Fixed ingress paths mapping ([#12932](https://github.com/grafana/loki/issues/12932)) ([5ada92b](https://github.com/grafana/loki/commit/5ada92b190c671055bb09ca2dd234b6bac49289e)).
- **Helm:** Only default bucket names when using MinIO ([#12548](https://github.com/grafana/loki/issues/12548)) ([2e32ec5](https://github.com/grafana/loki/commit/2e32ec52d8766c0a5a75be30585402f1dce52cc5)).
- **Helm:** Removed duplicate bucketNames from documentation and fixed key name `deploymentMode` ([#12641](https://github.com/grafana/loki/issues/12641)) ([0d8ff9e](https://github.com/grafana/loki/commit/0d8ff9ee7929b8facbdb469abe344c320d3bd5ce)).
- **Helm:** Ingester zoneAwareReplication ([#12659](https://github.com/grafana/loki/issues/12659)) ([9edb0ce](https://github.com/grafana/loki/commit/9edb0ce140c4fe716a62e81e0fce747d92954f4c)).
- **Helm:** Missing password for Loki-Canary when loki.auth_enabled is true ([#12411](https://github.com/grafana/loki/issues/12411)) ([68b23dc](https://github.com/grafana/loki/commit/68b23dc2b5c74b9175d5e24fb445748c422cb7b6)).
- **indexstats:** Do not collect stats from "IndexStats" lookups for other query types ([#12978](https://github.com/grafana/loki/issues/12978)) ([1f5291a](https://github.com/grafana/loki/commit/1f5291a4a3bd3c98c190d9a5dda32bbd78f18c3b)).
- **ingester:** Replace channel check with atomic bool in tailer.send() ([#12976](https://github.com/grafana/loki/issues/12976)) ([4a5edf1](https://github.com/grafana/loki/commit/4a5edf1a2af9e8af1842dc8d9b5482659d61031e)).
- **ingester:** Not owned stream count ([#13030](https://github.com/grafana/loki/issues/13030)) ([4901a5c](https://github.com/grafana/loki/commit/4901a5c452fa6822a645f56e20e704db9366182a)).
- **ingester:** Properly return http status codes from ingester to querier for RPC function calls ([#13134](https://github.com/grafana/loki/issues/13134)) ([691b174](https://github.com/grafana/loki/commit/691b1741386716095a4926cea5d5bb53caa88d9a)).
- **ingester:** Track bytes discarded by ingester. ([#12981](https://github.com/grafana/loki/issues/12981)) ([88c6711](https://github.com/grafana/loki/commit/88c671162f70e075f6aa43599aa560fe7b4b5627)).
- Introduce feature flag for [last|first]_over_time sharding. ([#13067](https://github.com/grafana/loki/issues/13067)) ([6e45550](https://github.com/grafana/loki/commit/6e4555010eab5a2b12caf9af2df5f0991362d754)).
- Invalidate caches when pipeline wrappers are disabled ([#12903](https://github.com/grafana/loki/issues/12903)) ([a772ed7](https://github.com/grafana/loki/commit/a772ed705c6506992cd1f2364b11fa60c1879f57)).
- **ksonnet:** Do not generate RBAC for consul if you are using memberlist ([#12688](https://github.com/grafana/loki/issues/12688)) ([2d62fca](https://github.com/grafana/loki/commit/2d62fca05d6ec82196b46c956733c89439660754)).
- **Lambda-Promtail:** Update s3 filename regex to allow finding of log files from AWS GovCloud regions ([#12482](https://github.com/grafana/loki/issues/12482)) ([7a81d26](https://github.com/grafana/loki/commit/7a81d264a4ba54efdb1d79d382fd4188c036aaee)).
- **Lambda-Promtail:** Remove Hardcoded Bucket Name from EventBridge Example CloudFormation Template ([#12609](https://github.com/grafana/loki/issues/12609)) ([8c18463](https://github.com/grafana/loki/commit/8c18463285f214ba5b0b9a127bbe0071a2ec7d69)).
- **LogQL:** Fix multiple or filters would get wrong filtertype ([#13169](https://github.com/grafana/loki/issues/13169)) ([9981e9e](https://github.com/grafana/loki/commit/9981e9e40d4eda1a88d1aee0483cec1c098b92c7)).
- Loki version prefix in Makefile ([#12514](https://github.com/grafana/loki/issues/12514)) ([dff72d2](https://github.com/grafana/loki/commit/dff72d2a52094fb2a831b5930cbfc67759b0978d)).
- Make the TSDB filenames correctly reproducible from the identifier ([#12536](https://github.com/grafana/loki/issues/12536)) ([ec888ec](https://github.com/grafana/loki/commit/ec888ec8a564c7a93937c785c0540e7d2bcde20e)).
- **metamonitoring:** Add missing parentheses in meta monitoring dashboards ([#12802](https://github.com/grafana/loki/issues/12802)) ([151d0a5](https://github.com/grafana/loki/commit/151d0a58ac9f5aa67f944e6729720f5f70d07e27)).
- **mixins:** loki-operational.libsonnet ([#12789](https://github.com/grafana/loki/issues/12789)) ([51a841f](https://github.com/grafana/loki/commit/51a841f20dbcbcb233836373ee246fb723ef70ba)).
- **mixins:** Incorrect compactor matcher in loki-deletion dashboard mixin ([#12567](https://github.com/grafana/loki/issues/12567)) ([006f88c](https://github.com/grafana/loki/commit/006f88cef19d4d1fe14a40287ccdf534f6975475)).
- **mixins:** Fix compactor matcher in the loki-deletion dashboard ([#12790](https://github.com/grafana/loki/issues/12790)) ([a03846b](https://github.com/grafana/loki/commit/a03846b4367cbb5a0aa445e539d92ae41e3f481a)).
- **mixin:** Mixin generation when cluster label is changed ([#12613](https://github.com/grafana/loki/issues/12613)) ([1ba7a30](https://github.com/grafana/loki/commit/1ba7a303566610363c0c36c87e7bc6bb492dfc93)).
- **mixin:** dashboards $__auto fix ([#12707](https://github.com/grafana/loki/issues/12707)) ([91ef72f](https://github.com/grafana/loki/commit/91ef72f742fe1f8621af15d8190c5c0d4d613ab9)).
- **mixins:**  Add missing log data source on loki-deletion ([#13011](https://github.com/grafana/loki/issues/13011)) ([1948899](https://github.com/grafana/loki/commit/1948899999107e7f27f4b9faace64942abcdb41f)).
- **mixins:** Align loki-writes mixins with loki-reads ([#13022](https://github.com/grafana/loki/issues/13022)) ([757b776](https://github.com/grafana/loki/commit/757b776de39bf0fc0c6d1dd74e4a245d7a99023a)).
- **mixins:** Remove unnecessary disk panels for SSD read path ([#13014](https://github.com/grafana/loki/issues/13014)) ([8d9fb68](https://github.com/grafana/loki/commit/8d9fb68ae5d4f26ddc2ae184a1cb6a3b2a2c2127)).
- **mixins:** Upgrade old plugin for the loki-operational dashboard. ([#13016](https://github.com/grafana/loki/issues/13016)) ([d3c9cec](https://github.com/grafana/loki/commit/d3c9cec22891b45ed1cb93a9eacc5dad6a117fc5)).
- **nix:** Lambda-Promtail vendor hash ([#12763](https://github.com/grafana/loki/issues/12763)) ([ae180d6](https://github.com/grafana/loki/commit/ae180d6e070946eb5359ecd63a9e01e02f160ce3)).
- **OTLP:** Fix parsing of default per tenant OTLP config ([#12836](https://github.com/grafana/loki/issues/12836)) ([7cc9a93](https://github.com/grafana/loki/commit/7cc9a9386a8f89dbec6a25435180ed4625ae6490)).
- **operator:** Add alertmanager client config to ruler template ([#13182](https://github.com/grafana/loki/issues/13182)) ([6148c37](https://github.com/grafana/loki/commit/6148c3760d701768e442186d4e7d574c7dc16c91)).
- **operator:** Bump golang builder to 1.21.9 ([#12503](https://github.com/grafana/loki/issues/12503)) ([f680ee0](https://github.com/grafana/loki/commit/f680ee0453d1b7d315774591293927b988bca223)).
- **operator:** Configure Loki to use virtual-host-style URLs for S3 AWS endpoints ([#12469](https://github.com/grafana/loki/issues/12469)) ([0084262](https://github.com/grafana/loki/commit/0084262269f4e2cb94d04e0cc0d40e9666177f06)).
- **operator:** Improve API documentation for schema version ([#13122](https://github.com/grafana/loki/issues/13122)) ([3a9f50f](https://github.com/grafana/loki/commit/3a9f50f5099a02e662b8ac10ddad0b36cd844161)).
- **operator:** Use a minimum value for replay memory ceiling ([#13066](https://github.com/grafana/loki/issues/13066)) ([4f3ed77](https://github.com/grafana/loki/commit/4f3ed77cb92c2ffd605743237e609c28f7841728)).
- Optimize regular initialization ([#12926](https://github.com/grafana/loki/issues/12926)) ([a46d14f](https://github.com/grafana/loki/commit/a46d14fb05ea14dd39095d2d71cd037acc2dfc51)).
- **OTel:** Map 500 errors to 503 ([#13173](https://github.com/grafana/loki/issues/13173)) ([b31e04e](https://github.com/grafana/loki/commit/b31e04e3f1b7424cc52b518dc974a382a25bf045)).
- **packaging:** Require online network in systemd unit file for Loki and Promtail ([#12741](https://github.com/grafana/loki/issues/12741)) ([57f78b5](https://github.com/grafana/loki/commit/57f78b574ac9aa16f8322fb0edc4c7f0ec3cf6fa)).
- Fix panics when ingester response is nil ([#12946](https://github.com/grafana/loki/issues/12946)) ([3cc28aa](https://github.com/grafana/loki/commit/3cc28aaf0ec08373fb104327827e6a062807e7ff)).
- **Promtail:** Promtail race fixes ([#12656](https://github.com/grafana/loki/issues/12656)) ([4e04d07](https://github.com/grafana/loki/commit/4e04d07168a8c5cb7086ced8486c6d584faa1045)).
- **Promtail:** Clean up metrics generated from logs after a config reload. ([#11882](https://github.com/grafana/loki/issues/11882)) ([39a7181](https://github.com/grafana/loki/commit/39a7181a600e9dc848dd3c0b0163c07242a46278)).
- **Promtail:** Fix bug with Promtail config reloading getting stuck indefinitely ([#12795](https://github.com/grafana/loki/issues/12795)) ([4d761ac](https://github.com/grafana/loki/commit/4d761acd85b90cbdcafdf8d2547f0db14f6ae4dd)).
- **Promtail:** Fix UDP receiver on syslog transport ([#10708](https://github.com/grafana/loki/issues/10708)) ([a00f1f1](https://github.com/grafana/loki/commit/a00f1f1b0b8f536f2cdac2f8857eb40c716aa696)).
- **Promtail:** Handle docker logs when a log is split in multiple frames ([#12374](https://github.com/grafana/loki/issues/12374)) ([c0113db](https://github.com/grafana/loki/commit/c0113db4e8c4647188db6477d2ab265eda8dbb6c)).  This changes the current Promtail behavior.  Loki has a default `max_line_size` of 256kb, but
With this fix, if the `max_line_size` config in Promtail has a value, the docker service discovery will now respect that configuration, otherwise it will use the Loki default of 256kb.
- **querier:** Add retry middleware to the "limited" query roundtripper ([#13161](https://github.com/grafana/loki/pull/13161)) ([bb864b3](https://github.com/grafana/loki/commit/bb864b3ad63d61f5b091a9cc04246da2f44b2157)).
- **query sharding:** Generalize avg -&gt; sum/count sharding using existing binop mapper ([#12599](https://github.com/grafana/loki/issues/12599)) ([11e7687](https://github.com/grafana/loki/commit/11e768726fb25f905de880ad2f5495b0f7fba156)).
- **regression:** reverts grafana/loki [#13039](https://github.com/grafana/loki/issues/13039) to prevent use-after-free corruptions ([#13162](https://github.com/grafana/loki/issues/13162)) ([41c5ee2](https://github.com/grafana/loki/commit/41c5ee21fc80177b50e74515ca568223e86ae56a)).
- **spans:** Corrects early-close for a few spans ([#12887](https://github.com/grafana/loki/issues/12887)) ([93aaf29](https://github.com/grafana/loki/commit/93aaf29e681053a1d23dcf855cfe92af8415260d)).
- **stores:** Do not filter out chunks for store when `From==Through` and `From==start` ([#13117](https://github.com/grafana/loki/issues/13117)) ([d9cc513](https://github.com/grafana/loki/commit/d9cc513fd2decf96d047d388136417c03ccdc682)).
- **structured metadata:** Add copyString function to symbolizer to avoid retaining memory ([#13146](https://github.com/grafana/loki/issues/13146)) ([86b119a](https://github.com/grafana/loki/commit/86b119ac7ba206d294eb257f99c308fe8452bd58)).
- Temporarily moving from alloy to alloy dev ([#13062](https://github.com/grafana/loki/issues/13062)) ([7ffe0fb](https://github.com/grafana/loki/commit/7ffe0fb6490e171e0100cb35ce6fde9377eff237)).
- Update to build image 0.33.2, fixes bug with Promtail windows DNS resolution ([#12732](https://github.com/grafana/loki/issues/12732)) ([759f42d](https://github.com/grafana/loki/commit/759f42dd50bb4896f5e568691ef32245bb8fb25a)).
- Updated all dockerfiles go1.22 ([#12708](https://github.com/grafana/loki/issues/12708)) ([71a8f2c](https://github.com/grafana/loki/commit/71a8f2c2b11b419bd8c0af1f859671e5d8730448)).
- Use to the proper config names in warning messages ([#12114](https://github.com/grafana/loki/issues/12114)) ([4a05964](https://github.com/grafana/loki/commit/4a05964d5520d46d149f2a4e4709eee36c7fb418)).
- **workflows:** Use an intermediate env variable in GitHub workflow ([#12905](https://github.com/grafana/loki/issues/12905)) ([772616c](https://github.com/grafana/loki/commit/772616cd8f5cbac70374dd4a53f1714fb49a7a3b)).
- **workflows:** Don't run metric collector on forks ([#12687](https://github.com/grafana/loki/issues/12687)) ([7253444](https://github.com/grafana/loki/commit/72534449a07cd9f410973f2d01772024e8e4b7ba)).

# v3.2

Grafana Labs and the Loki team are excited to announce the release of Loki 3.2. Explore Logs is also now Generally Available. Upgrade to Loki/GEL 3.2 to get the best possible experience with Explore Logs. Here's a summary of new enhancements and important fixes.

For a full list of all changes and fixes, refer to the [CHANGELOG](https://github.com/grafana/loki/blob/release-3.2.x/CHANGELOG.md).

## Features and enhancements

Key features in Loki 3.2.0 include the following:

- ***API:** The Loki APIs have been updated to fail log queries when executed on instant query endpoint ([#13421](https://github.com/grafana/loki/issues/13421)) ([ce71f1c](https://github.com/grafana/loki/commit/ce71f1cf954625cac2af7c2d0c335248b01185a3)), add structured metadata to `/detected_fields` API ([#13604](https://github.com/grafana/loki/issues/13604)) ([ce02cc2](https://github.com/grafana/loki/commit/ce02cc254abc641dc40831b28c81199526581085)), and avoid looking up unnecessary TSDB symbols during Volume API ([#13960](https://github.com/grafana/loki/issues/13960)) ([7c1a849](https://github.com/grafana/loki/commit/7c1a8493b3837396d085547a42d8260271f1d68a)).

- **Query acceleration with Bloom filters** (experimental): The Bloom filters feature introduced in Loki 3.0 remains experimental. Note that the Bloom Compactor component has been removed ([#13969](https://github.com/grafana/loki/issues/13969)) ([b75eacc](https://github.com/grafana/loki/commit/b75eacc288c52737e41ba9932c06409c643e2e5c)) and replaced by the Bloom Planner and Bloom Builder components ([#13997](https://github.com/grafana/loki/pull/13997)) ([be9eb50](https://github.com/grafana/loki/commit/be9eb5067f57a0a3dfbf60aa785912f490a6396b)).

- **distributor:** Ignore empty streams in distributor if all entries fail validation ([#13674](https://github.com/grafana/loki/issues/13674)) ([6c4b062](https://github.com/grafana/loki/commit/6c4b0622aa3de44cccb76fe16bb6583bf91cf15c)), and limit to block ingestion until configured date ([#13958](https://github.com/grafana/loki/issues/13958)) ([b5ac6a0](https://github.com/grafana/loki/commit/b5ac6a0258be51a6d6c3a7743e498dc40014b64b)).

- **Explore Logs** Is now Generally Available (GA). For the best experience, you should be on Grafana 11.2 or later and Loki 3.2. This release includes enhancements to add _extracted suffix to detected fields conflicts ([#13993](https://github.com/grafana/loki/issues/13993)) ([ab1caea](https://github.com/grafana/loki/commit/ab1caea12325b5db777101347acf4f277312adf6)), collect and serve pre-aggregated bytes and counts ([#13020](https://github.com/grafana/loki/issues/13020)) ([467eb1b](https://github.com/grafana/loki/commit/467eb1bb1b08fa69e3d5e40a1e0143f65230ad2b)), and remove cardinality filter ([#13652](https://github.com/grafana/loki/issues/13652)) ([4f534d7](https://github.com/grafana/loki/commit/4f534d7317fa0557251f16b76ebf790f079cf98e)).

- **Helm:** This release includes updates to the Helm charts to make gateway container port configurable. ([#13294](https://github.com/grafana/loki/issues/13294)) ([05176e4](https://github.com/grafana/loki/commit/05176e445b90597379c268e799b0fb86b8629b9e)) and to support alibabacloud oss in the Helm chart ([#13441](https://github.com/grafana/loki/issues/13441)) ([3ebab6f](https://github.com/grafana/loki/commit/3ebab6f3931841f62ac59e6b09afef98db656c71)). It also includes a **breaking change** to the Helm chart to support distributed mode and 3.0 ([#12067](https://github.com/grafana/loki/issues/12067)).

- **ingester:** Ingester Stream Limit Improvements: Ingester stream limits now take into account "owned streams" and periodically update when the Ingester ring is changed. Non-owned streams are now also flushed when this update takes place. The stream limit calculation has also been updated for improved accuracy in multi-zone ingester deployments. ([#13532](https://github.com/grafana/loki/issues/13532)) ([ec34aaa](https://github.com/grafana/loki/commit/ec34aaa1ff2e616ef223631657b63f7dffedd3cc)).

- **lambda-promtail:** Add S3 log parser support for AWS GuardDuty ([#13148](https://github.com/grafana/loki/issues/13148)) ([2d92fff](https://github.com/grafana/loki/commit/2d92fff2aa4dbda5f9f8c18ea19347e1236257af)), build lambda with zip file ([#13787](https://github.com/grafana/loki/issues/13787)) ([9bf08f7](https://github.com/grafana/loki/commit/9bf08f7cc055db1997c439ef8edb11247c4e1d67)), and ensure messages to Kinesis are usable by refactoring parsing of KinesisEvent to match parsing of CWEvents + code cleanup ([#13098](https://github.com/grafana/loki/issues/13098)) ([dbfb19b](https://github.com/grafana/loki/commit/dbfb19be49fb3bc1f2f62613f50370028cbf5552)).

- **loki:** Add ability to disable AWS S3 dualstack endpoints usage([#13785](https://github.com/grafana/loki/issues/13785)) ([bb257f5](https://github.com/grafana/loki/commit/bb257f54b33ecb04cbe1786c4efac779d8d28d8c)), not enforce max-query-bytes-read and max-querier-bytes-read in limited tripperware ([#13406](https://github.com/grafana/loki/issues/13406)) ([47f6ea5](https://github.com/grafana/loki/commit/47f6ea53fc4816b259bce4ce4efddee377422d3c)), and upgrade Prometheus ([#13671](https://github.com/grafana/loki/issues/13671)) ([b88583d](https://github.com/grafana/loki/commit/b88583da7d3cc840d4b66698de042773422e334d)).

- **operator:** Add alert for discarded samples ([#13512](https://github.com/grafana/loki/issues/13512)) ([5f2a02f](https://github.com/grafana/loki/commit/5f2a02f14222dab891b7851e8f48052d6c9b594a)), add support for the volume API ([#13369](https://github.com/grafana/loki/issues/13369)) ([d451e23](https://github.com/grafana/loki/commit/d451e23225047a11b4d5d82900cec4a46d6e7b39)), enable leader-election ([#13760](https://github.com/grafana/loki/issues/13760)) ([1ba4bff](https://github.com/grafana/loki/commit/1ba4bff005930b173391df35248e6f58e076fa74)), and update Loki operand to v3.1.0 ([#13422](https://github.com/grafana/loki/issues/13422)) ([cf5f52d](https://github.com/grafana/loki/commit/cf5f52dca0db93847218cdd2c3f4860d983381ae)).

- **storage:** Convert WalSegmentWriter to io.ReadSeeker ([#13340](https://github.com/grafana/loki/issues/13340)) ([19c0509](https://github.com/grafana/loki/commit/19c050926e75e6dcac6d228b838836367414a5f8)) and instrument failed chunk encoding/decoding ([#13684](https://github.com/grafana/loki/issues/13684)) ([5a87ccb](https://github.com/grafana/loki/commit/5a87ccb648ee3bf48a3704643ae9923d64651aed)).

- **docs:** Add getting started video for ingesting OTel logs ([#13226](https://github.com/grafana/loki/issues/13226)) ([5e560f9](https://github.com/grafana/loki/commit/5e560f93ecfa399e85878e30998042646ee4e603)).

Other improvements include the following:

- **chunks-inspect:** Support structured metadata ([#11506](https://github.com/grafana/loki/issues/11506)) ([1834065](https://github.com/grafana/loki/commit/183406570411a5ad5ceaf32bf07451b8fce608c1)).
- **exporter:** Include boolean values in limit exporter ([#13466](https://github.com/grafana/loki/issues/13466)) ([4220737](https://github.com/grafana/loki/commit/4220737a52da7ab6c9346b12d5a5d7bedbcd641d)).
- **mempool:** Replace `sync.Mutex` with `sync.Once` ([#13293](https://github.com/grafana/loki/issues/13293)) ([61a9854](https://github.com/grafana/loki/commit/61a9854eb189e5d2c91528ced10ecf39071df680)).
- **metrics:** Collect duplicate log line metrics ([#13084](https://github.com/grafana/loki/issues/13084)) ([40ee766](https://github.com/grafana/loki/commit/40ee7667244f2e094b5a7199705b4f3dacb7ffaf)).

## Deprecations

One of the focuses of Loki 3.0 was cleaning up unused code and old features that had been previously deprecated but not removed. Loki 3.0 removed a number of previous deprecations and introduces some new deprecations. Some of the main areas with changes include:

- [Deprecated storage options](https://grafana.com/docs/loki/<LOKI_VERSION>/storage/) including the deprecation of the BoltDB store.

- [Deprecated configuration options](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/).

- [API endpoint deprecations](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/api/#deprecated-endpoints).

To learn more about breaking changes in this release, refer to the [Upgrade guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

{{< docs/shared source="alloy" lookup="agent-deprecation.md" version="next" >}}

## Upgrade Considerations

For important upgrade guidance, refer to the [Upgrade Guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/) and the separate [Helm Upgrade Guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/upgrade-helm-to-6.x/).

- **BREAKING CHANGE - API:** Fail log queries when executed on instant query endpoint ([#13421](https://github.com/grafana/loki/issues/13421)).
- **BREAKING CHANGE - blooms:** Remove bloom compactor component ([#13969](https://github.com/grafana/loki/issues/13969)).
- **BREAKING CHANGE - docker:** Remove wget from Promtail docker image([#15101](https://github.com/grafana/loki/issues/15101)).
- **BREAKING CHANGE - Helm:** Update Helm chart to support distributed mode and 3.0 ([#12067](https://github.com/grafana/loki/issues/12067)).
- **BREAKING CHANGE - Helm:** Fix how we set imagePullSecrets for enterprise-gateway and admin-api. ([#13761](https://github.com/grafana/loki/issues/13761)) ([3be5a45](https://github.com/grafana/loki/commit/3be5a4576fd0f0dca321e017a637f7a3159c00e5)).
- **BREAKING CHANGE - jsonnet:** Convert read statefulset into deployment for loki-simple-scalable ([#13977](https://github.com/grafana/loki/issues/13977)).

{{< admonition type="important" >}}
Out of an abundance of caution, we advise that users with Loki or Grafana Enterprise Logs (GEL) deployments on AWS upgrade their Helm charts or change the names of their buckets, as outlined in the solutions and mitigations section of this [blog post](https://grafana.com/blog/2024/06/27/grafana-security-update-grafana-loki-and-unintended-data-write-attempts-to-amazon-s3-buckets/).
{{< /admonition >}}

## Bug fixes

### 3.2.2 (2024-12-04)

- **BREAKING CHANGE - docker:** Remove wget from Promtail docker image([#15101](https://github.com/grafana/loki/issues/15101)).
- **docker:** Move from base-nossl to static. This PR removes the inclusion of glibc into most of the Docker images created by the Loki build system. ([#15203](https://github.com/grafana/loki/issues/15203)).
- **logql:** Updated JSONExpressionParser not to unescape extracted values if it is JSON object.  ([#14499](https://github.com/grafana/loki/issues/14499)).
- **promtail:** Switch Promtail base image from Debian to Ubuntu to fix critical security issues ([#15195](https://github.com/grafana/loki/issues/15195)).
- **storage:** Have GetObject check for canceled context. S3ObjectClient.GetObject incorrectly returned nil, 0, nil when the provided context is already canceled ([#14420](https://github.com/grafana/loki/issues/14420)).

### 3.2.1 (2024-10-17)

- **config:** Copy Alibaba and IBM object storage configuration from common ([#14315](https://github.com/grafana/loki/issues/14315)) ([32a9bc0](https://github.com/grafana/loki/commit/32a9bc0ca852bdc692c2ccebbae448856e191953)).
- **logql:** updated JSONExpressionParser not to unescape extracted values if it is JSON object. (backport release-3.2.x) ([#14502](https://github.com/grafana/loki/issues/14502)) ([e9bbaf3](https://github.com/grafana/loki/commit/e9bbaf3f20ec8f303e977b8e6752152b11cd75b9)).
- **promtail:** Revert build image to Debian Bullseye to fix libc version issue in Promtail ([#14386](https://github.com/grafana/loki/issues/14386)) ([1e913df](https://github.com/grafana/loki/commit/1e913dfc36e0c0aba726c850fd2af975b2a2bbdc)).
- **storage:** have GetObject check for canceled context (backport release-3.2.x) ([#14422](https://github.com/grafana/loki/issues/14422)) ([1b7dd95](https://github.com/grafana/loki/commit/1b7dd95a6c041577c9710c53ae7ad3c93bad771e)).

### 3.2.0 (2024-09-19)

- **blooms:** Cleanup temp blockdir in bloom compactor ([#13622](https://github.com/grafana/loki/issues/13622)) ([64215e1](https://github.com/grafana/loki/commit/64215e18495b12e6d5565eba6fe54bc381ac7189)).
- **blooms:** Delete outdated metas during planning ([#13363](https://github.com/grafana/loki/issues/13363)) ([11e1976](https://github.com/grafana/loki/commit/11e19763d0ee4e1b1130ab0326ed0f4f605bca8d)).
- **blooms:** Ensure tokenizer cache is reset between series ([#13370](https://github.com/grafana/loki/issues/13370)) ([04bc3a4](https://github.com/grafana/loki/commit/04bc3a423c8ea9e7c945b15dffb83d674bab3a68)).
- **blooms:** Fix eviction of multiple blockcache items ([#13573](https://github.com/grafana/loki/issues/13573)) ([c9950e3](https://github.com/grafana/loki/commit/c9950e394d2bca8bd290f60672a3bc904cd72d7b)).
- **blooms:** Fix panic in bloom gateway ([#13303](https://github.com/grafana/loki/issues/13303)) ([66f97b2](https://github.com/grafana/loki/commit/66f97b2aec3cbe0d60acd5e13a9fda9000b03bae)).
- **blooms:** Ignores bloom filtering errors in bounded shard query planning ([#13285](https://github.com/grafana/loki/issues/13285)) ([ede6941](https://github.com/grafana/loki/commit/ede6941c6ff0f40d836b288e167a26c34c2a9437)).
- **blooms:** Improve error wrap to make ignoreNotFound work when fetching blocks ([#13656](https://github.com/grafana/loki/issues/13656)) ([bd20171](https://github.com/grafana/loki/commit/bd20171975e913e429048a0a30328811fc4c8a87)).
- **blooms:** Improves mempool metrics ([#13283](https://github.com/grafana/loki/issues/13283)) ([d36e1d5](https://github.com/grafana/loki/commit/d36e1d580af0a64ce0fcb8de57724d27e399c0dd)).
- **blooms:** Minor fixes and improvements for testing in dev ([#13341](https://github.com/grafana/loki/issues/13341)) ([d0f56ee](https://github.com/grafana/loki/commit/d0f56eeb0a585c37e4a9c62b7a200f4d8360bf4d)).
- **blooms:** Remove backoff from notify planner ([#13506](https://github.com/grafana/loki/issues/13506)) ([e506995](https://github.com/grafana/loki/commit/e506995e595bb5c465941f3f1227311b2ea1c8c5)).
- **blooms:** Remove unused arg ([#13343](https://github.com/grafana/loki/issues/13343)) ([fcb9b28](https://github.com/grafana/loki/commit/fcb9b283ba0cf927646d332a68c049718ec1d236)).
- **blooms:** Ship chunkrefs in task payload ([#13677](https://github.com/grafana/loki/issues/13677)) ([450bbce](https://github.com/grafana/loki/commit/450bbce938fd548715104f6a1a4dde76e2e7ff34)).
- **blooms:** Add logging to empty bloom ([#13502](https://github.com/grafana/loki/issues/13502)) ([c263a68](https://github.com/grafana/loki/commit/c263a681f8e19417ea3056a3e2cae7d3015d081a)).
- **blooms:** Skip empty blooms on reads ([#13500](https://github.com/grafana/loki/issues/13500)) ([bfa6955](https://github.com/grafana/loki/commit/bfa69556afda160051cab677ce278aba5ab48448)).
- **blooms:** Suppress error from resolving server addresses for blocks ([#13385](https://github.com/grafana/loki/issues/13385)) ([3ac2317](https://github.com/grafana/loki/commit/3ac231728e6bc9d3166684bcb697c78b4fb56fae)).
- **blooms:** Use correct key to populate blockscache at startup ([#13624](https://github.com/grafana/loki/issues/13624)) ([2624a4b](https://github.com/grafana/loki/commit/2624a4bdd43badcd1159b83e26c1b0ff14479ac0)).
- **blooms:** Fix log line for fingerprint not found ([#13555](https://github.com/grafana/loki/issues/13555)) ([aeb23bb](https://github.com/grafana/loki/commit/aeb23bb7fc3d33327060828ddf97cb7da7b3c8f8)).
- **blooms:** Fix panic in BloomStore initialization ([#13457](https://github.com/grafana/loki/issues/13457)) ([5f4b8fc](https://github.com/grafana/loki/commit/5f4b8fc9e44ac386ef5bfc64dd5f8f47b72f8ef9)).
- **blooms:** Flaky test blockPlansForGaps ([#13743](https://github.com/grafana/loki/issues/13743)) ([37e33d4](https://github.com/grafana/loki/commit/37e33d41b4583626a0384e4eb4c4570d3ef11882)).
- **blooms:** Keep blocks referenced by newer metas ([#13614](https://github.com/grafana/loki/issues/13614)) ([784e7d5](https://github.com/grafana/loki/commit/784e7d562fedec7134c8ed4e2cee8ccb7049e271)).
- **blooms:** Lint issues after merge to main ([#13326](https://github.com/grafana/loki/issues/13326)) ([7e19cc7](https://github.com/grafana/loki/commit/7e19cc7dca8480932b39c87c7c2e296f99318c95)).
- **blooms:** Use elements match in gapsBetweenTSDBsAndMetas test ([#13722](https://github.com/grafana/loki/issues/13722)) ([4cbe2a6](https://github.com/grafana/loki/commit/4cbe2a6a85c3095f66610cffd32cb3d3bdd43b3f)).
- **ci:** Add cleanup step into job `dist` ([#13801](https://github.com/grafana/loki/issues/13801)) ([217f928](https://github.com/grafana/loki/commit/217f928f52b3d3fad414a01502c37d143cabf567)).
- **ci:** Fixed release-please manifest ([#13810](https://github.com/grafana/loki/issues/13810)) ([f253db5](https://github.com/grafana/loki/commit/f253db5598156a4461fd1f5ede14443c937e2ac8)).
- **deps:** Update github.com/axiomhq/hyperloglog digest to af9851f ([#13806](https://github.com/grafana/loki/issues/13806)) ([67295e0](https://github.com/grafana/loki/commit/67295e0a16677feabb83284e058926b016993128)).
- **deps:** Update github.com/c2h5oh/datasize digest to aa82cc1 ([#13807](https://github.com/grafana/loki/issues/13807)) ([a93f38c](https://github.com/grafana/loki/commit/a93f38cb055c9a3f22cf07d0bd5888a0596ec5d6)).
- **deps:** Update github.com/docker/go-plugins-helpers digest to 45e2431 ([#13808](https://github.com/grafana/loki/issues/13808)) ([e5a3994](https://github.com/grafana/loki/commit/e5a3994fba37247cf2b81405eb4b19b29af89959)).
- **deps:** Update github.com/grafana/jsonparser digest to ea80629 ([#13814](https://github.com/grafana/loki/issues/13814)) ([d5718eb](https://github.com/grafana/loki/commit/d5718eb111f8f0fbbc43294eb8b72877b250d433)).
- **deps:** Update module github.com/aliyun/aliyun-oss-go-sdk to v2.2.10+incompatible ([#13861](https://github.com/grafana/loki/issues/13861)) ([6f79194](https://github.com/grafana/loki/commit/6f791941ee5a188a658313c12f549d40f8802528)).
- **deps:** Update module github.com/azure/go-autorest/autorest/adal to v0.9.24 ([#13862](https://github.com/grafana/loki/issues/13862)) ([8041bd2](https://github.com/grafana/loki/commit/8041bd29b90a79066f7c6393fef1db5ba29440b0)).
- **deps:** Update module github.com/azure/go-autorest/autorest/azure/auth to v0.5.13 ([#13863](https://github.com/grafana/loki/issues/13863)) ([71c4421](https://github.com/grafana/loki/commit/71c4421e09f30ebd8a1826c976436d3ca3ad603d)).
- **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.186 ([#13864](https://github.com/grafana/loki/issues/13864)) ([3c0e3e2](https://github.com/grafana/loki/commit/3c0e3e2c13591e3af44ce4826245043c81bb66c3)).
- **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.187 ([#13933](https://github.com/grafana/loki/issues/13933)) ([56af84d](https://github.com/grafana/loki/commit/56af84d3a638dbe30f1cacffd7d090118720d787)).
- **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.188 ([#14000](https://github.com/grafana/loki/issues/14000)) ([79039a2](https://github.com/grafana/loki/commit/79039a24a244b06b43018095e29c9ed65b0e1067)).
- **deps:** Update module github.com/cespare/xxhash/v2 to v2.3.0 (main) ([#13615](https://github.com/grafana/loki/issues/13615)) ([cfc7b34](https://github.com/grafana/loki/commit/cfc7b34b9eb94960bd960b7a8a4442a2a1a9ecaf)).
- **deps:** Update module github.com/datadog/sketches-go to v1.4.6 ([#13865](https://github.com/grafana/loki/issues/13865)) ([1f3c467](https://github.com/grafana/loki/commit/1f3c467b412dabf7f330dc71befcdf50596ba517)).
- **deps:** Update module github.com/docker/docker to v25.0.5+incompatible [security] (main) ([#12279](https://github.com/grafana/loki/issues/12279)) ([960c034](https://github.com/grafana/loki/commit/960c03438477435b606cf4dfbb7af43a5b52068d)).
- **deps:** Update module github.com/docker/docker to v27.1.1+incompatible [security] (main) ([#13762](https://github.com/grafana/loki/issues/13762)) ([f8bf3bb](https://github.com/grafana/loki/commit/f8bf3bb3786ccf5c3784e5b75e9d030251dcc8fb)).
- **deps:** Update module github.com/docker/docker to v27.1.2+incompatible ([#13872](https://github.com/grafana/loki/issues/13872)) ([8ab4c20](https://github.com/grafana/loki/commit/8ab4c2057256511b5bc25c5f9c9ff870b5b71cb5)).
- **deps:** Update module github.com/efficientgo/core to v1.0.0-rc.3 ([#14001](https://github.com/grafana/loki/issues/14001)) ([90f7e5f](https://github.com/grafana/loki/commit/90f7e5fa67dcf7b05c8aae54bacdf96f98c27faf)).
- **deps:** Update module github.com/felixge/fgprof to v0.9.4 ([#13870](https://github.com/grafana/loki/issues/13870)) ([c68848f](https://github.com/grafana/loki/commit/c68848f8056aca3ebb358dd1fc8adf6e07611e9c)).
- **deps:** Update module github.com/fsouza/fake-gcs-server to v1.47.7 ([#13935](https://github.com/grafana/loki/issues/13935)) ([d43b2de](https://github.com/grafana/loki/commit/d43b2de1b4e0d0a999569900f69755cfe6b17c21)).
- **deps:** Update module github.com/gogo/googleapis to v1.4.1 ([#13871](https://github.com/grafana/loki/issues/13871)) ([6da7eb5](https://github.com/grafana/loki/commit/6da7eb577cac62208801374af71c90d4a06df097)).
- **deps:** Update module github.com/gorilla/mux to v1.8.1 (main) ([#13618](https://github.com/grafana/loki/issues/13618)) ([19b288e](https://github.com/grafana/loki/commit/19b288eee4ad9c25fa58de56c3be02393e63a20e)).
- **deps:** Update module github.com/gorilla/websocket to v1.5.3 ([#13873](https://github.com/grafana/loki/issues/13873)) ([1eb8342](https://github.com/grafana/loki/commit/1eb8342d41a9fdb6c5fcd3e6e5a8c6b98bde4e43)).
- **deps:** Update module github.com/hashicorp/consul/api to v1.29.4 ([#14002](https://github.com/grafana/loki/issues/14002)) ([e11b244](https://github.com/grafana/loki/commit/e11b244a8bcbc69d6829d31fb164dc43d505068e)).
- **deps:** Update module github.com/ibm/go-sdk-core/v5 to v5.17.4 ([#13892](https://github.com/grafana/loki/issues/13892)) ([b6991f2](https://github.com/grafana/loki/commit/b6991f29d232267c1fa0ed8dff55da72240c23f6)).
- **deps:** Update module github.com/ibm/ibm-cos-sdk-go to v1.11.0 ([#13893](https://github.com/grafana/loki/issues/13893)) ([9b7e7e9](https://github.com/grafana/loki/commit/9b7e7e97a41d2ce0abe62b0d920538e9974cef69)).
- **deps:** Update module github.com/klauspost/pgzip to v1.2.6 ([#13874](https://github.com/grafana/loki/issues/13874)) ([fdea7a1](https://github.com/grafana/loki/commit/fdea7a1763618812284a44d6c247c4215d317950)).
- **deps:** Update module github.com/mattn/go-ieproxy to v0.0.12 ([#13876](https://github.com/grafana/loki/issues/13876)) ([775bf8e](https://github.com/grafana/loki/commit/775bf8ebe7893a5b0807984a1c791f211820eed2)).
- **deps:** Update module github.com/ncw/swift to v2 ([#13951](https://github.com/grafana/loki/issues/13951)) ([246a1df](https://github.com/grafana/loki/commit/246a1dfbe24a00b75b03257cb7e75be6cc96a3a8)).
- **deps:** Update module github.com/oschwald/geoip2-golang to v1.11.0 ([#13934](https://github.com/grafana/loki/issues/13934)) ([3bebba5](https://github.com/grafana/loki/commit/3bebba59b5a81da77c6b0d6c499f92f9ce320d46)).
- **deps:** Update module github.com/schollz/progressbar/v3 to v3.14.6 ([#13884](https://github.com/grafana/loki/issues/13884)) ([fb9cae4](https://github.com/grafana/loki/commit/fb9cae4aaa6a12a375fa6199bfcd562833385737)).
- **deps:** Update module github.com/tonistiigi/fifo to v1 ([#13952](https://github.com/grafana/loki/issues/13952)) ([96b5c79](https://github.com/grafana/loki/commit/96b5c79e7770f706bdc1d07e306bf225706273a3)).
- **deps:** Update module github.com/workiva/go-datastructures to v1.1.5 ([#13885](https://github.com/grafana/loki/issues/13885)) ([d817aee](https://github.com/grafana/loki/commit/d817aeeab374f414b08598a8784ea708000856d2)).
- **deps:** Update module golang.org/x/text to v0.17.0 (main) ([#13794](https://github.com/grafana/loki/issues/13794)) ([df61482](https://github.com/grafana/loki/commit/df61482207eb8f44f43d9c2ef4f450fc0c9a00ee)).
- **deps:** Update module golang.org/x/time to v0.6.0 ([#13910](https://github.com/grafana/loki/issues/13910)) ([dff00bd](https://github.com/grafana/loki/commit/dff00bd8f26e85ce04edc16a9f43cb32d3691add)).
- **detected fields:** Detected fields incorrect type bug ([#13515](https://github.com/grafana/loki/issues/13515)) ([f6a94d3](https://github.com/grafana/loki/commit/f6a94d303444dbf22cf1198f549c9cde070f1bdc)).
- **detected fields:** Remove query size limit for detected fields ([#13423](https://github.com/grafana/loki/issues/13423)) ([1fa5127](https://github.com/grafana/loki/commit/1fa51277978ead6569e31e908dec7f140dadb90f)).
- **detected labels:** Response when store label values are empty ([#13970](https://github.com/grafana/loki/issues/13970)) ([6f99af6](https://github.com/grafana/loki/commit/6f99af62227f98c7d9de8a5cf480ae792ce6220a)).
- **detected_labels:** Add matchers to get labels from store" ([#14012](https://github.com/grafana/loki/issues/14012)) ([25234e8](https://github.com/grafana/loki/commit/25234e83483cb8a974d40b7c80b3d4dd62d6d880)).
- **detected_labels:** Remove limit middleware for `detected_labels` ([#13643](https://github.com/grafana/loki/issues/13643)) ([2642718](https://github.com/grafana/loki/commit/2642718d50569931b71cfc0c9288318ab775ca41)).
- **docs:** Fixed typo in ruler URL ([#13692](https://github.com/grafana/loki/issues/13692)) ([1476498](https://github.com/grafana/loki/commit/14764989a2c6f01803f0313d8151f7aa20affd4a)).
- **docs:** Remove trailing backtick in verify-config for Loki 3.0 ([#13640](https://github.com/grafana/loki/issues/13640)) ([498f29a](https://github.com/grafana/loki/commit/498f29a66b2dbfeff85454f22d0596d20066a635)).
- **Helm:** Fix HPA ingester typo ([#13158](https://github.com/grafana/loki/issues/13158)) ([4ca9785](https://github.com/grafana/loki/commit/4ca97858d9dc33db7abbe20ca01c6735cb9ce34e)).
- **Helm:** Fix extraObjects ([#13107](https://github.com/grafana/loki/issues/13107)) ([b7fcf2b](https://github.com/grafana/loki/commit/b7fcf2bb7ea35206c0015545c93582991f64f581)).
- **Helm:** Fix imagePullSecrets for statefulset-results-cache ([#13051](https://github.com/grafana/loki/issues/13051)) ([8434b2f](https://github.com/grafana/loki/commit/8434b2f6e8e124225aafe6e55ca9c1b6ff6a2c5b)).
- **Helm:** Fixed memcached and provisioner templates ([#13788](https://github.com/grafana/loki/issues/13788)) ([1bf9791](https://github.com/grafana/loki/commit/1bf97912de83200d02689511f48658ce7d9543cf)).
- **Helm:** Removed helm test ([#13651](https://github.com/grafana/loki/issues/13651)) ([ef03476](https://github.com/grafana/loki/commit/ef03476f3dac159e5f58490351223fcdb9ac3469)).
- **Helm:** Update yaml file `./production/helm/loki/Chart.yaml` (+1 other) ([#13392](https://github.com/grafana/loki/issues/13392)) ([b5b861c](https://github.com/grafana/loki/commit/b5b861c348bc768254fd083fb40d2820cf347be6)).
- **Helm:** Update yaml file `./production/helm/loki/values.yaml` (+1 other) ([#13426](https://github.com/grafana/loki/issues/13426)) ([fc3904e](https://github.com/grafana/loki/commit/fc3904ee69d0824dc681ca5a4280f7aa2ec5563b)).
- **Helm:** Querier address in SingleBinary mode ([#13297](https://github.com/grafana/loki/issues/13297)) ([29f1ea9](https://github.com/grafana/loki/commit/29f1ea91ecd935a6becae2bd425224a913285071)).
- **Helm:** Update Loki v3 Helm statefulset-ingester.yaml template ([#13118](https://github.com/grafana/loki/issues/13118)) ([5b4e576](https://github.com/grafana/loki/commit/5b4e57602f7b7e5f4d73204ad682826d1041f8a8)).
- **index gateways:** Do not retain span logger created with index set initialized at query time ([#14027](https://github.com/grafana/loki/issues/14027)) ([bd25ac2](https://github.com/grafana/loki/commit/bd25ac2503b00812d959c2aaf092bd2618f16a5a)).
- **index gateway:** Fix nil pointer dereference panic when using ruler in ring mode ([#13436](https://github.com/grafana/loki/issues/13436)) ([304db10](https://github.com/grafana/loki/commit/304db100b382f0c1d1d9999dfe8ca77d1ac901c9)).
- **ingester:** Fix panic in ingester.go ([#13557](https://github.com/grafana/loki/issues/13557)) ([dbff69a](https://github.com/grafana/loki/commit/dbff69a2e92f3ce34f7d58a7418cd0456d644be3)).
- **ingester:** Redo ingester profile tagging ([#13239](https://github.com/grafana/loki/issues/13239)) ([32097c8](https://github.com/grafana/loki/commit/32097c84627f5190cfcf6c1f247c9d0531d92865)).
- **ingester:** Remove tenant label tagging from profiles to reduce cardinality ([#13270](https://github.com/grafana/loki/issues/13270)) ([f897758](https://github.com/grafana/loki/commit/f8977587476169197d6da4d7055b97b189808344)).
- **ingester:** Stream ownership check ([#13314](https://github.com/grafana/loki/issues/13314)) ([5ae5b31](https://github.com/grafana/loki/commit/5ae5b31b1f9ffcac9193cfd4ba47a64d911966db)).
- **ingester:** Support multi-zone ingesters when converting global to local limits for streams in limiter.go ([#13321](https://github.com/grafana/loki/issues/13321)) ([e28c15f](https://github.com/grafana/loki/commit/e28c15f56c2aab62eecbaa382055eac99fc3a581)).
- **ingester:** Update fixed limit once streams ownership re-checked ([#13231](https://github.com/grafana/loki/issues/13231)) ([7ac19f0](https://github.com/grafana/loki/commit/7ac19f00b4f5186b0c38a8dad23cf61e14d071de)).
- **LOgQL:** AST left circular reference result in out of memory ([#13501](https://github.com/grafana/loki/issues/13501)) ([6dd6b65](https://github.com/grafana/loki/commit/6dd6b65139b3b8d4254f114e99ab8fb3eaa2ae09)).
- **LogQL:** Improve execution speed for queries with label filters ([#13922](https://github.com/grafana/loki/issues/13922)) ([40f4f14](https://github.com/grafana/loki/commit/40f4f1479170a90b39c005292e11a3ec4db4bc34)).
- **LogQL:** Panic when parsing and extracting JSON key values ([#13790](https://github.com/grafana/loki/issues/13790)) ([5ef83a7](https://github.com/grafana/loki/commit/5ef83a741ba515f68343e9dc345fcb8afe921bfd)).
- **LogQL:** Propagate headers/warnings/stats from quantile downstreams ([#13881](https://github.com/grafana/loki/issues/13881)) ([a0c7598](https://github.com/grafana/loki/commit/a0c75987a24d0adc520c60dd5d85df4c34009548)).
- **LogQL:** Record datasample queries are limited query type ([#13930](https://github.com/grafana/loki/issues/13930)) ([ae938d0](https://github.com/grafana/loki/commit/ae938d06d941a386aa839b6717445c2295ce2efa)).
- **LogQL:** Return empty vector instead of nil for empty evaluator. ([#13485](https://github.com/grafana/loki/issues/13485)) ([08615bf](https://github.com/grafana/loki/commit/08615bf7519b31e825903577427f7407194baf74)).
- **LogQL:** Special case the return values from a sharded first/last_over_time query ([#13578](https://github.com/grafana/loki/issues/13578)) ([29a37d5](https://github.com/grafana/loki/commit/29a37d5dcdab33d62615a79aefe97ea2a80dea03)).
- **log results cache:** Include pipeline wrapper disabled in cache key ([#13328](https://github.com/grafana/loki/issues/13328)) ([221491c](https://github.com/grafana/loki/commit/221491c123adb6cedfabace6fc2cd03a32124655)).
- **loki:** Init internal server log along with loki's server instance ([#13221](https://github.com/grafana/loki/issues/13221)) ([66b8c9b](https://github.com/grafana/loki/commit/66b8c9b7738acd0e0616b88d35cf3ddc0df83e7e)).
- **loki-mixin:** Fix latency panel for Index Gateway ([#13629](https://github.com/grafana/loki/issues/13629)) ([f586c00](https://github.com/grafana/loki/commit/f586c00a9fcfa8bb84781698e141dff928b86c92)).
- **loki-mixin:** Various latency panels in operational dashboard should have ms unit type instead of seconds ([#13260](https://github.com/grafana/loki/issues/13260)) ([f5a9905](https://github.com/grafana/loki/commit/f5a99058036f60f5ae0c190c48cbcf5ce22ea96d)).
- **loki-mixin:** Attribute OTLP route correctly to write path ([#13943](https://github.com/grafana/loki/issues/13943)) ([b91b782](https://github.com/grafana/loki/commit/b91b7829075f9df565d468d9e72191e9f4c5e94e)).
- **loki-mixins:** Incorrect pod matcher for compactor in mixin when using ssd mode ([#12846](https://github.com/grafana/loki/issues/12846)) ([515e13c](https://github.com/grafana/loki/commit/515e13cc6c92b08968bc87e220b8bca64683fd05)).
- **operator:** Allow structured metadata only if V13 schema provided ([#13463](https://github.com/grafana/loki/issues/13463)) ([3ac130b](https://github.com/grafana/loki/commit/3ac130b8a152169766cb173718f2312aeb4f694e)).
- **operator:** Don't overwrite annotations for LokiStack ingress resources ([#13708](https://github.com/grafana/loki/issues/13708)) ([f523530](https://github.com/grafana/loki/commit/f52353060dd936cff587ff2060c8616941695ece)).
- **operator:** Remove duplicate conditions from status ([#13497](https://github.com/grafana/loki/issues/13497)) ([527510d](https://github.com/grafana/loki/commit/527510d1a84a981250047dbabba8d492177b8452)).
- **operator:** Set object storage for delete requests when using retention ([#13562](https://github.com/grafana/loki/issues/13562)) ([46de4c1](https://github.com/grafana/loki/commit/46de4c1bc839ef682798bec5003123f7d5f4404b)).
- **operator:** Skip updating annotations for serviceaccounts ([#13450](https://github.com/grafana/loki/issues/13450)) ([1b9b111](https://github.com/grafana/loki/commit/1b9b11116b48fb37b7015d27104668412fc04937)).
- **operator:** Support v3.1.0 in OpenShift dashboards ([#13430](https://github.com/grafana/loki/issues/13430)) ([8279d59](https://github.com/grafana/loki/commit/8279d59f145df9c9132aeff9e3d46c738650027c)).
- **operator:** Watch for CredentialsRequests on CCOAuthEnv only ([#13299](https://github.com/grafana/loki/issues/13299)) ([7fc926e](https://github.com/grafana/loki/commit/7fc926e36ea8fca7bd8e9955c8994574535dbbae)).
- **querier:** Add a retry middleware to all the stats handlers ([#13584](https://github.com/grafana/loki/issues/13584)) ([7232795](https://github.com/grafana/loki/commit/7232795e1f5fb1868c83111f5aab72ca0f3d9891)).
- **querier:** Adjust tailer loop criteria so it is actually re-tested ([#13906](https://github.com/grafana/loki/issues/13906)) ([dabbfd8](https://github.com/grafana/loki/commit/dabbfd81ef5c4f02a255b404ab25edd1eec126cf)).
- **querier:** Fix retry code to handle grpc status codes. updated newer stats retries to be wrapped with spans ([#13592](https://github.com/grafana/loki/issues/13592)) ([d3e1edb](https://github.com/grafana/loki/commit/d3e1edbf1102b2f0f4116c3bb1773000d0368dde)).
- **querier:** Fixes span name of serializeRounTripper ([#13541](https://github.com/grafana/loki/issues/13541)) ([4451d56](https://github.com/grafana/loki/commit/4451d56d6b9a9d2eb54ed75d3d2c8fe0db6908eb)).
- **querier:** Remove retries on the stats handlers because they already retry ([#13608](https://github.com/grafana/loki/issues/13608)) ([1008315](https://github.com/grafana/loki/commit/10083159a7e54df4e41efe2fc2e04e267fee1147)).
- **query engine:** Include lines with ts equal to end timestamp of the query range when executing range aggregations ([#13448](https://github.com/grafana/loki/issues/13448)) ([e0ca67d](https://github.com/grafana/loki/commit/e0ca67dd4563e41c57b2f1409ef235b76b2a1a6e)).
- **retry:** Fix retries when using protobuf encoding ([#13316](https://github.com/grafana/loki/issues/13316)) ([a457c5d](https://github.com/grafana/loki/commit/a457c5d171d5ffa0a7060c98a8bc48abd735911a)).
- **ruler:** Protect ruler remote-write overrides map with a mutex when creating new appenders ([#13676](https://github.com/grafana/loki/issues/13676)) ([e9a9c60](https://github.com/grafana/loki/commit/e9a9c60c22e78b52c0c046d379b4b2b986d91dca)).
- **sharding:** Use without() grouping when merging `avg_over_time` shard results ([#12176](https://github.com/grafana/loki/issues/12176)) ([eb8a363](https://github.com/grafana/loki/commit/eb8a36306674c497d8b0150b482f275e2c00f6c9)).
- **storage:** Handle block offset exceeding chunk length in memchunk.go ([#13661](https://github.com/grafana/loki/issues/13661)) ([d42476a](https://github.com/grafana/loki/commit/d42476aa58fca07b17ee39d388639807624f884a)).
- **storage:** Read "404" as object not exist ([#13901](https://github.com/grafana/loki/issues/13901)) ([3c9c647](https://github.com/grafana/loki/commit/3c9c6479226818229802b97e08d6c9e13e3798a5)).
- **storage:** Separates directory creation from permission checks ([#13248](https://github.com/grafana/loki/issues/13248)) ([1086783](https://github.com/grafana/loki/commit/1086783a1d8886f0e6888289975e771e18d800e6)).
- **storage:** Try reading chunks which have incorrect offset for blocks ([#13720](https://github.com/grafana/loki/issues/13720)) ([7e224d5](https://github.com/grafana/loki/commit/7e224d53de8a5c43448ffd341f0d9c48abb335ef)).
- **structured metadata:** Sanitize structured metadata at query time ([#13983](https://github.com/grafana/loki/issues/13983)) ([3bf7fa9](https://github.com/grafana/loki/commit/3bf7fa9f159a7c76b1bcdd640c765b333766f748)).
- **WAL:** Fix a bug where AppendRequest with no entries triggers flush ([#13672](https://github.com/grafana/loki/issues/13672)) ([8a3ae22](https://github.com/grafana/loki/commit/8a3ae223ba160584d61bd5cb39b546a3c28f46b5)).
- **WAL:** Properly reset wal segment writer ([#13468](https://github.com/grafana/loki/issues/13468)) ([6ea83b4](https://github.com/grafana/loki/commit/6ea83b45b5e9f8e1d0f9d7e5574bb5b520ddfefd)).

# v3.3

Grafana Labs and the Loki team are excited to announce the release of Loki 3.3. Here's a summary of new enhancements and important fixes.

For a full list of all changes and fixes, refer to the [CHANGELOG](https://github.com/grafana/loki/blob/release-3.3.x/CHANGELOG.md).

## Features and enhancements

Key features in Loki 3.3.0 include the following:

- **Query acceleration with Bloom filters** (experimental): The Bloom filters feature introduced in Loki 3.0 remains experimental. There has been a major shift in how Loki leverages bloom filters — pivoting from free-text search to harnessing the power of structured metadata ([#14061](https://github.com/grafana/loki/issues/14061)) ([a2fbaa8](https://github.com/grafana/loki/commit/a2fbaa8e09b6eebff2f7c20746e84f1365bd7433)). This change leverages the growing adoption of OpenTelemetry in observability stacks. The Bloom Compactor component has been removed and replaced by the Bloom Planner and Bloom Builder components ([#13997](https://github.com/grafana/loki/issues/13997)) ([bf60455](https://github.com/grafana/loki/commit/bf60455c8e52b87774df9ca90232b4c72d72e46b)) and blooms has a new block schema (V3) ([#14038](https://github.com/grafana/loki/issues/14038)) ([5395daf](https://github.com/grafana/loki/commit/5395daf898c2d0bbc4756ab6260c54feda960911)) which is a breaking change.

- **Explore Logs:** Starting with Grafana v11.3, the plugin for the Explore Logs app is installed in Grafana by default. This release includes enhancements to improve pattern ingester tracing ([#14707](https://github.com/grafana/loki/issues/14707)) ([80aec25](https://github.com/grafana/loki/commit/80aec2548203957dbb834ba69e6d734d9054416d)), to apply patterns line length limit to json message keys ([#14296](https://github.com/grafana/loki/issues/14296)) ([41fafd8](https://github.com/grafana/loki/commit/41fafd87933224d5d43592e91e339322fc90a466)), add configuration to set `max-allowed-line-length` in the pattern ingester ([#14070](https://github.com/grafana/loki/issues/14070)) ([0780456](https://github.com/grafana/loki/commit/0780456662b67edde69004cf4ee3873c23d5094b)) and move metric aggregation to a per-tenant configuration ([#14709](https://github.com/grafana/loki/issues/14709)) ([c1fde26](https://github.com/grafana/loki/commit/c1fde26730b4fc54e4bbc724d1b29f653541f720)).

- **logql:** We've added an `approx_topk` function to LogQL, which uses probabilistic techniques to give you an approximation of the most frequent values in the input, along with their approximate frequencies. This is great for cases where the equivalent `topk` query hits the maximum series limit or times out before completion. Its also great in cases where you want a faster response to your query and are fine with an approximation of the true answer. ([#14243](https://github.com/grafana/loki/issues/14243)). For more information, visit [LogQL docs](https://grafana.com/docs/loki/latest/query/metric_queries/#probabilistic-aggregation).

- **helm:** Multiple improvements to the Helm charts, including: Updating the chart with Loki version 3.2.0 ([#14281](https://github.com/grafana/loki/issues/14281)) ([11b92ee](https://github.com/grafana/loki/commit/11b92eeb95612a2bb002ea22f048c55ae20557a2)), replacing bloom compactor with bloom planner and builder ([#14003](https://github.com/grafana/loki/issues/14003)) ([08e61ca](https://github.com/grafana/loki/commit/08e61ca4db086b573ef636a156bfc624132515be)), adding the ability to configure `client_max_body_size` ([#12924](https://github.com/grafana/loki/issues/12924)) ([809a024](https://github.com/grafana/loki/commit/809a024581c1f600744b9db0b2b2142234317082)), adding additional service annotations for components in distributed mode ([#14131](https://github.com/grafana/loki/issues/14131)) ([5978f13](https://github.com/grafana/loki/commit/5978f1344c84525e6b8bda45869b867b7e878956)), adding configurable `extraEnvFrom` to admin-api and enterprise-gateway ([#14533](https://github.com/grafana/loki/issues/14533)) ([5d78a3a](https://github.com/grafana/loki/commit/5d78a3a3fd1f630d6b012a9240fa081e63bcb7ef)), adding `kubeVersionOverride` for Helm chart ([#14434](https://github.com/grafana/loki/issues/14434)) ([0935d77](https://github.com/grafana/loki/commit/0935d77df08e6ad40a9f498f53e94e335b020ded)), adding persistence option to memcached on Helm chart ([#13619](https://github.com/grafana/loki/issues/13619)) ([ef1df0e](https://github.com/grafana/loki/commit/ef1df0e66fc8e2fe9327a66aea31279ca5c7307a)), adding tolerations to pattern-ingester statefulset ([#13605](https://github.com/grafana/loki/issues/13605)) ([09530c0](https://github.com/grafana/loki/commit/09530c0f4a1503713a76c68153b4da5287f9b79f)), allowing setting node attributes to `tokengen` and `provisioner` ([#14311](https://github.com/grafana/loki/issues/14311)) ([c708ae6](https://github.com/grafana/loki/commit/c708ae691ca2d9a26b1c2a4591ed32dbfdd94619)), updating Loki Helm chart for restricted environments ([#14440](https://github.com/grafana/loki/issues/14440)) ([adc7538](https://github.com/grafana/loki/commit/adc75389a39e3aaad69303b82b0d68ec3d94485c)), and supporting ruler sidecar in singleBinary mode ([#13572](https://github.com/grafana/loki/issues/13572)) ([684baf7](https://github.com/grafana/loki/commit/684baf7dbacef4b85a08db8de9934458745124d8)).

- **mixins:** Multiple improvements to the Loki mixins, including: adding a missing cluster label to mixins ([#12870](https://github.com/grafana/loki/issues/12870)) ([547ca70](https://github.com/grafana/loki/commit/547ca708b9b56e2761bd19ebfcfc9f8571d9af2a)), adding support for partition ingester in dashboards ([#14498](https://github.com/grafana/loki/issues/14498)) ([70deebf](https://github.com/grafana/loki/commit/70deebf26e88c6f2b10c78b3b8ce785c8a16e03b)), adding a Loki compaction not successful alert ([#14239](https://github.com/grafana/loki/issues/14239)) ([da04f50](https://github.com/grafana/loki/commit/da04f5007edd85f35d1af5ba8c2c5a4eb96d2149)), allowing overriding of some labels by parameterizing mixin recording/alert rules ([#11495](https://github.com/grafana/loki/issues/11495)) ([f1425b6](https://github.com/grafana/loki/commit/f1425b6c24e9d90c99477f67289c3aa34f69573d)), allowing disabling of bloom dashboards ([#14177](https://github.com/grafana/loki/issues/14177)) ([ce2e6d5](https://github.com/grafana/loki/commit/ce2e6d520b48fe9c5c7593ae2400a6983905782e)), allowing hiding of unused rows in loki-operational ([#13646](https://github.com/grafana/loki/issues/13646)) ([3aa4f22](https://github.com/grafana/loki/commit/3aa4f2227e4178f05e6b13cffc044989c7839372)), and merging read-resources and write-resources dashboards for SSD into one dashboard that also includes the backend-resources([#13471](https://github.com/grafana/loki/issues/13471)) ([45b8719](https://github.com/grafana/loki/commit/45b8719aa768db35d4e7559fd87e22056248b912)).

- **operator:** Multiple updates to the Operator, including: Adding support for Loki OTLP limits configuration ([#13446](https://github.com/grafana/loki/issues/13446)) ([d02f435](https://github.com/grafana/loki/commit/d02f435d3bf121b19e15de4f139c95a6d010b25c)), providing default OTLP attribute configuration ([#14410](https://github.com/grafana/loki/issues/14410)) ([1b52387](https://github.com/grafana/loki/commit/1b5238721994c00764b6a7e7d63269c5b56d2480)), adding a User-guide for OTLP configuration ([#14620](https://github.com/grafana/loki/issues/14620)) ([27b4071](https://github.com/grafana/loki/commit/27b40713540bd60918780cdd4cb645e6761427cb)), updating the Loki operand to v3.1.1 ([#14042](https://github.com/grafana/loki/issues/14042)) ([7ae1588](https://github.com/grafana/loki/commit/7ae1588200396b73a16fadd2610670a5ce5fd747)), updating the Loki operand to v3.2.1 ([#14526](https://github.com/grafana/loki/issues/14526)) ([5e970e5](https://github.com/grafana/loki/commit/5e970e50b166e73f5563e21c23db3ea99b24642e)), renaming the Loki API go module ([#14568](https://github.com/grafana/loki/issues/14568)) ([976d8ab](https://github.com/grafana/loki/commit/976d8ab81c1a79f35d7cec96f6a9c35a9947fa48)), introducing 1x.pico size ([#14407](https://github.com/grafana/loki/issues/14407)) ([57de81d](https://github.com/grafana/loki/commit/57de81d8c27e221832790443cebaf141353c3e3f)), migrating the project layout to kubebuilder go/v4 ([#14447](https://github.com/grafana/loki/issues/14447)) ([dbb3b6e](https://github.com/grafana/loki/commit/dbb3b6edc96f3545a946319c0324518800d286cf)), declaring feature FIPS support for OpenShift only ([#14308](https://github.com/grafana/loki/issues/14308)) ([720c303](https://github.com/grafana/loki/commit/720c3037923c174e71a02d99d4bee6271428fbdb)).

- **docs:** New videos and tutorials, including a New Cloud deployment guide for [Deploying Loki on AWS](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/deployment-guides/aws/), a new [Fluent Bit tutorial](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/fluentbit/fluent-bit-loki-tutorial/) and a [Getting Started with OpenTelemetry Collector tutorial](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/otel/otel-collector-getting-started/). Also there are improvements to the Query documentation, including a new [Query Best Practices topic](https://grafana.com/docs/loki/<LOKI_VERSION>/query/bp-query/), a reorganized [Query Template](https://grafana.com/docs/loki/<LOKI_VERSION>/query/template_functions/), and a revised [LogQL Simulator](https://grafana.com/docs/loki/<LOKI_VERSION>/query/analyzer/) topic.

Other improvements include the following:

- **api:** Added endpoint to return detected field values ([#14350](https://github.com/grafana/loki/issues/14350)) ([7983f94](https://github.com/grafana/loki/commit/7983f94b15b422b94517641bd9cec5c9da6903e1)).
- **blooms:** Add disk-backed queue for the bloom-planner (backport k227) ([#14927](https://github.com/grafana/loki/issues/14927)) ([1f6828b](https://github.com/grafana/loki/commit/1f6828b25c5c5d6ad5eda3be60a435db8ca55fc3)).
- **blooms:** Only write key and key=value to blooms ([#14686](https://github.com/grafana/loki/issues/14686)) ([3af0004](https://github.com/grafana/loki/commit/3af0004cb4d4dafbcbe099e4409edf6e6ff056a5)).
- **blooms:** Do not add empty blooms to offsets ([#14577](https://github.com/grafana/loki/issues/14577)) ([51c42e8](https://github.com/grafana/loki/commit/51c42e864563f2fa9ffc160cb13f6d6126ea5c6d)).
- **blooms:** Extract task computing into a strategy interface ([#13690](https://github.com/grafana/loki/issues/13690)) ([ab5e6ea](https://github.com/grafana/loki/commit/ab5e6eaaeea24f93f434dcece6ff5d9dc83e6d32)).
- **blooms:** New bloom planning using chunk size TSDB stats ([#14547](https://github.com/grafana/loki/issues/14547)) ([673ede1](https://github.com/grafana/loki/commit/673ede16a5f675684f9e6a53903335af5075a507)).
- **blooms:** Remove compression of `.tar` archived bloom blocks ([#14159](https://github.com/grafana/loki/issues/14159)) ([cdf084f](https://github.com/grafana/loki/commit/cdf084fdaeaf632e7c078022c6ad4322bfef2989)).
- **compactor:** Add backoff mechanism to the retention process ([#14182](https://github.com/grafana/loki/issues/14182)) ([3136880](https://github.com/grafana/loki/commit/31368806a9c5e0ff6c43045e008861f26ed61af3)).
- **distributor:** Track discarded data by usageTracker ([#14081](https://github.com/grafana/loki/issues/14081)) ([c65721e](https://github.com/grafana/loki/commit/c65721e7ade0ef89fd282d9f764fb2d05f6b9c42)).
- **distributor:** Add ability to log stream selectors before service name detection ([#14154](https://github.com/grafana/loki/issues/14154)) ([d7ff426](https://github.com/grafana/loki/commit/d7ff42664681794b9ef5026ac3758cdd9569ac1a)).
- **distributors:** Use a pool of worker to push to ingesters. ([#14245](https://github.com/grafana/loki/issues/14245)) ([f80d68a](https://github.com/grafana/loki/commit/f80d68a1edbd85a605be882eb0104b169343cf00)).
- **fluentd:** Support custom http headers ([#14299](https://github.com/grafana/loki/issues/14299)) ([e59035e](https://github.com/grafana/loki/commit/e59035e17315f453d4b2e2334330bc062d40f0fd)).
- **ingester:** Implement owned streams calculation using Partition Ring ([#14282](https://github.com/grafana/loki/issues/14282)) ([3c36ba9](https://github.com/grafana/loki/commit/3c36ba949d65e803cc6702b8664f87aca07ed052)).
- **ingester:** Implement partition shuffle sharding for ingester ([#14304](https://github.com/grafana/loki/issues/14304)) ([1a4436c](https://github.com/grafana/loki/commit/1a4436c41721e3e6aca82c26abaec8fe6f775d9f)).
- **jsonnet:** Allow to name prefix zoned ingesters ([#14260](https://github.com/grafana/loki/issues/14260)) ([fac3177](https://github.com/grafana/loki/commit/fac3177814b8d2914eb3af618d571104eba18934)).
- **labels:** Add app_name as a service label ([#13660](https://github.com/grafana/loki/issues/13660)) ([f2a16f4](https://github.com/grafana/loki/commit/f2a16f43b27503ba9ee76bac2b44d825ce030e0f)).
- **logcli:** Add gzip compression option ([#14598](https://github.com/grafana/loki/issues/14598)) ([4d3f9f5](https://github.com/grafana/loki/commit/4d3f9f5a7b483b563348c322958486825d314526)).
- **logql:** Micro-optimizations for IP filter ([#14072](https://github.com/grafana/loki/issues/14072)) ([c5083c7](https://github.com/grafana/loki/commit/c5083c7f1ff2f86c74b96c9a87cead78ee6fb3cd)).
- **logql:** Add query user and query source to "executing query" log lines ([#14320](https://github.com/grafana/loki/issues/14320)) ([4d69929](https://github.com/grafana/loki/commit/4d6992982d99a542f1e99af18b691830b71469e0)).
- **promtail:** Add structured metadata to the promtail push API ([#14153](https://github.com/grafana/loki/issues/14153)) ([66cffcb](https://github.com/grafana/loki/commit/66cffcb427bda28af6fbcfcf85a34771db3787bc)).
- **querier:** Move detected field logic to query frontend ([#14212](https://github.com/grafana/loki/issues/14212)) ([36ace66](https://github.com/grafana/loki/commit/36ace66b73e9f9ad2a2d367fbc20803c0d9779c2)).
- **ruler:** Enable ruler store that uses clients from thanos-io/objstore pkg ([#11713](https://github.com/grafana/loki/issues/11713)) ([8bca2e7](https://github.com/grafana/loki/commit/8bca2e76089e0b9894b7a4c18a950f4baaa5a412)).
- **storage:** Add retries for s3 ObjectExists calls ([#14062](https://github.com/grafana/loki/issues/14062)) ([73cbbb0](https://github.com/grafana/loki/commit/73cbbb0f2257b9eb5a3bf5d2cf1f4d4d2490d47d)).
- **storage:** Implement IsRetryableErr for S3ObjectClient ([#14174](https://github.com/grafana/loki/issues/14174)) ([fc90a63](https://github.com/grafana/loki/commit/fc90a63636c689993bd9b568f9c54198bfb1f3ae)).
- **storage:** Introduce new `ObjectExistsWithSize` API to ([#14268](https://github.com/grafana/loki/issues/14268)) ([ac422b3](https://github.com/grafana/loki/commit/ac422b3bc3e822b4525401496a8b73e91d566128)).
- **storage:** AWS backend using thanos.io/objstore ([#11221](https://github.com/grafana/loki/issues/11221)) ([b872246](https://github.com/grafana/loki/commit/b87224647dc88901c61cb4bd571dfda9405a7826)).
- **storage:** Azure backend using thanos.io/objstore ([#11315](https://github.com/grafana/loki/issues/11315)) ([5824e3d](https://github.com/grafana/loki/commit/5824e3d35cd1273ccd1a63d7381098617a7697dd)).
- **storage:** GCS backend using thanos.io/objstore ([#11132](https://github.com/grafana/loki/issues/11132)) ([c059ace](https://github.com/grafana/loki/commit/c059ace53edba79864a567035b120db80addf23c)).
- **structured metadata:** Include structured_metadata size while asserting rate limit ([#14571](https://github.com/grafana/loki/issues/14571)) ([a962edb](https://github.com/grafana/loki/commit/a962edba332f4fdfee29cf11e70019b1b498c258)).

## Deprecations

One of the focuses of Loki 3.0 was cleaning up unused code and old features that had been previously deprecated but not removed. Loki 3.0 removed a number of previous deprecations and introduces some new deprecations. Some of the main areas with changes include:

- [Deprecated storage options](https://grafana.com/docs/loki/<LOKI_VERSION>/storage/) including the deprecation of the BoltDB store.

- [Deprecated configuration options](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/).

- [API endpoint deprecations](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/api/#deprecated-endpoints).

To learn more about breaking changes in this release, refer to the [Upgrade guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

{{< docs/shared source="alloy" lookup="agent-deprecation.md" version="next" >}}

## Upgrade Considerations

For important upgrade guidance, refer to the [Upgrade Guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

- **BREAKING CHANGE - blooms:** Introduce a new block schema (V3) ([#14038](https://github.com/grafana/loki/issues/14038)).
- **BREAKING CHANGE - blooms:** Index structured metadata into blooms ([#14061](https://github.com/grafana/loki/issues/14061)).
- **BREAKING CHANGE - docker:** Remove wget from Promtail docker image([#15101](https://github.com/grafana/loki/issues/15101)).
- **BREAKING CHANGE - operator:** Migrate project layout to kubebuilder go/v4 ([#14447](https://github.com/grafana/loki/issues/14447)).
- **BREAKING CHANGE - operator:** Rename Loki API go module ([#14568](https://github.com/grafana/loki/issues/14568)).
- **BREAKING CHANGE - operator:** Provide default OTLP attribute configuration ([#14410](https://github.com/grafana/loki/issues/14410)).

## Bug fixes

### 3.3.4 (2025-02-14)

- **deps:** Move to Go 1.23.7 ([#16681](https://github.com/grafana/loki/issues/16681)) ([f6c6474](https://github.com/grafana/loki/commit/f6c6474ca6b09eaadd0c9d0f42e337b62cba3f6a)).
- **deps:** Update jwt and oauth2 dependencies for 3.3.x ([#17021](https://github.com/grafana/loki/issues/17021)) ([241f5aa](https://github.com/grafana/loki/commit/241f5aa6490b09b299c1136e70e5b67f85b8632d)).
- **deps:** Update module golang.org/x/crypto to v0.35.0 [security] (release-3.3.x) ([#16590](https://github.com/grafana/loki/issues/16590)) ([454cad2](https://github.com/grafana/loki/commit/454cad207b55e65e7b22c077eaedf84589860746)).
- **deps:** Update module golang.org/x/oauth2 to v0.27.0 [security] (release-3.3.x) ([#16591](https://github.com/grafana/loki/issues/16591)) ([7b35a41](https://github.com/grafana/loki/commit/7b35a41c17be1326df4324188eb43e4e88d16f8d)).
- **docs:** Add a note on docker configuration.md doc to explain accepted values for boolean type ([#16746](https://github.com/grafana/loki/issues/16746)) ([0102107](https://github.com/grafana/loki/commit/01021076ccc2bfe4812fc9116c66190334d3aa91)).

### 3.3.3 (2025-02-14)

- **build:** Update build info 3 3 x ([#16169](https://github.com/grafana/loki/issues/16169)) ([4672625](https://github.com/grafana/loki/commit/4672625c1013079056e1124df5845733ec5928d8)).
- **ci:** Provide correct build info for release builds ([#15939](https://github.com/grafana/loki/issues/15939)) ([#16214](https://github.com/grafana/loki/issues/16214)) ([e5f80d3](https://github.com/grafana/loki/commit/e5f80d3ee618ec1f36d00f469350b9f61ad01cd0))
- **ci:** Revert "fixed `Publish Rendered Helm Chart Diff` workflow" ([#15511](https://github.com/grafana/loki/issues/15511)) ([09d1085](https://github.com/grafana/loki/commit/09d1085fe71e479d329e3a454f36c2e589caae24)).
- correct typo in architecture name (amr64 -&gt; arm64) (backport release-3.3.x) ([#15715](https://github.com/grafana/loki/issues/15715)) ([9acdc33](https://github.com/grafana/loki/commit/9acdc336c14b9f25102b7bb214dfe6f85cc46aef)).
- **deps:** CVE updates for Loki 3.3.2 ([#15645](https://github.com/grafana/loki/issues/15645)) ([01a7a0d](https://github.com/grafana/loki/commit/01a7a0d2ac58973b700bb3ec35aa0bb5bc54f4b3)).

### 3.3.2 (2024-12-18)

- **deps:** backport update module golang.org/x/crypto to v0.31.0 ([#15394](https://github.com/grafana/loki/issues/15394)) ([57a7a2c](https://github.com/grafana/loki/commit/57a7a2caae477fc35a5ad16dec5c083749d4a9d5)).
- **docker-driver:** Fix the docker driver build (backport release-3.3.x) ([#15478](https://github.com/grafana/loki/issues/15478)) ([23b5fc2](https://github.com/grafana/loki/commit/23b5fc2c9b1a77b8776eac70279018956a458fc6)).
- enable metric aggregation in `loki-local-config.yaml` (backport release-3.3.x) ([#15319](https://github.com/grafana/loki/issues/15319)) ([635f04d](https://github.com/grafana/loki/commit/635f04d3deafaadac9e28ff2a11ae89f65b13c00)).

### 3.3.1 (2024-12-04)

- **BREAKING CHANGE - docker:** Remove wget from Promtail docker image([#15101](https://github.com/grafana/loki/issues/15101)).
- **docker:** Move from base-nossl to static. This PR removes the inclusion of glibc into most of the Docker images created by the Loki build system. ([#15203](https://github.com/grafana/loki/issues/15203)).
- **promtail:** Switch Promtail base image from Debian to Ubuntu to fix critical security issues ([#15195](https://github.com/grafana/loki/issues/15195)).

### 3.3.0 (2024-11-19)

- **blooms:** Add tenant limits as dependency to pattern ingester ([#14665](https://github.com/grafana/loki/issues/14665)) ([31eea90](https://github.com/grafana/loki/commit/31eea9042ada6650227eb281a36410ab521817a8)).
- **blooms:** Check length of tasks before accessing first element in slice ([#14634](https://github.com/grafana/loki/issues/14634)) ([601f549](https://github.com/grafana/loki/commit/601f549656efa5ac769a685169d5bc84eff15a35)).
- **blooms:** Copy chunks from ForSeries (backport k227) ([#14864](https://github.com/grafana/loki/issues/14864)) ([d10f79c](https://github.com/grafana/loki/commit/d10f79c700c100d7333e682287aabbaa3c029768)).
- **blooms:** Do not restart builders when planner disconnects (backport k227) ([#14922](https://github.com/grafana/loki/issues/14922)) ([213e8ee](https://github.com/grafana/loki/commit/213e8eeba6e7fb138069e2858d62f1e3c4556a0e)).
- **blooms:** Exclude label filters where label name is part of the series labels. ([#14661](https://github.com/grafana/loki/issues/14661)) ([d1668f6](https://github.com/grafana/loki/commit/d1668f6a110f7119ebb1cc0e582be369b2af95b8)).
- **blooms:** Fix panic in initialization of the bloom planner and builder ([#14110](https://github.com/grafana/loki/issues/14110)) ([8307c42](https://github.com/grafana/loki/commit/8307c42c541e769c9d0133df3856af049a815b73)).
- **blooms:** Fix strategy logger and add task test (backport k227) ([#14921](https://github.com/grafana/loki/issues/14921)) ([dc36a1e](https://github.com/grafana/loki/commit/dc36a1e1288a03b68d269ba261f41ac7c2942962)).
- **blooms:** Fix tenants slice on loadTenantTables (backport k227) ([#14901](https://github.com/grafana/loki/issues/14901)) ([540dd5a](https://github.com/grafana/loki/commit/540dd5a5ccb53bc2ee4236871632c7e1daa7f7e5)).
- **blooms:** Skip multi-tenant TSDBs during bloom planning (backport k227) ([#14888](https://github.com/grafana/loki/issues/14888)) ([631cff3](https://github.com/grafana/loki/commit/631cff345cdab110202d757572fbbf8088c0be87)).
- **blooms:** Improve docs for min and max table offsets (backport k227) ([#14929](https://github.com/grafana/loki/issues/14929)) ([3161fdc](https://github.com/grafana/loki/commit/3161fdcc6dc1e80a86933a59e6af102c10336c39)).
- **blooms:** Fix lint errors ([#14574](https://github.com/grafana/loki/issues/14574)) ([99ef900](https://github.com/grafana/loki/commit/99ef9009e5e2e74f76c865fbb3feaf1559f4b47c)).
- **build:** Use Debian Bullseye base image for build image ([#14368](https://github.com/grafana/loki/issues/14368)) ([3beb8ff](https://github.com/grafana/loki/commit/3beb8ff9cfe7f765b5d5db87892981a223d72f50)).
- **canary:** Reconnect immediately upon tail max duration ([#14287](https://github.com/grafana/loki/issues/14287)) ([9267ee3](https://github.com/grafana/loki/commit/9267ee3561ccbb90589600d7b045f7e05b1b2ee0)).
- **ci:** Fix `Publish Rendered Helm Chart Diff` workflow ([#14365](https://github.com/grafana/loki/issues/14365)) ([6de6420](https://github.com/grafana/loki/commit/6de64209547ec970cb27564be87fe2085307e183)).
- **ci:** Update helm diff rendering workflow ([#14424](https://github.com/grafana/loki/issues/14424)) ([916e511](https://github.com/grafana/loki/commit/916e5115d9099e82834f0d8e123273c75c9cddec)).
- **config:** Copy Alibaba and IBM object storage configuration from common ([#14297](https://github.com/grafana/loki/issues/14297)) ([59ff1ec](https://github.com/grafana/loki/commit/59ff1ece1dacc461d03f71e41c0728396727eee6)).
- **config:** Migrate renovate configuration ([#14646](https://github.com/grafana/loki/issues/14646)) ([a67d8ef](https://github.com/grafana/loki/commit/a67d8ef219aab80071e8256a6cbb18a47c7078e6)).
- **deps:** Update aws-sdk-go-v2 monorepo ([#13986](https://github.com/grafana/loki/issues/13986)) ([6f49123](https://github.com/grafana/loki/commit/6f491233cae226d54d190521d2b935249d88ad05)).
- **deps:** update aws-sdk-go-v2 monorepo ([#14742](https://github.com/grafana/loki/issues/14742)) ([53a1ab7](https://github.com/grafana/loki/commit/53a1ab76257d900b80334d68439d7ff4bfcfd39b)).
- **deps:** update github.com/grafana/dskit digest to 687ec48 ([#14395](https://github.com/grafana/loki/issues/14395)) ([c2f38e1](https://github.com/grafana/loki/commit/c2f38e18c6b8dd134b8f3da164afc9c8625f2f2b)).
- **deps:** update github.com/grafana/dskit digest to 7c41a40 ([#14277](https://github.com/grafana/loki/issues/14277)) ([f39cdbd](https://github.com/grafana/loki/commit/f39cdbd541d85a961db655e70da713be04d9a294)).
- **deps:** update github.com/grafana/dskit digest to 931a021 ([#14032](https://github.com/grafana/loki/issues/14032)) ([7c18642](https://github.com/grafana/loki/commit/7c186425210f892d34a2ccf8ad23b475af8bf9b9)).
- **deps:** update github.com/grafana/dskit digest to b69ac1b ([#14355](https://github.com/grafana/loki/issues/14355)) ([9d7a6ea](https://github.com/grafana/loki/commit/9d7a6ea68053b576553e426d339961d50ee07080)).
- **deps:** update github.com/grafana/dskit digest to f52de24 ([#14319](https://github.com/grafana/loki/issues/14319)) ([a4f3edf](https://github.com/grafana/loki/commit/a4f3edfb52ad4a44a17aaeb753a780b08d6b552c)).
- **deps:** update github.com/twmb/franz-go/pkg/kfake digest to cea7aa5 ([#14590](https://github.com/grafana/loki/issues/14590)) ([688c42a](https://github.com/grafana/loki/commit/688c42a971589be96921ce362c7fc6792368c3da)).
- **deps:** update k8s.io/utils digest to 702e33f ([#14033](https://github.com/grafana/loki/issues/14033)) ([b7eecc7](https://github.com/grafana/loki/commit/b7eecc7a693e96f4d0fe0dcd7583ecdc4dd7283f)).
- **deps:** Update module cloud.google.com/go/bigtable to v1.33.0 ([#14580](https://github.com/grafana/loki/issues/14580)) ([a0920ed](https://github.com/grafana/loki/commit/a0920ed9929080926f0f439182cb2428e938c208)).
- **deps:** Update module cloud.google.com/go/pubsub to v1.45.0 ([#14361](https://github.com/grafana/loki/issues/14361)) ([4351238](https://github.com/grafana/loki/commit/4351238305a680852b6b29a7cdaef69e46042ee4)).
- **deps:** Update module cloud.google.com/go/pubsub to v1.45.1 ([#14650](https://github.com/grafana/loki/issues/14650)) ([f173708](https://github.com/grafana/loki/commit/f17370867b70f65528d98fbfe751d079b5909be0)).
- **deps:** Update module cloud.google.com/go/storage to v1.46.0 ([#14744](https://github.com/grafana/loki/issues/14744)) ([8e45116](https://github.com/grafana/loki/commit/8e451165add426e480b2e691c7c69252d98a2d22)).
- **deps:** Update module github.com/alicebob/miniredis/v2 to v2.33.0 ([#14721](https://github.com/grafana/loki/issues/14721)) ([7bfda25](https://github.com/grafana/loki/commit/7bfda259721c2b3858066ab71d9df09ad35895a6)).
- **deps:** Update module github.com/aws/aws-sdk-go to v1.55.5 ([#14715](https://github.com/grafana/loki/issues/14715)) ([03f0f5a](https://github.com/grafana/loki/commit/03f0f5ab1691550eea59431c9c580530c13bf259)).
- **deps:** Update module github.com/axiomhq/hyperloglog to v0.2.0 ([#14722](https://github.com/grafana/loki/issues/14722)) ([0167b22](https://github.com/grafana/loki/commit/0167b22ac6d4886a1c3157437a3c5b19e327723a)).
- **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.189 ([#14044](https://github.com/grafana/loki/issues/14044)) ([7fb34b4](https://github.com/grafana/loki/commit/7fb34b4884269e7dad7cfa27969f470d9466279d)).
- **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.192 ([#14337](https://github.com/grafana/loki/issues/14337)) ([6f7cae2](https://github.com/grafana/loki/commit/6f7cae2a7aae471c8161bd1e596a31fa89c48ae1)).
- **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.196 ([#14651](https://github.com/grafana/loki/issues/14651)) ([478085a](https://github.com/grafana/loki/commit/478085ae02a0df3b2455211326519dd4aef26499)).
- **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.197 ([#14682](https://github.com/grafana/loki/issues/14682)) ([b898294](https://github.com/grafana/loki/commit/b89829421ee3a4589efe34a4b1332fe659c9d8e7)).
- **deps:** Update module github.com/coder/quartz to v0.1.2 ([#14652](https://github.com/grafana/loki/issues/14652)) ([7459e07](https://github.com/grafana/loki/commit/7459e07adb6aac48b305d50582eac915ea26528e)).
- **deps:** Update module github.com/felixge/fgprof to v0.9.5 ([#14338](https://github.com/grafana/loki/issues/14338)) ([a2ad3aa](https://github.com/grafana/loki/commit/a2ad3aa66940faae4fef7f92aab5a383f576190e)).
- **deps:** Update module github.com/fsouza/fake-gcs-server to v1.50.2 ([#14313](https://github.com/grafana/loki/issues/14313)) ([275c97c](https://github.com/grafana/loki/commit/275c97cec7f70e68c56192c565d53a6c2a18ff78)).
- **deps:** Update module github.com/hashicorp/raft to v1.7.1 ([#14005](https://github.com/grafana/loki/issues/14005)) ([e9cec1d](https://github.com/grafana/loki/commit/e9cec1d159b02977b6104e0006902e0d6b805527)).
- **deps:** Update module github.com/ibm/go-sdk-core/v5 to v5.17.5 ([#14045](https://github.com/grafana/loki/issues/14045)) ([677d217](https://github.com/grafana/loki/commit/677d217533b7d2338e25a8b9b9e8a78045489e7c)).
- **deps:** Update module github.com/ibm/go-sdk-core/v5 to v5.18.1 ([#14716](https://github.com/grafana/loki/issues/14716)) ([8395acd](https://github.com/grafana/loki/commit/8395acd0cbd3db9c6f330bd94a22b194fad35a93)).
- **deps:** Update module github.com/ibm/ibm-cos-sdk-go to v1.11.1 ([#14342](https://github.com/grafana/loki/issues/14342)) ([aa82a7c](https://github.com/grafana/loki/commit/aa82a7c804edd6df99d3fddc581d02c3b7fa6774)).
- **deps:** Update module github.com/klauspost/compress to v1.17.10 ([#14352](https://github.com/grafana/loki/issues/14352)) ([e23c5ed](https://github.com/grafana/loki/commit/e23c5ed9fa97010ef4c985afea25af3922ca215b)).
- **deps:** Update module github.com/minio/minio-go/v7 to v7.0.76 ([#14006](https://github.com/grafana/loki/issues/14006)) ([51f9376](https://github.com/grafana/loki/commit/51f937684795982f0d234ab251017ce2c86c9e20)).
- **deps:** Update module github.com/minio/minio-go/v7 to v7.0.77 ([#14353](https://github.com/grafana/loki/issues/14353)) ([d0e3ef7](https://github.com/grafana/loki/commit/d0e3ef709a222821fd764f6af72308c302faefb3)).
- **deps:** Update module github.com/minio/minio-go/v7 to v7.0.80 ([#14654](https://github.com/grafana/loki/issues/14654)) ([eec2513](https://github.com/grafana/loki/commit/eec25130468eb648c4667361cae7630449af7ef5)).
- **deps:** Update module github.com/ncw/swift/v2 to v2.0.3 ([#14356](https://github.com/grafana/loki/issues/14356)) ([c843288](https://github.com/grafana/loki/commit/c8432887d3d4459ad4bc40deba3a3a3726a2f5eb)).
- **deps:** Update module github.com/prometheus/client_golang to v1.20.5 ([#14655](https://github.com/grafana/loki/issues/14655)) ([e12f843](https://github.com/grafana/loki/commit/e12f8436b4080db54c6d31c6af38416c6fdd7eb4)).
- **deps:** Update module github.com/schollz/progressbar/v3 to v3.17.0 ([#14720](https://github.com/grafana/loki/issues/14720)) ([4419d0f](https://github.com/grafana/loki/commit/4419d0f33e9f4f6f9305d89dd6f2ca47e3a18d8c)).
- **deps:** Update module github.com/shirou/gopsutil/v4 to v4.24.10 ([#14719](https://github.com/grafana/loki/issues/14719)) ([3280376](https://github.com/grafana/loki/commit/32803762781c53ec3fe1bdb64841eb24aeed48f5)).
- **deps:** Update module github.com/shirou/gopsutil/v4 to v4.24.9 ([#14357](https://github.com/grafana/loki/issues/14357)) ([c8e6a9d](https://github.com/grafana/loki/commit/c8e6a9d38f36ccf1f32e634765bb2363628f3710)).
- **deps:** Update module github.com/shopify/sarama to v1.43.3 ([#14059](https://github.com/grafana/loki/issues/14059)) ([1cf4813](https://github.com/grafana/loki/commit/1cf48131d42db7302d6bcf980c355b018fcedb06)).
- **deps:** Update module github.com/spf13/afero to v1.11.0 ([#14060](https://github.com/grafana/loki/issues/14060)) ([bbbd82b](https://github.com/grafana/loki/commit/bbbd82bc73322d662ba81efeda3884efcdc09708)).
- **deps:** Update module go.etcd.io/bbolt to v1.3.11 ([#14358](https://github.com/grafana/loki/issues/14358)) ([b7bccfc](https://github.com/grafana/loki/commit/b7bccfcec3275b1d6d76c7450415ac8744e4d7b0)).
- **deps:** Update module golang.org/x/net to v0.29.0 ([#14341](https://github.com/grafana/loki/issues/14341)) ([1b6b9da](https://github.com/grafana/loki/commit/1b6b9da4e126738037e24d09309b62eac7d54a10)).
- **deps:** Downgrade grpc to fix regression ([#14065](https://github.com/grafana/loki/issues/14065)) ([8c38d46](https://github.com/grafana/loki/commit/8c38d462f5a057497ab222d463223400f2e7b4ab)).
- **deps:** Fix missing dep PartitionRing for Ingester ([#14292](https://github.com/grafana/loki/issues/14292)) ([6354ded](https://github.com/grafana/loki/commit/6354deda90a9430856447e27123b3a33fd1b77a0)).
- **deps:** nix build, downgrade toolchain to go1.23.1 ([#14442](https://github.com/grafana/loki/issues/14442)) ([26dfd62](https://github.com/grafana/loki/commit/26dfd628f0effe2367420f591da36727ebe78806)).
- **deps:** Revert "fix(deps): Update module github.com/shirou/gopsutil/v4 to v4.24.9 ([#14357](https://github.com/grafana/loki/issues/14357))" ([#14437](https://github.com/grafana/loki/issues/14437)) ([d53955b](https://github.com/grafana/loki/commit/d53955bbff5abae63a166099cef1f26b450a31f1)).
- **detected_fields:** Return parsed labels when parsers are passed ([#14047](https://github.com/grafana/loki/issues/14047)) ([aa1ac99](https://github.com/grafana/loki/commit/aa1ac99f4d369c87fd0db4fcf853ebce534e3500)).
- **detected_fields:** Always return empty array as `null` ([#14112](https://github.com/grafana/loki/issues/14112)) ([93009d4](https://github.com/grafana/loki/commit/93009d4e8ce520a3925bf5c0baff940db6c9caba)).
- **docs:** Correct OTLP documentation typo ([#14602](https://github.com/grafana/loki/issues/14602)) ([063c590](https://github.com/grafana/loki/commit/063c590faa4aa30540572c5d6fdc1da8a6a25ee4)).
- **distributor:** Always write detected_level when enabled, even if unknown ([#14464](https://github.com/grafana/loki/issues/14464)) ([41c6b6c](https://github.com/grafana/loki/commit/41c6b6c2c2f5f56ca76cf75ed05689564b9e9dcd)).
- **distributor:** Validate partition ring is kafka is enabled ([#14303](https://github.com/grafana/loki/issues/14303)) ([8438d41](https://github.com/grafana/loki/commit/8438d415931f0a3763d551eb36c3d9f476f70713)).
- **distributor:** Level detection for warning level ([#14444](https://github.com/grafana/loki/issues/14444)) ([242a852](https://github.com/grafana/loki/commit/242a852d7d471351ea294fc09e2b5dc62eec0d03)).
- **explore logs:** Correctly create logfmt string ([#14124](https://github.com/grafana/loki/issues/14124)) ([63e84b4](https://github.com/grafana/loki/commit/63e84b476a9a7b97a121847659172fadbb8a1eee)).
- **explore logs:** Allow any level for aggregated metrics ([#14255](https://github.com/grafana/loki/issues/14255)) ([c001a1d](https://github.com/grafana/loki/commit/c001a1d93af5438fef521460dcba650b44629a93)).
- **helm:** Add missing `loki.storage.azure.chunkDelimiter` parameter to Helm chart ([#14011](https://github.com/grafana/loki/issues/14011)) ([08c70cc](https://github.com/grafana/loki/commit/08c70cca2e7b3a7444b0ec9822a6d5fd58ae70d5)).
- **helm:** Check for `rbac.namespaced` condition before creating roles ([#14201](https://github.com/grafana/loki/issues/14201)) ([3f47f09](https://github.com/grafana/loki/commit/3f47f09a6956719480677f6af02f58394d7f26bb)).
- **helm:** Fix persistence configuration for Memcached ([#14049](https://github.com/grafana/loki/issues/14049)) ([ee6e1cf](https://github.com/grafana/loki/commit/ee6e1cf78864ad3ed915056f695e1f556cc4a22e)).
- **helm:** Fix wrong port name referenced for ingress NetworkPolicy ([#12907](https://github.com/grafana/loki/issues/12907)) ([963a25b](https://github.com/grafana/loki/commit/963a25bf417bbd4171c4d9a2b501330fd663410f)).
- **helm:** Various fixes and enhancements for bloom components ([#14128](https://github.com/grafana/loki/issues/14128)) ([dc0cbd4](https://github.com/grafana/loki/commit/dc0cbd42dcb8e53152573f0baf03ad93aa0d3cd8)).
- **ingester:** Report PSRL error message correctly ([#14187](https://github.com/grafana/loki/issues/14187)) ([a475153](https://github.com/grafana/loki/commit/a47515300a5cfac667eca1ca8e8d1a71e590b7d2)).
- **ingester:** Wait for OwnedStreams service in Ingester startup ([#14208](https://github.com/grafana/loki/issues/14208)) ([a4aee4f](https://github.com/grafana/loki/commit/a4aee4f4ff494b525f68c9c6c1ae3417a8e61ebe)).
- **lambda-promtail:** Add s3 principal to iam policy attached to sqs in lambda-promtail terraform code ([#14619](https://github.com/grafana/loki/issues/14619)) ([db0889e](https://github.com/grafana/loki/commit/db0889e2748b69a5c60d044dfab44bc652f1464d)).
- **logcli:** Check for errors before checking for `exists` when fetching data (backport k227) ([#14906](https://github.com/grafana/loki/issues/14906)) ([31b2a63](https://github.com/grafana/loki/commit/31b2a63ee23098fbd0151ef93020bd1cac093afe)).
- **logcli:** Create new tail response for every line ([#14525](https://github.com/grafana/loki/issues/14525)) ([bcfd0d1](https://github.com/grafana/loki/commit/bcfd0d1ad1c72c6c3861c8263989f2ce683eee08)).
- **logql:** Fix panic in json parsing when using empty array index ([#14393](https://github.com/grafana/loki/issues/14393)) ([833bf0d](https://github.com/grafana/loki/commit/833bf0def6a07e2f58996f54b4b983858750e3e3)).
- **logql:** Update JSONExpressionParser to not unescape extracted values if it is JSON object. ([#14499](https://github.com/grafana/loki/issues/14499)) ([08b1a90](https://github.com/grafana/loki/commit/08b1a9080b03bc041471f1ef72c4e3d7c6aea4f4)).
- **logql:** Allow rename of structured metadata labels ([#13955](https://github.com/grafana/loki/issues/13955)) ([2d4792a](https://github.com/grafana/loki/commit/2d4792a54fb52caa5cd904a17349b04410fae4c0)).
- **logql:** Renamed label should use ParsedLabel category ([#14515](https://github.com/grafana/loki/issues/14515)) ([82fb2f0](https://github.com/grafana/loki/commit/82fb2f0ae2403686b55fdb2fd5be248f706eddab)).
- **logql:** Skipping label if it contains special symbol ([#14068](https://github.com/grafana/loki/issues/14068)) ([55e374e](https://github.com/grafana/loki/commit/55e374e85e7275da8f40d1149defd88f31856f25)).
- **mixin:** Remove pod label from disk usage aggregation ([#14180](https://github.com/grafana/loki/issues/14180)) ([5d45c96](https://github.com/grafana/loki/commit/5d45c96ce12f7f16c21e61db1a78e94a09c16007)).
- **mixins:** Fix loki-resources-overview panel layout ([#14178](https://github.com/grafana/loki/issues/14178)) ([8f54ec6](https://github.com/grafana/loki/commit/8f54ec65881bcad90078464d663af9110ef72603)).
- **mixins:** Add backend path section in loki-operational for single scalable deployment ([#13023](https://github.com/grafana/loki/issues/13023)) ([16881ab](https://github.com/grafana/loki/commit/16881ab0d3b9e9e6bfc37f22ff69f5f1019a0df1)).
- **mixins:** Disk space utilization panels with latest KSM versions ([#13486](https://github.com/grafana/loki/issues/13486)) ([0ea7431](https://github.com/grafana/loki/commit/0ea7431139ae0a18ef4e90bed836a7a6b92ab890)).
- **mixins:** Retention dashboards fix metric name ([#14617](https://github.com/grafana/loki/issues/14617)) ([c762b9b](https://github.com/grafana/loki/commit/c762b9b5d3877e7cbfc41d8ab9a1a4287ebe97b2)).
- **otlp:** Enable service detection for otlp endpoint ([#14036](https://github.com/grafana/loki/issues/14036)) ([4f962ef](https://github.com/grafana/loki/commit/4f962ef7af250fc347dbed15583787d0238f6e9f)).
- **operator:** Add 1x.pico OpenShift UI dropdown menu ([#14660](https://github.com/grafana/loki/issues/14660)) ([4687f37](https://github.com/grafana/loki/commit/4687f377db0a7ae07ffdea354582c882c10b72c4)).
- **operator:** Add missing groupBy label for all rules on OpenShift ([#14279](https://github.com/grafana/loki/issues/14279)) ([ce7b2e8](https://github.com/grafana/loki/commit/ce7b2e89d9470e4e6a61a94f2b51ff8b938b5a5e)).
- **operator:** Correctly ignore again BlotDB dashboards ([#14587](https://github.com/grafana/loki/issues/14587)) ([4879d10](https://github.com/grafana/loki/commit/4879d106bbeea29e331ddb7c9a49274600190032)).
- **operator:** Disable automatic discovery of service name ([#14506](https://github.com/grafana/loki/issues/14506)) ([3834c74](https://github.com/grafana/loki/commit/3834c74966b307411732cd3cbaf66305008b10eb)).
- **operator:** Disable log level discovery for OpenShift tenancy modes ([#14613](https://github.com/grafana/loki/issues/14613)) ([5034d34](https://github.com/grafana/loki/commit/5034d34ad23451954ea2459c341456da8d93d020)).
- **operator:** Fix building the size-calculator image ([#14573](https://github.com/grafana/loki/issues/14573)) ([a79b8fe](https://github.com/grafana/loki/commit/a79b8fe7802964cbb96bde75a7502a8b1e8a23ab)).
- **operator:** Fix make build target for size-calculator ([#14551](https://github.com/grafana/loki/issues/14551)) ([e727187](https://github.com/grafana/loki/commit/e727187ec3be2f10c80e984d00c40dad0308b036)).
- **operator:** Move OTLP attribute for statefulset name to stream labels ([#14630](https://github.com/grafana/loki/issues/14630)) ([5df3594](https://github.com/grafana/loki/commit/5df3594f791d77031c53d7b0f5b01191de8a23f2)).
- **operator:** Use empty initialized pod status map when no pods ([#14314](https://github.com/grafana/loki/issues/14314)) ([6f533ed](https://github.com/grafana/loki/commit/6f533ed4386ee2db61680a9021934bfe9a9ba749)).
- **operator:** Update renovate ignore for operator API with new module path ([#14581](https://github.com/grafana/loki/issues/14581)) ([c9b2907](https://github.com/grafana/loki/commit/c9b2907f3c97cf0a14837c0b27cad7a06d84f447)).
- **pattern:** Fixes latency metric namespace for tee to pattern ([#14241](https://github.com/grafana/loki/issues/14241)) ([ae955ed](https://github.com/grafana/loki/commit/ae955ed30d841675dbb9e30327b84728050e724a)).
- **promtail:** Fix configuration unmarshalling ([#14408](https://github.com/grafana/loki/issues/14408)) ([a05431f](https://github.com/grafana/loki/commit/a05431f879a8c29fac6356b6c46be62133c3e93c)).
- **promtail:** Fix parser for azureeventhubs message without time field ([#14218](https://github.com/grafana/loki/issues/14218)) ([2e62abb](https://github.com/grafana/loki/commit/2e62abbf47c47041027baf240722b3d76e7bd9a3)).
- **promtail:** Validate scrape_config job name, do not allow duplicate job names ([#13719](https://github.com/grafana/loki/issues/13719)) ([f2d3499](https://github.com/grafana/loki/commit/f2d349924c2aa0453e49fc607603a189108666ec)).
- **querier:** Propagate query stats from quantile & topk queries ([#13831](https://github.com/grafana/loki/issues/13831)) ([78b275b](https://github.com/grafana/loki/commit/78b275bf1092d834065315207666d6fd1c505f06)).
- **querier:** Correct _extracted logic in detected fields ([#14064](https://github.com/grafana/loki/issues/14064)) ([1b3ba53](https://github.com/grafana/loki/commit/1b3ba530b8fab9aac999387a135a76a62de3e000)).
- **querier:** Timestamp. Time should be called with milliseconds ([#14196](https://github.com/grafana/loki/issues/14196)) ([f8d9143](https://github.com/grafana/loki/commit/f8d9143eead92d8727053e065c2d3403f689e4b5)).
- **querier:** Report correct status code for metric and log queries in metrics.go ([#12102](https://github.com/grafana/loki/issues/12102)) ([900751c](https://github.com/grafana/loki/commit/900751c3bb008c50441c47eef3927a27201b1a11)).
- **query:** Fix bug in query result marshaling for invalid utf8 characters ([#14585](https://github.com/grafana/loki/issues/14585)) ([f411a07](https://github.com/grafana/loki/commit/f411a0795af67630a0a70a88ce64fa071de50a56)).
- Remove usage of unsafe string in label adapter unmarshall ([#14216](https://github.com/grafana/loki/issues/14216)) ([758364c](https://github.com/grafana/loki/commit/758364c7775fba22a84498089a476c21f737d32f)).
- Rename misspelled filename ([#14237](https://github.com/grafana/loki/issues/14237)) ([cf1d4a3](https://github.com/grafana/loki/commit/cf1d4a31af5c376e82756eaaab267369f862265d)).
- **sharding:** Apply offset to both `from` and `through` in shard request ([#14256](https://github.com/grafana/loki/issues/14256)) ([17c472d](https://github.com/grafana/loki/commit/17c472d9abea6b1cae21de5fe2af8b365bdaf137)).
- **storage:** Add additional validation for timeout while retrieving headers ([#14217](https://github.com/grafana/loki/issues/14217)) ([8322e51](https://github.com/grafana/loki/commit/8322e518e68de286b2bc58cf15ea9fe947eeec86)).
- **storage:** Do not retain span logger created with index set initialized at query time ([#14027](https://github.com/grafana/loki/issues/14027)) ([4e41744](https://github.com/grafana/loki/commit/4e4174400fba410b9f32e0e43c1d866d283a9e62)).
- **storage:** Expand matching for additional variations ([#14221](https://github.com/grafana/loki/issues/14221)) ([71d7291](https://github.com/grafana/loki/commit/71d7291c9c00c3887d9a509991eb4d3e15ae8699)).
- **storage:** Disable client retries when congestion control is enabled in S3([#14588](https://github.com/grafana/loki/issues/14588)) ([cff9f43](https://github.com/grafana/loki/commit/cff9f43dd6fb5e90c875c14c138ea39b58202dff)).
- **storage:** Have GetObject check for canceled context ([#14420](https://github.com/grafana/loki/issues/14420)) ([5f325aa](https://github.com/grafana/loki/commit/5f325aac56e41848979e9e33a4a443e31ea525d0)).
- **storage:** Transform `ObjectExistsWithSize` into `GetAttributes` ([#14329](https://github.com/grafana/loki/issues/14329)) ([2f56f50](https://github.com/grafana/loki/commit/2f56f50cc6591ca482358933c719d005446d0c01)).
- **storage:** Update AWS storage timeout error for Go 1.23 behavior ([#14226](https://github.com/grafana/loki/issues/14226)) ([a4642b5](https://github.com/grafana/loki/commit/a4642b55e9b374ccd974b662e7b17a2389c3dcbd)).

# v3.4

Grafana Labs and the Loki team are excited to announce the release of Loki 3.4. Here's a summary of new enhancements and important fixes.

For a full list of all changes and fixes, refer to the [CHANGELOG](https://github.com/grafana/loki/blob/release-3.4.x/CHANGELOG.md).

## Features and enhancements

Key features in Loki 3.4.0 include the following:

- **Time sharding ingestion:** Add per-tenant time sharding for long out-of-order ingestion. The setting `shard_streams.time_sharding_enabled` is a per-tenant config that splits incoming log streams by injecting a new `__time_shard__` synthetic label in such a way that no stream will ever be longer than `1h` (or, rather, `max_chunk_age`/2). This allows the ingestion of very old logs, because the logs in these new streams will never be "too far behind" by design. ([#14711](https://github.com/grafana/loki/issues/14711)) ([0d6d68d](https://github.com/grafana/loki/commit/0d6d68d9bfd145164e48718c794c50cea5de02fa)).

- **Thanos object client:** Previously Loki and Grafana Enterprise Logs used different Go packages to talk to the underlying object store. Migrating to Thanos means that there is now a single object client across Loki and GEL, and across the other Grafana backend databases: Mimir and Pyroscope.([#14891](https://github.com/grafana/loki/issues/14891)) ([fb6789d](https://github.com/grafana/loki/commit/fb6789d9df1b76aa8fb08b6e9db951a6f7e93aae)), ([#14638](https://github.com/grafana/loki/issues/14638)) ([8958eb9](https://github.com/grafana/loki/commit/8958eb91eda56a5334d0bdb683a9fb592cd5d17e)), ([#14867](https://github.com/grafana/loki/issues/14867)) ([947a66f](https://github.com/grafana/loki/commit/947a66f35e1e02505290f52f2eee17c3e281cbbc)), ([#14793](https://github.com/grafana/loki/issues/14793)) ([cfc3819](https://github.com/grafana/loki/commit/cfc38194b46711c23c84bbb52cf1f11e6db99bda)), ([#14937](https://github.com/grafana/loki/issues/14937)) ([a629212](https://github.com/grafana/loki/commit/a629212ceba6bde476995c0550216f0431fb380b)). The documentation now includes [configuration examples](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/examples/thanos-storage-configs/) using Thanos-based storage clients.

- **API:** Add support for [Parquet file format](https://parquet.apache.org/docs/file-format/) as a query response format. ([#15408](https://github.com/grafana/loki/issues/15408)) ([d0c11a6](https://github.com/grafana/loki/commit/d0c11a689e7835628259f845cd41da818c7e1e18)).

- **Structured metadata:** Introduces a new feature,based on per-tenant configuration, that allows for extraction of "fields" into structured metadata at ingest time. Fields can either be regular labels, structured metadata keys, or keys from `logfmt` or `json` formatted log lines. ([#15188](https://github.com/grafana/loki/issues/15188)) ([7033091](https://github.com/grafana/loki/commit/7033091bc9f60505e1492779c91f0d0c0e42dfc7)).

- **lambda-promtail:** Adds support for relabeling log entries in lambda-promtail using Prometheus-style relabel configs. This allows for more flexible label manipulation and filtering of log entries before they are sent to Loki. ([#15600](https://github.com/grafana/loki/issues/15600)) ([c41a8b4](https://github.com/grafana/loki/commit/c41a8b4b9b57cfc98445992ecab0b6378e831e03)).

- **Kafka:** Loki 3.4 adds an Apache Kafka development environment to support ongoing development work([#15603](https://github.com/grafana/loki/issues/15603)) ([226e9f1](https://github.com/grafana/loki/commit/226e9f101aab2faf32b2d6b0c93f6e5832782c25)).

- **Helm charts:** Loki 3.4 includes multiple improvements to the Helm charts, including: Added support for overrides exporter ([#15630](https://github.com/grafana/loki/issues/15630)) ([db72164](https://github.com/grafana/loki/commit/db72164215280b8b47780036ca866f065e681f60)), Expose topologySpreadConstraints for admin-api pods ([#14995](https://github.com/grafana/loki/issues/14995)) ([1b93086](https://github.com/grafana/loki/commit/1b93086a2082ec18a8f766dec7778b04e991ba10)), Expose topologySpreadConstraints for distributed deployment ([#14853](https://github.com/grafana/loki/issues/14853)) ([d075107](https://github.com/grafana/loki/commit/d0751071a4db971788c93f89a1add97887cb2e6d)), Compute ConfigMap/Secret checksum only over .data content ([#15177](https://github.com/grafana/loki/issues/15177)) ([990f71c](https://github.com/grafana/loki/commit/990f71c5e90a1b9921820ac06555b9ecd1a6539c)), Do not explicitly template `managed-by`, as it should be set at install time by the installation manager. ([#14763](https://github.com/grafana/loki/issues/14763)) ([10b7478](https://github.com/grafana/loki/commit/10b74781cf17b4df13815a4aa4ab0f9b17da5216)).

- **Query acceleration with Bloom filters (experimental)**: The Bloom filters feature introduced in Loki 3.0 remains experimental. In Loki 3.3, there was a major shift in how Loki leverages bloom filters — pivoting from free-text search to harnessing the power of structured metadata. In Loki 3.4, work continues on the new Bloom Planner ([#14874](https://github.com/grafana/loki/issues/14874)) ([b646861](https://github.com/grafana/loki/commit/b646861e770e6903538bb22dd551686aaeb7349d)).

- **Explore logs**: The Explore apps suite for metrics, logs, traces, and profiles has a new name: Grafana Drilldown. To learn more about why we made this switch, you can read about it in this [blog post](https://grafana.com/blog/2025/02/20/grafana-drilldown-apps-the-improved-queryless-experience-formerly-known-as-the-explore-apps/). The new name is reflected in the Grafana Cloud user interface and navigation as of 2025-02-20, and it will be part of the Grafana 11.6 release in March.

- **Docs:** This release includes a heavily revised [labels topic](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/) and a new [Cardinality topic](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/cardinality/). This release also marks the return of the [sizing guidance page](https://grafana.com/docs/loki/latest/setup/size/), newly updated based on Grafana Labs experience running Loki. Continuing updates to the Query section include a revised [logcli topic](https://grafana.com/docs/loki/latest/query/logcli/getting-started/) and new [logcli tutorial](https://grafana.com/docs/loki/latest/query/logcli/logcli-tutorial/). There is also two additional new tutorials: [Deploying Loki on Azure tutorial](https://grafana.com/docs/loki/latest/setup/install/helm/deployment-guides/azure/) and [Kubernetes Monitoring Helm tutorial](https://grafana.com/docs/loki/latest/send-data/k8s-monitoring-helm/).

{{< admonition type="note" >}}
Note that when appropriate, documentation updates have been backported to previous releases.
{{< /admonition >}}

Other improvements include the following:

- **analytics:** Add TLS config to the analytics client ([#15227](https://github.com/grafana/loki/issues/15227)) ([2c5eabd](https://github.com/grafana/loki/commit/2c5eabdb727bb169cb2e5c0b95b31c2ba562fa23)).
- **approx_topk:** Reduce memory usage of HyperLogLog in approx_topk. ([#15559](https://github.com/grafana/loki/issues/15559)) ([bef2043](https://github.com/grafana/loki/commit/bef20431cbbf302e584c4eea2eb423537bcf86e7)).
- **approx_topk:** Improve `approx_topk` performance by reducing allocations. ([#15450](https://github.com/grafana/loki/issues/15450)) ([04994ca](https://github.com/grafana/loki/commit/04994ca8218e82711aa77679d1392c05935d2416)).
- **blooms:** Add task timing and sizing metrics ([#15032](https://github.com/grafana/loki/issues/15032)) ([79cccfd](https://github.com/grafana/loki/commit/79cccfd50bddc150b8f1e89f66774a117c91f06c)).
- **blooms:** Apply task timeout in bloom builder ([#14988](https://github.com/grafana/loki/issues/14988)) ([f92dde0](https://github.com/grafana/loki/commit/f92dde0e413804dcd79167efa5b9729ad200b60a)).
- **blooms:** Prefetch bloom blocks as soon as they are built ([#15050](https://github.com/grafana/loki/issues/15050)) ([b406015](https://github.com/grafana/loki/commit/b4060154d198e17bef8ba0fbb1c99bb5c93a412d)).
- **blooms:** Compute chunkrefs for series right before sending task to builder ([#14808](https://github.com/grafana/loki/issues/14808)) ([66e6b1c](https://github.com/grafana/loki/commit/66e6b1c1df3bf9e2444a29b2e166061a18ba15f2)).
- **blooms:** Replace JSON lib for encoding/decoding metas ([#14767](https://github.com/grafana/loki/issues/14767)) ([57f16a0](https://github.com/grafana/loki/commit/57f16a0a26f7ee4c4885c451a8d21fe6bba41897)).
- **distributor:** Add a metric ingestion time SM sanitization ([#15222](https://github.com/grafana/loki/issues/15222)) ([e9d0c3e](https://github.com/grafana/loki/commit/e9d0c3ee527597d1db61a20f01efca1189ac0079)).
- **docker compose:** enable feature toggle ([#14858](https://github.com/grafana/loki/issues/14858)) ([8f243ee](https://github.com/grafana/loki/commit/8f243eef907c0345182b1a66effd1ea6cd156fae)).
- **docker driver:** Add ARM64 build and release of the Docker driver in Drone pipeline([#9247](https://github.com/grafana/loki/issues/9247)) ([e950251](https://github.com/grafana/loki/commit/e950251744955e93027dbd06d0b99c541e1ce32a)).
- **lambda-promtail:** Add terraform variable for s3 bucket notification filter prefix ([#15381](https://github.com/grafana/loki/issues/15381)) ([3dd8d7d](https://github.com/grafana/loki/commit/3dd8d7d95f788e126e087c3a782d30b8aa375cc4)).
- **logcli:** Add opt-in support for ProxyFromEnvironment in logcli ([#11742](https://github.com/grafana/loki/issues/11742)) ([#14950](https://github.com/grafana/loki/issues/14950)) ([c56b95d](https://github.com/grafana/loki/commit/c56b95d29805e40d902ca797156cdfc92cde265f)).
- **logcli:** Include common labels ([#15611](https://github.com/grafana/loki/issues/15611)) ([639ac74](https://github.com/grafana/loki/commit/639ac74c485bd99ceddc5de498831cbc4f8982ff)).
- **logql:** Allow line_format bypass template execution if possible ([#15411](https://github.com/grafana/loki/issues/15411)) ([2150fbc](https://github.com/grafana/loki/commit/2150fbcf52f96e41175b1d49b65dfd3e4155f1ec)).
- **metrics:** Add lines skipped metric to pattern ingesters ([#14997](https://github.com/grafana/loki/issues/14997)) ([dea5d78](https://github.com/grafana/loki/commit/dea5d7883a05856c99e18db976ff88e25a40ac31)).
- **operator:** Add support for managed GCP WorkloadIdentity ([#14752](https://github.com/grafana/loki/issues/14752)) ([7635a5c](https://github.com/grafana/loki/commit/7635a5cffa80cf5ff627b8de2ed00fa96c058629)).
- **otel/labels:** Add otlp attribute deployment.environment.name to list of default labels ([#14747](https://github.com/grafana/loki/issues/14747)) ([2d407bd](https://github.com/grafana/loki/commit/2d407bd308064bf014cd5f9718119b8fc625fea6)).
- **querier:** Present DF bytes values in queryable format ([#15272](https://github.com/grafana/loki/issues/15272)) ([f5d62bd](https://github.com/grafana/loki/commit/f5d62bd505c19ceb287bbcb65359156f7273e75f)).
- **querier:** Use context propagation to call the same ingester in GetChunksID as Query ([#15186](https://github.com/grafana/loki/issues/15186)) ([70d9587](https://github.com/grafana/loki/commit/70d9587863410b5610606bf20210402a2438ffd3)).
- **ruler:** Add Rule Name and Type to Query Tags ([#15055](https://github.com/grafana/loki/issues/15055)) ([b272573](https://github.com/grafana/loki/commit/b272573efc695f0725312f88505e34e7c419378f)).
- **storage:** Add objstore support for Swift using thanos.io/objstore ([#11672](https://github.com/grafana/loki/issues/11672)) ([44523e0](https://github.com/grafana/loki/commit/44523e085dc2576b8929659250f8ae421a084443)).
- **storage:** Skip writeback for chunks fetched by queriers older than a duration ([#15605](https://github.com/grafana/loki/issues/15605)) ([3b8d993](https://github.com/grafana/loki/commit/3b8d993500cb715f27e5618d90afe5c8ac9d501d)).
- **storage:** Support config timeout for AlibabaCloud OSS requests ([#14856](https://github.com/grafana/loki/issues/14856)) ([ac2e21f](https://github.com/grafana/loki/commit/ac2e21fb424b2a031a021ad49c6970dc7131055e)).
- **storage:** Allow TSDB index creation in memory only ([#14732](https://github.com/grafana/loki/issues/14732)) ([831c0d5](https://github.com/grafana/loki/commit/831c0d56b805318e95ed366a22605a4804dae1c8)).
- **structured metadata:** Sanitize structured metadata during ingestion in the distributor ([#15141](https://github.com/grafana/loki/issues/15141)) ([be4f17e](https://github.com/grafana/loki/commit/be4f17eefe3df81dae060bf86890fe1054aeb2f2)).

### 3.4.5 (2025-07-11)

* **storage:** Add a configuration option for custom GCS endpoints ([#16419](https://github.com/grafana/loki/issues/16419)) ([#18419](https://github.com/grafana/loki/issues/18419)) ([b2c87ba](https://github.com/grafana/loki/commit/b2c87ba6b68ddcad2f9284df5821a4ce299a3c56)).

## Deprecations

One of the focuses of Loki 3.0 was cleaning up unused code and old features that had been previously deprecated but not removed. Loki 3.0 removed a number of previous deprecations and introduces some new deprecations. Some of the main areas with changes include:

- [Promtail](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/promtail/) is deprecated, as the code has been merged into Grafana Alloy. You can find migration documentation and a utility to convert your Promtail configuration to Alloy configuration in the [Alloy documentation](https://grafana.com/docs/alloy/latest/set-up/migrate/from-promtail/). Note that this deprecation does NOT include the lambda-promtail client.

- [Deprecated storage options](https://grafana.com/docs/loki/<LOKI_VERSION>/storage/) including the deprecation of the BoltDB store.

- [Deprecated configuration options](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/).

- [API endpoint deprecations](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/api/#deprecated-endpoints).

To learn more about breaking changes in this release, refer to the [Upgrade guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

{{< docs/shared source="alloy" lookup="agent-deprecation.md" version="next" >}}

## Upgrade Considerations

For important upgrade guidance, refer to the [Upgrade Guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

- **BREAKING CHANGE - Docker image:** Remove `wget` from Promtail docker image ([#15101](https://github.com/grafana/loki/issues/15101)).

## Bug fixes

### 3.4.6 (2025-09-11)

* **deps:** Update Go minor version ([#19137](https://github.com/grafana/loki/issues/19137)) ([2b7b94f](https://github.com/grafana/loki/commit/2b7b94ffbfe4b5434aff6a9bd8cdd69c440650ac)).

### 3.4.5 (2025-07-11)

* **frontend:** Allow resolution of v6 addresses. (backport release-3.4.x) ([#18260](https://github.com/grafana/loki/issues/18260)) ([93e0880](https://github.com/grafana/loki/commit/93e088034d871cbce5d479413a8fd16f04a02ebe)).
* **memberlist:** Allow resolution of advertise address from IPv6  interfaces defined in common `instance_interface_names`.(backport release-3.4.x) ([#18256](https://github.com/grafana/loki/issues/18256)) ([640cd33](https://github.com/grafana/loki/commit/640cd331fe89bbd020e8c2359e1403a85d1f441d)).
* **WAL:** Handle WAL corruption properly on startup (backport release-3.4.x) ([#18410](https://github.com/grafana/loki/issues/18410)) ([1ecc9e4](https://github.com/grafana/loki/commit/1ecc9e4315d5bb223525b4101c409a169fe8413e)).

### 3.4.4 (2025-06-09)

- **ci:** Update release code 3.4 ([#18015](https://github.com/grafana/loki/issues/18015)) ([4108c5d](https://github.com/grafana/loki/commit/4108c5d6f23d8daf54376ab9a3bb126a597af933)).
- **deps:** Move to Go 1.23.8 ([#17806](https://github.com/grafana/loki/issues/17806)) ([2e18686](https://github.com/grafana/loki/commit/2e186867b34af5d34fe79b4e2909611ea6c17c73)).
- **deps:** Update module golang.org/x/net to v0.38.0 [security] (release-3.4.x) ([#17273](https://github.com/grafana/loki/issues/17273)) ([57cd6ce](https://github.com/grafana/loki/commit/57cd6ce4d14439aab277944502851cfccc5e7f06)).
- **docs:** Update configuration.md (backport release-3.4.x) ([#17298](https://github.com/grafana/loki/issues/17298)) ([cc8db57](https://github.com/grafana/loki/commit/cc8db57f1576a7444b001fe466cb06a3698ffa67)).

### 3.4.3 (2025-04-03)

- **deps:** Move to Go 1.23.7 ([#16682](https://github.com/grafana/loki/issues/16682)) ([5de86f1](https://github.com/grafana/loki/commit/5de86f1375ddbd7efbd1f786298163de68747484)).
- **deps:** Update jwt, oauth and go-redis dependencies ([#17023](https://github.com/grafana/loki/issues/17023)) ([e038d0f](https://github.com/grafana/loki/commit/e038d0fc3af9cc2adecbda81dbc39163be22ba11)).
- **deps:** Update module golang.org/x/crypto to v0.35.0 [security] (release-3.4.x) ([#16588](https://github.com/grafana/loki/issues/16588)) ([184fe1a](https://github.com/grafana/loki/commit/184fe1ac64c9e519bd173edf91eb0e408dbc0a39)).
- **deps:** Update module golang.org/x/oauth2 to v0.27.0 [security] (release-3.4.x) ([#16589](https://github.com/grafana/loki/issues/16589)) ([bead91e](https://github.com/grafana/loki/commit/bead91ef11934a73a7722a03bebd2d8875428cbc)).
- **docs:** Add a note on docker configuration.md doc to explain accepted values for boolean type (backport release-3.4.x) ([#16743](https://github.com/grafana/loki/issues/16743)) ([c39780d](https://github.com/grafana/loki/commit/c39780d44979d32e121c008c2194f4ec9dd09f78)).

### 3.4.2 (2025-02-14)

- **blooms:** Initialize bloom gateway client only once (backport release-3.4.x) ([#16268](https://github.com/grafana/loki/issues/16268)) ([1b9829b](https://github.com/grafana/loki/commit/1b9829b48935ffc9f99d741cab11864ac9d0f35c))
- **ci:** do not disable CGO in Makefile (backport release-3.4.x) ([#16272](https://github.com/grafana/loki/issues/16272)) ([4fa045d](https://github.com/grafana/loki/commit/4fa045d3807f4de0543b06e6ce79b89afb741adc))

### 3.4.1 (2025-02-12)

- **docker:** Update the build info in docker images ([#16225](https://github.com/grafana/loki/issues/16225)) ([4484080](https://github.com/grafana/loki/commit/4484080c7662817e945c0276ba15d05315e93194)).

### 3.4.0 (2025-02-12)

- **aggregated_metrics:** Fix the IsError method causing retries ([#15296](https://github.com/grafana/loki/issues/15296)) ([7a7b3b1](https://github.com/grafana/loki/commit/7a7b3b1ee3c73d4ed7c802becb98ec795e042572)).
- **azureblob:** Return error if Azure returns no service principal token ([#13195](https://github.com/grafana/loki/issues/13195)) ([e98a86b](https://github.com/grafana/loki/commit/e98a86b3d63e250c33571a44df61821fb864aab4)).
- **blooms:** Copy chunks from ForSeries ([#14863](https://github.com/grafana/loki/issues/14863)) ([bfc2890](https://github.com/grafana/loki/commit/bfc289028ec2a7405cfa8356af4fa1cc2e8f3c81)).
- **blooms:** Do not restart builders when planner disconnects ([#14783](https://github.com/grafana/loki/issues/14783)) ([7669385](https://github.com/grafana/loki/commit/766938525f3e94d3e94f8adf2f7230a38593de09)).
- **blooms:** Enable metas cache on backend when running in ssd mode ([#15275](https://github.com/grafana/loki/issues/15275)) ([4cce207](https://github.com/grafana/loki/commit/4cce207c6ab767f13cf55347b9cfe0a3a595b8d9)).
- **blooms:** Fix check for skipping most recent data when filtering blooms ([#15300](https://github.com/grafana/loki/issues/15300)) ([78d3c44](https://github.com/grafana/loki/commit/78d3c4415662b4003fc275b29aff053d531d90f0)).
- **blooms:** Fix strategy logger and add task test ([#14857](https://github.com/grafana/loki/issues/14857)) ([ab2721d](https://github.com/grafana/loki/commit/ab2721d7ee7cb573baf53905df8fd3e9a676cbf1)).
- **blooms:** Fix tenants slice on loadTenantTables ([#14900](https://github.com/grafana/loki/issues/14900)) ([a8f7851](https://github.com/grafana/loki/commit/a8f7851161607427e67dab0ec0e94d0202268222)).
- **blooms:** Match series to newest block only ([#15481](https://github.com/grafana/loki/issues/15481)) ([5846ea2](https://github.com/grafana/loki/commit/5846ea2ea044ed7168313363f70fb55b2f8e2ff0)).
- **blooms:** Skip multi-tenant TSDBs during bloom planning ([#14770](https://github.com/grafana/loki/issues/14770)) ([76d35cc](https://github.com/grafana/loki/commit/76d35cc97f0cca4cd0375f5003a4028eec9cf654)).
- **blooms:** Fix data race in bloombuild tests ([#15360](https://github.com/grafana/loki/issues/15360)) ([aa7b849](https://github.com/grafana/loki/commit/aa7b849e758caaefc59dfd59ff575e4886ba8e5e)).
- **ci:** Fix test compile error ([#15404](https://github.com/grafana/loki/issues/15404)) ([db2b6db](https://github.com/grafana/loki/commit/db2b6dbfed67d051bf15458945c29daca771897e)).
- **ci:** Remove submodule once and for all ([#15495](https://github.com/grafana/loki/issues/15495)) ([80e8e60](https://github.com/grafana/loki/commit/80e8e60e2df071ccc9fe8c4ae6a5c68a12180da1)).
- **ci:** Revert "fixed `Publish Rendered Helm Chart Diff` workflow" ([#15506](https://github.com/grafana/loki/issues/15506)) ([6c49cc0](https://github.com/grafana/loki/commit/6c49cc07305e823fc0f405f515b39e3ddc649303)).
- **ci:** Transform TestChunkSize into a benchmark ([#15361](https://github.com/grafana/loki/issues/15361)) ([fed43db](https://github.com/grafana/loki/commit/fed43db0d98db530c52d76396a66dbdbc0a195f9)).
- **ci:** Update Python version for Helm CI ([#15420](https://github.com/grafana/loki/issues/15420)) ([bdbb009](https://github.com/grafana/loki/commit/bdbb00912fec8869a82f6530618e1ae31204a8ba)).
- **deps:** Downgrade hyperloglog to 0.2.0 ([#15424](https://github.com/grafana/loki/issues/15424)) ([#15426](https://github.com/grafana/loki/issues/15426)) ([3b0d815](https://github.com/grafana/loki/commit/3b0d815749f943fc856aad66d0c23174bf9ada60)).
- **deps:** Update aws-sdk-go-v2 monorepo ([#14820](https://github.com/grafana/loki/issues/14820)) ([a355840](https://github.com/grafana/loki/commit/a355840cec12a9e80696e6b2fc4cbab764b45f69)).
- **deps:** Update module cloud.google.com/go/bigtable to v1.34.0 ([#15581](https://github.com/grafana/loki/issues/15581)) ([9f28f69](https://github.com/grafana/loki/commit/9f28f6938d6c6484836b35765a95a778d47b3a4a)).
- **deps:** Update module cloud.google.com/go/pubsub to v1.45.3 ([#15294](https://github.com/grafana/loki/issues/15294)) ([672fb08](https://github.com/grafana/loki/commit/672fb08d48611667176fa60cf348f7d34093bac8)).
- **deps:** Update module cloud.google.com/go/storage to v1.50.0 ([#15690](https://github.com/grafana/loki/issues/15690)) ([efd3ec3](https://github.com/grafana/loki/commit/efd3ec30feb783d22ea99869b25ea05703591061)).
- **deps:** Update module github.com/alecthomas/chroma/v2 to v2.15.0 ([#15596](https://github.com/grafana/loki/issues/15596)) ([a2d3057](https://github.com/grafana/loki/commit/a2d305760f2ca4868eddf94cf0d86f758856d86b)).
- **deps:** Update module github.com/alicebob/miniredis/v2 to v2.34.0 ([#15501](https://github.com/grafana/loki/issues/15501)) ([61b4ddf](https://github.com/grafana/loki/commit/61b4ddfa9186759705512f75bad0068b27e6202b)).
- **deps:** Update module github.com/aliyun/aliyun-oss-go-sdk to v3 ([#14976](https://github.com/grafana/loki/issues/14976)) ([64081c0](https://github.com/grafana/loki/commit/64081c0680ec2443dd7bd7efe251e444641fb5e4)).
- **deps:** Update module github.com/aws/aws-sdk-go-v2 to v1.32.8 ([#15680](https://github.com/grafana/loki/issues/15680)) ([42c43ec](https://github.com/grafana/loki/commit/42c43ecb4213f0a83e016afe5b8a89cd36757aa2)).
- **deps:** Update module github.com/aws/aws-sdk-go-v2/config to v1.28.9 ([#15663](https://github.com/grafana/loki/issues/15663)) ([c5c9a2e](https://github.com/grafana/loki/commit/c5c9a2ef81d3cb8a4e99f4cd6de053fe23573153)).
- **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.72.2 ([#15685](https://github.com/grafana/loki/issues/15685)) ([0e329e1](https://github.com/grafana/loki/commit/0e329e18cc17e6b6675e0379ea0e924198fe5711)).
- **deps:** Update module github.com/axiomhq/hyperloglog to v0.2.3 ([#15641](https://github.com/grafana/loki/issues/15641)) ([b6df82e](https://github.com/grafana/loki/commit/b6df82e9654fb8ae529fdc442ae2427971fa7955)).
- **deps:** Update module github.com/azure/azure-storage-blob-go to v0.15.0 ([#14746](https://github.com/grafana/loki/issues/14746)) ([ad9bdcd](https://github.com/grafana/loki/commit/ad9bdcd769dd852aa416e90f5039db24a3d1ae91)).
- **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.213 ([#15686](https://github.com/grafana/loki/issues/15686)) ([95dac7c](https://github.com/grafana/loki/commit/95dac7c16af9f2a73c8d8fab1db29bdecb8ad68a)).
- **deps:** Update module github.com/bmatcuk/doublestar to v4 ([#14977](https://github.com/grafana/loki/issues/14977)) ([a2b66d3](https://github.com/grafana/loki/commit/a2b66d35bdc29c54e44e1d5f5eb56536be98a41a)).
- **deps:** Update module github.com/docker/docker to v27.3.1+incompatible ([#14753](https://github.com/grafana/loki/issues/14753)) ([b4a926c](https://github.com/grafana/loki/commit/b4a926c406ef14603e3a52002d6cdd54abe192cd)).
- **deps:** Update module github.com/fatih/color to v1.18.0 ([#14754](https://github.com/grafana/loki/issues/14754)) ([8fc9b28](https://github.com/grafana/loki/commit/8fc9b2818a45067ebaa9f709c3af1bd58c8f9eb3)).
- **deps:** Update module github.com/fsnotify/fsnotify to v1.8.0 ([#14755](https://github.com/grafana/loki/issues/14755)) ([0704f5d](https://github.com/grafana/loki/commit/0704f5d3655b7db2d8658689da02c1a5ab4c1556)).
- **deps:** Update module github.com/go-redis/redis/v8 to v9 ([#14978](https://github.com/grafana/loki/issues/14978)) ([33a2148](https://github.com/grafana/loki/commit/33a214880bd950a10db85c0418538261014e7fa1)).
- **deps:** Update module github.com/gocql/gocql to v1 ([#15016](https://github.com/grafana/loki/issues/15016)) ([2a51081](https://github.com/grafana/loki/commit/2a510816ae2cc262f708dd12fc936175452ed55f)).
- **deps:** Update module github.com/golang/snappy to v0.0.4 ([#15415](https://github.com/grafana/loki/issues/15415)) ([035f871](https://github.com/grafana/loki/commit/035f87169a662188e05fcaf4abf2ab2baefcc0d9)).
- **deps:** Update module github.com/grafana/loki/v3 to v3.2.1 ([#14826](https://github.com/grafana/loki/issues/14826)) ([b8509fc](https://github.com/grafana/loki/commit/b8509fc39809cee3e3f2866be4582fb04ff1dcf8)).
- **deps:** Update module github.com/grpc-ecosystem/go-grpc-middleware/v2 to v2.2.0 ([#15392](https://github.com/grafana/loki/issues/15392)) ([6b7c3fc](https://github.com/grafana/loki/commit/6b7c3fc8cf87773b30086046b36b7799b24db5d3)).
- **deps:** Update module github.com/hashicorp/consul/api to v1.31.0 ([#15540](https://github.com/grafana/loki/issues/15540)) ([8207a9b](https://github.com/grafana/loki/commit/8207a9bf5e4aa4e963ed6ca05509ca10f720032f)).
- **deps:** Update module github.com/hashicorp/golang-lru to v2 ([#14979](https://github.com/grafana/loki/issues/14979)) ([3abb3b1](https://github.com/grafana/loki/commit/3abb3b116d157ce26ecbb02e6c74b562e31f732d)).
- **deps:** Update module github.com/heroku/x to v0.4.1 ([#15370](https://github.com/grafana/loki/issues/15370)) ([20f5414](https://github.com/grafana/loki/commit/20f5414b746fc82bfa940046d8cce09e939e578c)).
- **deps:** Update module github.com/ibm/go-sdk-core/v5 to v5.18.5 ([#15673](https://github.com/grafana/loki/issues/15673)) ([b7ec8e3](https://github.com/grafana/loki/commit/b7ec8e3a54f32a92652c9dd36e0036acfee4e787)).
- **deps:** Update module github.com/ibm/ibm-cos-sdk-go to v1.12.0 ([#15228](https://github.com/grafana/loki/issues/15228)) ([5689e2e](https://github.com/grafana/loki/commit/5689e2e90e57cca8e51581aeeefbad633676ad08)).
- **deps:** Update module github.com/ibm/sarama to v1.45.0 ([#15636](https://github.com/grafana/loki/issues/15636)) ([9e13abd](https://github.com/grafana/loki/commit/9e13abd454536646b8334a31af4cd600222a569e)).
- **deps:** Update module github.com/imdario/mergo to v1 ([#15035](https://github.com/grafana/loki/issues/15035)) ([be24862](https://github.com/grafana/loki/commit/be24862f53fb44616662c924b7ef6d237297606c)).
- **deps:** Update module github.com/influxdata/telegraf to v1.33.1 ([#15688](https://github.com/grafana/loki/issues/15688)) ([d57492c](https://github.com/grafana/loki/commit/d57492cb89ffd1cb00ad0a035e977cbb12d86157)).
- **deps:** Update module github.com/klauspost/compress to v1.17.11 ([#15451](https://github.com/grafana/loki/issues/15451)) ([6e8bd9d](https://github.com/grafana/loki/commit/6e8bd9d23a4028f26b9ae3bc244f219212f67f56)).
- **deps:** Update module github.com/masterminds/sprig/v3 to v3.3.0 ([#14899](https://github.com/grafana/loki/issues/14899)) ([8874878](https://github.com/grafana/loki/commit/8874878636404ce2d930c0ad5294da870449fb3d)).
- **deps:** Update module github.com/minio/minio-go/v7 to v7.0.82 ([#15417](https://github.com/grafana/loki/issues/15417)) ([7b8d82e](https://github.com/grafana/loki/commit/7b8d82edd75727a75a06605ef2f2ed3a1c48f4fa)).
- **deps:** Update module github.com/opentracing-contrib/go-stdlib to v1.1.0 ([#15221](https://github.com/grafana/loki/issues/15221)) ([9f7892a](https://github.com/grafana/loki/commit/9f7892a55f9e0952e248ff820628681851d8511f)).
- **deps:** Update module github.com/pierrec/lz4 to v2.6.1+incompatible ([#15502](https://github.com/grafana/loki/issues/15502)) ([60f031a](https://github.com/grafana/loki/commit/60f031a372069ee2c4931aba940a367d5be0cb50)).
- **deps:** Update module github.com/pierrec/lz4/v4 to v4.1.22 ([#15400](https://github.com/grafana/loki/issues/15400)) ([36bda72](https://github.com/grafana/loki/commit/36bda72d671d646707ea95e2403a26faf8f1c74f)).
- **deps:** Update module github.com/prometheus/common to v0.61.0 ([#15352](https://github.com/grafana/loki/issues/15352)) ([860c25d](https://github.com/grafana/loki/commit/860c25d898e7f3e905664362acde09fe628effea)).
- **deps:** Update module github.com/schollz/progressbar/v3 to v3.18.0 ([#15691](https://github.com/grafana/loki/issues/15691)) ([5f902b4](https://github.com/grafana/loki/commit/5f902b460d2be187dcf63d26b8553d186f9ff350)).
- **deps:** Update module github.com/shirou/gopsutil/v4 to v4.24.12 ([#15569](https://github.com/grafana/loki/issues/15569)) ([bc43878](https://github.com/grafana/loki/commit/bc43878af899189da1bb9033ecaabdef25fba3ba)).
- **deps:** Update module github.com/sony/gobreaker/v2 to v2.1.0 ([#15556](https://github.com/grafana/loki/issues/15556)) ([d188d06](https://github.com/grafana/loki/commit/d188d06370ff0a721da55c72c4f885822ef27b0a)).
- **deps:** Update module github.com/spf13/afero to v1.12.0 ([#15696](https://github.com/grafana/loki/issues/15696)) ([94cfb90](https://github.com/grafana/loki/commit/94cfb90e7c34eb8c7cdf84f52dd623603ac9a408)).
- **deps:** Update module github.com/stretchr/testify to v1.10.0 ([#15091](https://github.com/grafana/loki/issues/15091)) ([f65ab13](https://github.com/grafana/loki/commit/f65ab130725dc25c9d546fa4d5fb1e4a6d26009e)).
- **deps:** Update module github.com/twmb/franz-go/pkg/kadm to v1.14.0 ([#14911](https://github.com/grafana/loki/issues/14911)) ([1b59525](https://github.com/grafana/loki/commit/1b595259e26edd8828eeb1a682d33c4ddc417694)).
- **deps:** Update module go.opentelemetry.io/collector/pdata to v1.22.0 ([#15452](https://github.com/grafana/loki/issues/15452)) ([661ae79](https://github.com/grafana/loki/commit/661ae79beda14b10bd89bb3beb4fbd8d56c78322)).
- **deps:** Update module go.opentelemetry.io/otel/trace to v1.33.0 ([#15454](https://github.com/grafana/loki/issues/15454)) ([82c2c56](https://github.com/grafana/loki/commit/82c2c565270e4a2d301af265ca474a68018b7f54)).
- **deps:** Update module golang.org/x/crypto to v0.32.0 ([#15610](https://github.com/grafana/loki/issues/15610)) ([db1188f](https://github.com/grafana/loki/commit/db1188fc48341d6fb27f01caa262b78beb7ff4c9)).
- **deps:** Update module golang.org/x/net to v0.34.0 ([#15612](https://github.com/grafana/loki/issues/15612)) ([0b25012](https://github.com/grafana/loki/commit/0b250125907beae1bf5e080341d54478c0420647)).
- **deps:** Update module golang.org/x/oauth2 to v0.25.0 ([#15590](https://github.com/grafana/loki/issues/15590)) ([ccee7f9](https://github.com/grafana/loki/commit/ccee7f96e0947512092017d4e43b1d1e09d1b981)).
- **deps:** Update module golang.org/x/sync to v0.10.0 ([#15253](https://github.com/grafana/loki/issues/15253)) ([3150f07](https://github.com/grafana/loki/commit/3150f073db00a7bc6000fd84035e9f3a68497bdc)).
- **deps:** Update module golang.org/x/sys to v0.29.0 ([#15591](https://github.com/grafana/loki/issues/15591)) ([efb9a81](https://github.com/grafana/loki/commit/efb9a815d8bfb979171089bc41e330214ecf7f8b)).
- **deps:** Update module golang.org/x/text to v0.21.0 ([#15266](https://github.com/grafana/loki/issues/15266)) ([818446c](https://github.com/grafana/loki/commit/818446c4234384cec8feef93690574d4f68b7c77)).
- **deps:** Update module golang.org/x/time to v0.9.0 ([#15592](https://github.com/grafana/loki/issues/15592)) ([5401e78](https://github.com/grafana/loki/commit/5401e785548a8986fb5426190719a013befd4f05)).
- **deps:** Update module google.golang.org/api to v0.214.0 ([#15519](https://github.com/grafana/loki/issues/15519)) ([e34944d](https://github.com/grafana/loki/commit/e34944d60f6653280d02f2a80b69cd4ec6f6a5b4)).
- **deps:** Update module google.golang.org/grpc to v1.68.1 ([#15269](https://github.com/grafana/loki/issues/15269)) ([b607fe0](https://github.com/grafana/loki/commit/b607fe0969a2bfb88f63446c88e8a03fa582e0f8)).
- **deps:** Update module google.golang.org/protobuf to v1.36.2 ([#15635](https://github.com/grafana/loki/issues/15635)) ([74885a2](https://github.com/grafana/loki/commit/74885a20c735e8cd02bb590336bb7ae81c54bb33)).
- **deps:** Update module gopkg.in/alecthomas/kingpin.v2 to v2.4.0 ([#14970](https://github.com/grafana/loki/issues/14970)) ([37d0bb8](https://github.com/grafana/loki/commit/37d0bb89355f1e898cfe38cf2511cb02afa0f2bb)).
- **deps:** Update module k8s.io/apimachinery to v0.32.0 ([#15398](https://github.com/grafana/loki/issues/15398)) ([fada02c](https://github.com/grafana/loki/commit/fada02c668329da4b6aeaf39c40af8f487717fb5)).
- **deps:** Update module k8s.io/klog to v2 ([#15046](https://github.com/grafana/loki/issues/15046)) ([734caf5](https://github.com/grafana/loki/commit/734caf5908917d1431bf53e557523862fd760923)).
- **distributor:** Fix data race in distributor tests ([#15423](https://github.com/grafana/loki/issues/15423)) ([b8168a8](https://github.com/grafana/loki/commit/b8168a87f5ad052408b5c5619ffae038b7bbe7d5)).
- **distributor:** Return error 422 (Unprocessable Content) when push request does not contain any streams ([#13706](https://github.com/grafana/loki/issues/13706)) ([17bf32b](https://github.com/grafana/loki/commit/17bf32bc50598aaf620fda269511c6db64359dbd)).
- **docs:** Correct typo in architecture name (amr64 -&gt; arm64) ([#15707](https://github.com/grafana/loki/issues/15707)) ([068f81d](https://github.com/grafana/loki/commit/068f81d91d12b6faa38c2e98e4f53b8214cc4b5f)).
- **docs:** Improve docs for min and max table offsets ([#14890](https://github.com/grafana/loki/issues/14890)) ([fd9d332](https://github.com/grafana/loki/commit/fd9d33241d4a5cdf0066233bf8bdda69ea23a9f7)).
- **docs:** Change "did received" to "did receive" in NOTES.txt ([#15584](https://github.com/grafana/loki/issues/15584)) ([9191eaf](https://github.com/grafana/loki/commit/9191eafc735b1f80bf3a7520abd0670aea7fc398)).
- **helm/loki:** Add namespace to Loki templates for ruler and index-gateway ([#14838](https://github.com/grafana/loki/issues/14838)) ([733d99f](https://github.com/grafana/loki/commit/733d99fc5f4a5b6083e4c66b0f674535c5a48b37)).
- **helm/loki:** Allow prefixing of rollout-group value and ingester name ([#15063](https://github.com/grafana/loki/issues/15063)) ([f4accc1](https://github.com/grafana/loki/commit/f4accc115f44f565f4e4b0672548aeaccbbe4817)).
- **helm:** Add `appProtocol: tcp` to headless backend grpc ports ([#14507](https://github.com/grafana/loki/issues/14507)) ([400f5af](https://github.com/grafana/loki/commit/400f5afda0e45b335ade5b1debb2d9ac5b0e5409)).
- **helm:** Add conditional to include ruler config only when enabled ([#15385](https://github.com/grafana/loki/issues/15385)) ([3badbb3](https://github.com/grafana/loki/commit/3badbb3d961bcd2fcb1194af3fbc568346945982)).
- **helm:** Add default wal dir to ruler config ([#14920](https://github.com/grafana/loki/issues/14920)) ([1a6751f](https://github.com/grafana/loki/commit/1a6751fc17bb10ba0018057f56f5cc4395c547a3)).
- **helm:** Fix statefulset templates to not show diffs in ArgoCD ([#15192](https://github.com/grafana/loki/issues/15192)) ([3c71c83](https://github.com/grafana/loki/commit/3c71c8375e6b3780b9e36b5bcf46d820c57787c7)).
- **helm:** Split ingester HPA when zoneAwareReplication is enabled ([#14565](https://github.com/grafana/loki/issues/14565)) ([80e46f7](https://github.com/grafana/loki/commit/80e46f7cd212a3ffbdcd9d833f431461d304043a)).
- **helm:** FIx Yaml templates incorrectly annotated ([#15207](https://github.com/grafana/loki/issues/15207)) ([acd4d11](https://github.com/grafana/loki/commit/acd4d112b0721a2c24fbfad6c5c317f33b07c3b4)).
- **helm:** Disable Helm test pod when test.enabled=false ([#15346](https://github.com/grafana/loki/issues/15346)) ([1fb4e2b](https://github.com/grafana/loki/commit/1fb4e2b22b527ac74b9a234f59b3551fbc900e80)).
- **ingester:** data race in ingester test ([#15465](https://github.com/grafana/loki/issues/15465)) ([04f621e](https://github.com/grafana/loki/commit/04f621ec861aea8935589bf0c9475ac437612bce)).
- **ingester:** Add stream creation error logging ([#15640](https://github.com/grafana/loki/issues/15640)) ([f63c16f](https://github.com/grafana/loki/commit/f63c16f77d086735e7a8a92f07b13e0773593018)).
- **kafka:** Ensure partition-reader starts up correctly ([#14845](https://github.com/grafana/loki/issues/14845)) ([b2f3d2e](https://github.com/grafana/loki/commit/b2f3d2e36e91e89e449c2246e0f917524dbd8938)).
- **kafka:** Fix incorrect error check in autoCommitLoop ([#15149](https://github.com/grafana/loki/issues/15149)) ([4b5925a](https://github.com/grafana/loki/commit/4b5925a28e61f29a20aaabda3a159386a8ba7638)).
- **kafka:** Read from beginning if no committed offset ([#14865](https://github.com/grafana/loki/issues/14865)) ([1b6da11](https://github.com/grafana/loki/commit/1b6da11ee22ccab6545a8fdd71386d997946b6fa)).
- **kafka:** Use KafkaEndOffset instead of -1 ([#15099](https://github.com/grafana/loki/issues/15099)) ([2113526](https://github.com/grafana/loki/commit/21135269ad3d6206616970f7222f43dfd7f729ee)).
- **kafka:** Use separate variable to track the consume offset ([#15095](https://github.com/grafana/loki/issues/15095)) ([161a192](https://github.com/grafana/loki/commit/161a192aec9cfd22b307f0190ea12b7684375889)).
- Keep the service_name label after discovery in push.ParseLokiRequest before calculating retention and passing to the usage tracker ([#14960](https://github.com/grafana/loki/issues/14960)) ([71d4d6a](https://github.com/grafana/loki/commit/71d4d6a8c61e44adbd4fe745c04ffdc33e01e802))
- **lambda-promtail:** Correct S3 bucket owner label key to prevent retrieval errors ([#14913](https://github.com/grafana/loki/issues/14913)) ([85aaf63](https://github.com/grafana/loki/commit/85aaf6367068226e1eb98e1175a85795ffc97324)).
- **lambda-promtail:** Remove s3 bucket ownership verification ([#15383](https://github.com/grafana/loki/issues/15383)) ([bf73014](https://github.com/grafana/loki/commit/bf7301470453161b510c2f745dd7bc94f952509e)).
- **logcli:** Check for errors before checking for `exists` when fetching data ([#14780](https://github.com/grafana/loki/issues/14780)) ([9e7d2f2](https://github.com/grafana/loki/commit/9e7d2f2982b5d9e0c3904c951ecc70039ff9cb68)).
- **logql:** Allow comparison against zero bytes ([#15217](https://github.com/grafana/loki/issues/15217)) ([17f1972](https://github.com/grafana/loki/commit/17f197206d9e6f001754d854d26ba6c08b3c6934)).
- **memberlist:** Respect interface names config for advertise address ([#13940](https://github.com/grafana/loki/issues/13940)) ([751324c](https://github.com/grafana/loki/commit/751324cffe27ff5b1dd4bdf35dd512ccc2016a04)).
- **metric_aggregation:** Fix duplicate metrics registration ([#15142](https://github.com/grafana/loki/issues/15142)) ([215f994](https://github.com/grafana/loki/commit/215f994d0e0ac002ff8b5237c58cb88b82751dbe)).
- **metrics:** Correctly propagate index stats to metrics.go log line ([#14941](https://github.com/grafana/loki/issues/14941)) ([ff111dc](https://github.com/grafana/loki/commit/ff111dce050d42939f291c87dc2dcacf2a30c83c)).
- **metrics:** Enable metric aggregation in `loki-local-config.yaml` ([#15302](https://github.com/grafana/loki/issues/15302)) ([c96b750](https://github.com/grafana/loki/commit/c96b750a3f6bee9a76482ead8823e71405e5d68b)).
- **operator:** Add log attribute for level to structured metadata ([#14776](https://github.com/grafana/loki/issues/14776)) ([036c131](https://github.com/grafana/loki/commit/036c1312d5fd797dda9839dc30d5028e8b7f6c59)).
- **operator:** Fix maximum OpenShift version ([#14764](https://github.com/grafana/loki/issues/14764)) ([cc496c6](https://github.com/grafana/loki/commit/cc496c68b76b56c457f6c30d696de23698addaa9)).
- **operator:** Fix operator release pipeline warnings ([#14817](https://github.com/grafana/loki/issues/14817)) ([e707a3d](https://github.com/grafana/loki/commit/e707a3dfb2e25df76585ab42f715e361233479c8)).
- **operator:** Fix RBAC permission for poddisruptionbudgets ([#15107](https://github.com/grafana/loki/issues/15107)) ([f00d07a](https://github.com/grafana/loki/commit/f00d07a471a5b8c222ee4e2105942725934e93d1)).
- **operator:** Publish images on docker hub upon release ([#14786](https://github.com/grafana/loki/issues/14786)) ([126ebed](https://github.com/grafana/loki/commit/126ebeda0dd977cee14190c4127ee43f5999e73e)).
- **operator:** Update kube-rbac-proxy to upstream registry ([#14809](https://github.com/grafana/loki/issues/14809)) ([568d22f](https://github.com/grafana/loki/commit/568d22f598c763942e3d277aa6005050281e5f49)).
- **otlp:** Write protobuf status on error ([#15097](https://github.com/grafana/loki/issues/15097)) ([63a2442](https://github.com/grafana/loki/commit/63a2442191751e32aaafd6227e1602dfa3a95caa)).
- **pattern ingester:** Remove `enabled` field in loki-local-config.yaml ([#15059](https://github.com/grafana/loki/issues/15059)) ([7410f10](https://github.com/grafana/loki/commit/7410f1012fe5d4c12f636bee6d073cfeba10a109)).
- **promtail:** Fix API level node filtering of Kubernetes SD to only fetch pod metadata for pods of the current node ([#14267](https://github.com/grafana/loki/issues/14267)) ([d2b6fd8](https://github.com/grafana/loki/commit/d2b6fd8cf569f863ba97a4c43c0868d048dd3974)).

- **promtail:** Fix Windows forward event crash. [#16155](https://github.com/grafana/loki/pull/16155) ([1c0d558](https://github.com/grafana/loki/commit/1c0d558a615d34966fd1eb0b7e2aa78266e4a7ab)).

- **querier:** Fix goroutine leak in queryrange downstreamer ([#15665](https://github.com/grafana/loki/issues/15665)) ([5f476a3](https://github.com/grafana/loki/commit/5f476a39e130a22dc5b641af84a0c902f83a4ad3)).
- **querier:** Legacy label endpoint route ([#14933](https://github.com/grafana/loki/issues/14933)) ([8ce321e](https://github.com/grafana/loki/commit/8ce321ef83645f7ae5dce9bb746e01b32ef02866)).
- **querier:** Mitigate ingester race between Query & GetChunkIDs ([#15178](https://github.com/grafana/loki/issues/15178)) ([bd46e4c](https://github.com/grafana/loki/commit/bd46e4c7b27798209894b5d515a42f06a25df02e)).
- **querier:** Show correct setting in error message ([#14842](https://github.com/grafana/loki/issues/14842)) ([f5ae015](https://github.com/grafana/loki/commit/f5ae015b5ab1807df69f63177cb1e94c218178bd)).
- **queries:** Bytes unit detection in detected fields ([#15525](https://github.com/grafana/loki/issues/15525)) ([8e260fe](https://github.com/grafana/loki/commit/8e260fef834877b2fe677146b1fcaf2dd8b01c4d)).
- Remove unsafe pkg usage from util.mempool ([#15428](https://github.com/grafana/loki/issues/15428)) ([e6d82b9](https://github.com/grafana/loki/commit/e6d82b9253a46a08120dccf4317fd1d25c1d4ca3)).
- **ruler:** Fix loki ruler generator url left parameter url encoding ([#15601](https://github.com/grafana/loki/issues/15601)) ([e347eb7](https://github.com/grafana/loki/commit/e347eb7c803d573e56b4730bbaeb83bbd4a6596d)).
- Shut down delete client on local rule-evaluator ([#15345](https://github.com/grafana/loki/issues/15345)) ([42469cc](https://github.com/grafana/loki/commit/42469ccece89674c46941644c1c0ce832ed624fc)).
- **storage:** Data race in chunk client hedging tests ([#15466](https://github.com/grafana/loki/issues/15466)) ([fad06ee](https://github.com/grafana/loki/commit/fad06ee692576854ff2098e440237a1223a32715)).
- **storage:** Do not retain copy of chunk while indexing a new chunk in tsdb while processing delete requests ([#15541](https://github.com/grafana/loki/issues/15541)) ([ff19955](https://github.com/grafana/loki/commit/ff199557d2ecf15d68dcd1856e07aae7f1ab8579)).
- **structured metadata:** Structured metadata not always passed down for processing deletes ([#15526](https://github.com/grafana/loki/issues/15526)) ([a3808b6](https://github.com/grafana/loki/commit/a3808b6b1dc8235d4f3529c99014728a7a4dd636)).
- **wal:** Ensure successive WAL replays don't overwrite each other ([#14848](https://github.com/grafana/loki/issues/14848)) ([ec95ed1](https://github.com/grafana/loki/commit/ec95ed1d0530d02f38d5bdc225604a6422486cac)).

# v3.5

Grafana Labs and the Loki team are excited to announce the release of Loki 3.5. Here's a summary of new enhancements and important fixes.

For a full list of all changes and fixes, refer to the [CHANGELOG](https://github.com/grafana/loki/blob/release-3.5.x/CHANGELOG.md).

## Features and enhancements

Key features in Loki 3.5.0 include the following:

* **Helm charts:** Loki 3.5 includes multiple improvements to the Helm charts, including: Adding support for additional global variables in the Helm chart ([#16062](https://github.com/grafana/loki/issues/16062)) ([e5f31b3](https://github.com/grafana/loki/commit/e5f31b39f714fc065d360343163cfca9dbe58c85)), adding the `extraContainers` property to the backend Loki pods ([#16205](https://github.com/grafana/loki/issues/16205)) ([528a1c6](https://github.com/grafana/loki/commit/528a1c612867547ad32b52c6306a0de25126e0bd))[closes [#16164](https://github.com/grafana/loki/issues/16164)], adding `tpl()` support back to read, write and backend pods ([#16664](https://github.com/grafana/loki/issues/16664)) ([af05c38](https://github.com/grafana/loki/commit/af05c38fe719977f057c248e8d7ec297b6772fb2)), and allowing provisioner to be namespaced ([#16091](https://github.com/grafana/loki/issues/16091)) ([af85ecf](https://github.com/grafana/loki/commit/af85ecf943249dd45cfc4a118c465f30034d3867)).

* **storage:** Add support for `SQLite' as a database for storing delete requests ([#16437](https://github.com/grafana/loki/issues/16437)) ([5b33e65](https://github.com/grafana/loki/commit/5b33e65bcd8109122af15ac4189756f834f03f04)) and when using SQLite, use the stored completion time to reduce the requests considered for querytime filtering ([#16600](https://github.com/grafana/loki/issues/16600)) ([3277321](https://github.com/grafana/loki/commit/3277321e215072e119af700db77a3ef9e6b72e6b)).

* **Docs:** This release includes a heavily revised [meta-monitoring section](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/) to support new monitoring requirements for Grafana Enterprise Logs customers.

Other improvements include the following:

* **distributor:** Add simulated latency ([#16733](https://github.com/grafana/loki/issues/16733)) ([7119f6e](https://github.com/grafana/loki/commit/7119f6e480f940ab99029b3ba0dba0137e35c7a0))
* **distributor:** Do not enforce labels vs agg metric stream ([#16696](https://github.com/grafana/loki/issues/16696)) ([25c0620](https://github.com/grafana/loki/commit/25c0620da149a9b420df044c5b23222296a006c5))
* **distributor:** Enforce limits in distributors ([#16705](https://github.com/grafana/loki/issues/16705)) ([bc2111e](https://github.com/grafana/loki/commit/bc2111e8f10f3a73465c30a4ceffae39d87255d6))
* **distributor:** Update log level discovery from json to detect nested fields ([#16026](https://github.com/grafana/loki/issues/16026)) ([86dc47b](https://github.com/grafana/loki/commit/86dc47b3db6ea243b7c98640f820a1d0ffec1a87))
* **distributor:** Use stream labels instead of hash in rate limit reasons ([#16738](https://github.com/grafana/loki/issues/16738)) ([fdf8632](https://github.com/grafana/loki/commit/fdf86323ce080c12fe029a6d048e30a737d3cb46))
* **distributors:** Add dry-run mode for checking limits in distributors ([#16754](https://github.com/grafana/loki/issues/16754)) ([d6c499b](https://github.com/grafana/loki/commit/d6c499bbc35b3f8bc3d7172ee5d71b23436c97ea))
* **kafka:** tenant topics ([#15977](https://github.com/grafana/loki/issues/15977)) ([c258419](https://github.com/grafana/loki/commit/c25841901c9dcfdf067eb6052938077fd18e4ddc))
* **ksonnet:** Assert for bad `max_query_lookback` configuration ([#16362](https://github.com/grafana/loki/issues/16362)) ([073c94c](https://github.com/grafana/loki/commit/073c94c4062c1bdb91d8c87f1e299aca4b1ad801))
* **ksonnet:** Add no_schedule_constraints option to distributors ([#16029](https://github.com/grafana/loki/issues/16029)) ([9e9a09d](https://github.com/grafana/loki/commit/9e9a09db51a9f6f1954ecbe905a54de26e8a9629))
* **lambda-promtail:** Improve relabel configuration parsing and testing ([#16100](https://github.com/grafana/loki/issues/16100)) ([2587f34](https://github.com/grafana/loki/commit/2587f3425ddb055517444055bcae73cb69510399))
* **logical planning:** Planner playground ([#16403](https://github.com/grafana/loki/issues/16403)) ([89f0ed7](https://github.com/grafana/loki/commit/89f0ed795f5cd8bb9a116604950e59dde02d6d43))
* **logql:** Add LogQL Benchmark Suite ([#16434](https://github.com/grafana/loki/issues/16434)) ([1b57297](https://github.com/grafana/loki/commit/1b57297a468c11386f4ce92209347132e0c96ca4))
* **operator:** Add configuration option for dropping OTLP attributes ([#15857](https://github.com/grafana/loki/issues/15857)) ([bd1ea23](https://github.com/grafana/loki/commit/bd1ea2313220b9aa187ff5b252f55512434c1865))
* **operator:** Add support for Swift TLS CA configuration ([#15260](https://github.com/grafana/loki/issues/15260)) ([62a72f6](https://github.com/grafana/loki/commit/62a72f6405d5a5cbb0814fab8010c215b1782c93))
* **operator:** Enable time-based stream-sharding ([#16390](https://github.com/grafana/loki/issues/16390)) ([1b4f1f5](https://github.com/grafana/loki/commit/1b4f1f57fa6ceac405f5ab5d0314de9d97e309d3))
* **operator:** Update Loki operand to v3.4.2 ([#16360](https://github.com/grafana/loki/issues/16360)) ([42f87d3](https://github.com/grafana/loki/commit/42f87d3064b60438df09d1eb799fd50e5753d1f8))
* **storage:** Add S3 chunk delimiter configuration to support MinIO running on Windows ([#16319](https://github.com/grafana/loki/issues/16319)) ([8429b0f](https://github.com/grafana/loki/commit/8429b0fbd92f0a89269bbe8dc9e0ccddd15007e1))
* **storage:** Add a configuration option for custom GCS endpoints ([#16419](https://github.com/grafana/loki/issues/16419)) ([f1ebd97](https://github.com/grafana/loki/commit/f1ebd970e618326845a7d3f4181bebaaadc6f4de))

## Deprecations

One of the focuses of Loki 3.0 was cleaning up unused code and old features that had been previously deprecated but not removed. Loki 3.0 removed a number of previous deprecations and introduces some new deprecations. Some of the main areas with changes include:

- [Promtail](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/promtail/) is deprecated, as the code has been merged into Grafana Alloy. You can find migration documentation and a utility to convert your Promtail configuration to Alloy configuration in the [Alloy documentation](https://grafana.com/docs/alloy/latest/set-up/migrate/from-promtail/). Note that this deprecation does NOT include the lambda-promtail client.

- [Deprecated storage options](https://grafana.com/docs/loki/<LOKI_VERSION>/storage/) including the deprecation of the BoltDB store.

- [Deprecated configuration options](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/).

- [API endpoint deprecations](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/api/#deprecated-endpoints).

To learn more about breaking changes in this release, refer to the [Upgrade guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

{{< docs/shared source="alloy" lookup="agent-deprecation.md" version="next" >}}

## Upgrade Considerations

For important upgrade guidance, refer to the [Upgrade Guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

- **BREAKING CHANGE - ksonnet:** Clean-up deprecated configurations ([#16007](https://github.com/grafana/loki/issues/16007)).
- **BREAKING CHANGE - operator:** Add configuration option for dropping OTLP attributes ([#15857](https://github.com/grafana/loki/issues/15857)).

## Bug fixes

### 3.5.12 (2026-03-12)

* **deps:** Upgrade go to 1.25.8 ([#21116](https://github.com/grafana/loki/issues/21116)) ([3b305e3](https://github.com/grafana/loki/commit/3b305e3fe11a23c02ba34eb0382e06d5df0e3e07))

### 3.5.11 (2026-02-23)

* **alloc:** Set a limit on preallocations (backport release-3.5.x) ([#20919](https://github.com/grafana/loki/issues/20919)) ([1906dc2](https://github.com/grafana/loki/commit/1906dc2b29e1f1ffc37d9a6c9375d1590d7078a7)).

### 3.5.10 (2026-02-11)

* **deps:**  Update to go 1.25.7 ([#20694](https://github.com/grafana/loki/issues/20694)) ([#20752](https://github.com/grafana/loki/issues/20752)) ([dd71399](https://github.com/grafana/loki/commit/dd713991cb9f55d96a5ef5da33522297fc52ddbf)).
* build UI deps ([#20762](https://github.com/grafana/loki/issues/20762)) ([6d1e9ea](https://github.com/grafana/loki/commit/6d1e9eab7720ef486973db1a20076e357c6c081f)).

### 3.5.9 (2025-12-10)

* **deps:** Bump x/crypto module ([#20016](https://github.com/grafana/loki/issues/20016)) ([b21123b](https://github.com/grafana/loki/commit/b21123b27ee7f52164ff6a15d1422a21033685fa)).
* **deps:** Update go version to 1.24.11 ([#20200](https://github.com/grafana/loki/issues/20200)) ([79d8922](https://github.com/grafana/loki/commit/79d8922b9c4df8a89efe72afb25daabd90dadf2c)).
* **logger:** correct log level verbosity in rule evaluation ([#19519](https://github.com/grafana/loki/issues/19519)) (backport release-3.5.x) ([#19857](https://github.com/grafana/loki/issues/19857)) ([562d87d](https://github.com/grafana/loki/commit/562d87d330c42fc4a7a5c6d69c1785442c4e9dc7)).

### 3.5.8 (2025-11-05)

* **deps:** Update to Go version 1.24.9 ([#19702](https://github.com/grafana/loki/issues/19702)) ([cf33543](https://github.com/grafana/loki/commit/cf335437062aa68f3ba657c89012ab499dedb738)).

### 3.5.7 (2025-10-13)

This release fixes a build issue in 3.5.6 where it was not built with Go 1.24.8 as expected, but Go 1.24.1.

* **build:** Update rpm signature config ([#19476](https://github.com/grafana/loki/issues/19476)) ([#19483](https://github.com/grafana/loki/issues/19483)) ([d5b382b](https://github.com/grafana/loki/commit/d5b382b90738505d8bf7217ff3a1a8c90fc09b7e)).
* **build:** Update loki build image ([#19473](https://github.com/grafana/loki/issues/19473)) ([#19474](https://github.com/grafana/loki/issues/19474)) ([c70476a](https://github.com/grafana/loki/commit/c70476ab0fec3bef906f86ac72c2256bb94f9624)).

### 3.5.6 (2025-10-10)

* **deps:** Remove CVE GHSA-2464-8j7c-4cjm ([#19469](https://github.com/grafana/loki/issues/19469)) ([f2a304a](https://github.com/grafana/loki/commit/f2a304a34d0eb35be6c09e3f525ec6986a003275)).
* **deps:** Update module github.com/twmb/franz-go/plugin/kprom to v1.2.1 (release-3.5.x) ([#18793](https://github.com/grafana/loki/issues/18793)) ([bb7356c](https://github.com/grafana/loki/commit/bb7356c31d8b47c665cdd2188e47df6f406d3f7c)).
* **deps:** Update to Go version to 1.24.8 ([#19454](https://github.com/grafana/loki/issues/19454)) ([#19462](https://github.com/grafana/loki/issues/19462)) ([1b8b5dd](https://github.com/grafana/loki/commit/1b8b5ddfa8b0cc68a15a6e877c9ec13947b7276a)).

### 3.5.5 (2025-09-11)

* **otlp:** Backport fix to correctly calculate entry metadata size ([#19163](https://github.com/grafana/loki/issues/19163)) ([5aa8bd2](https://github.com/grafana/loki/commit/5aa8bd27946a1ce5267790ebe9210f93cd7111b2)).

### 3.5.4 (2025-09-04)

* **deps:** Update to Go version 1.24.6 in release-3.5.x ([#19096](https://github.com/grafana/loki/issues/19096)) ([ce8d705](https://github.com/grafana/loki/commit/ce8d70506c5bd905dd7ec7db8f174b9fcd264f30)).
* **docs:** added instructions for how to upgrade zone-aware ingesters. ([#18663](https://github.com/grafana/loki/issues/18663)) ([e0de7da](https://github.com/grafana/loki/commit/e0de7dac098f66bb71000f591f4fb5bf4f5b81bf)).

### 3.5.3 (2025-07-23)

* **deps:** Update to Go version 1.24.5 ([#18412](https://github.com/grafana/loki/issues/18412)) ([2aa4680](https://github.com/grafana/loki/commit/2aa468065721587d0db829ff6b3cce9b73c10699)).
* **WAL:** Handle WAL corruption properly on startup (backport release-3.5.x) ([#18408](https://github.com/grafana/loki/issues/18408)) ([5b8ee9a](https://github.com/grafana/loki/commit/5b8ee9a582d168cbde2cb5a0bad48283069351d6)).

### 3.5.2 (2025-04-22)

* **ci:** Update release code 3.5 ([#18014](https://github.com/grafana/loki/issues/18014)) ([b1b28b0](https://github.com/grafana/loki/commit/b1b28b0970b437a071050abd8d1391a037cc3ac5)).
* **deps:** Update to Go version 1.24.2 (backport release-3.5.x) ([#17805](https://github.com/grafana/loki/issues/17805)) ([d0fbad0](https://github.com/grafana/loki/commit/d0fbad095d46ec4edceed4dc9a1eff69b0ef1565)).
* **deps:** Update to Go version 1.24.5 (backport release-3.5.x) ([#18412](https://github.com/grafana/loki/issues/18412)) ([4642c63](https://github.com/grafana/loki/commit/4642c639da0875928295b52d1a02d9ab46725db6)).
* **frontend:** Allow resolution of v6 addresses. (backport release-3.5.x) ([#18261](https://github.com/grafana/loki/issues/18261)) ([aed4610](https://github.com/grafana/loki/commit/aed461037b9145ac8cc89813c40cfcad091ed6bf)).
* **jsonparser:** Fix possible JSON log line corruption caused by `json` parser on query path (backport release-3.5.x) ([#18059](https://github.com/grafana/loki/issues/18059)) ([546d456](https://github.com/grafana/loki/commit/546d456b9d1d75154d1c2e42309933e346629c2b)).
* **memberlist:** Allow resolution of advertise address from IPv6  interfaces defined in common `instance_interface_names`.(backport release-3.5.x) ([#18257](https://github.com/grafana/loki/issues/18257)) ([6f878ad](https://github.com/grafana/loki/commit/6f878ad1d02427df81b6684c7cb76711d6fee46a)).
* **WAL:** Handle WAL corruption properly on startup (backport release-3.5.x) ([#18408](https://github.com/grafana/loki/issues/18408)) ([5b8ee9a](https://github.com/grafana/loki/commit/5b8ee9a582d168cbde2cb5a0bad48283069351d6)).

### 3.5.1 (2025-05-19)

* **build:** Bump loki-build-image for docker-driver (backport release-3.5.x) ([#17741](https://github.com/grafana/loki/issues/17741)) ([d4e637c](https://github.com/grafana/loki/commit/d4e637cebb842a933b21f0753c028821b1ad5c26)).

### 3.5 (2025-04-22)

* **approx_topk:** Map approx_topk operation in all cases ([#16131](https://github.com/grafana/loki/issues/16131)) ([6d691ac](https://github.com/grafana/loki/commit/6d691ac38d6a56dd97761a456fb2b53e165f1df3))
* **blooms:** Initialize bloom gateway client only once ([#15994](https://github.com/grafana/loki/issues/15994)) ([e271e22](https://github.com/grafana/loki/commit/e271e22f7c1fae676e2fce6ab002b358694d9450))
* **builds:** Update crc64nvme to fix build issues with arm images ([#16413](https://github.com/grafana/loki/issues/16413)) ([80a616d](https://github.com/grafana/loki/commit/80a616dcfc7fd4e26a6bf5c70407be6d45dc0370))
* **ci:** do not disable CGO in Makefile ([#16271](https://github.com/grafana/loki/issues/16271)) ([810dff9](https://github.com/grafana/loki/commit/810dff9b41702370c19e08f329786cc0e9d36477))
* **ci:** Pass image tag details to logcli docker build ([#16159](https://github.com/grafana/loki/issues/16159)) ([f22527f](https://github.com/grafana/loki/commit/f22527f3d91b58c230b8ea1f831b5221060d1bbe))
* **ci:** Provide correct build info for release builds ([#15939](https://github.com/grafana/loki/issues/15939)) ([625bdab](https://github.com/grafana/loki/commit/625bdabce91f1043065b582052b2c3558c9af0e6))
* **compactor:** remove unused variable in delete requests store code ([#16241](https://github.com/grafana/loki/issues/16241)) ([5e6d0b5](https://github.com/grafana/loki/commit/5e6d0b5f12334bb5449dd7647722f5f0d09c8a9a))
* **dataobj:** Fix shutdown race in dataobj consumer ([#16157](https://github.com/grafana/loki/issues/16157)) ([656b1fa](https://github.com/grafana/loki/commit/656b1faaaff778cfbbde9cd3fc76f4ef03a0bd03))
* **dataobj:** Fixes timerange matching for dataobj files ([#16222](https://github.com/grafana/loki/issues/16222)) ([5aa9e47](https://github.com/grafana/loki/commit/5aa9e47d1ccdaea5e2d484ce3ecb9ca622fe2d5e))
* **dataobj:** Fixes timerange predicate ([#16245](https://github.com/grafana/loki/issues/16245)) ([5195296](https://github.com/grafana/loki/commit/5195296a175774c6ae319c1aab581fdcf1b92dc3))
* **deps:** Update dependency @hookform/resolvers to v4.1.3 (main) ([#16524](https://github.com/grafana/loki/issues/16524)) ([2869e92](https://github.com/grafana/loki/commit/2869e920380e4cd939a2fa7f41742b2b4f3ac53c))
* **deps:** Update dependency @radix-ui/react-checkbox to v1.1.4 (main) ([#16297](https://github.com/grafana/loki/issues/16297)) ([85a6e88](https://github.com/grafana/loki/commit/85a6e88a2e6c189f5a6a36ee7198ccef3e9bb298))
* **deps:** Update dependency @radix-ui/react-collapsible to v1.1.3 (main) ([#16298](https://github.com/grafana/loki/issues/16298)) ([4a1225e](https://github.com/grafana/loki/commit/4a1225ee8f7359b27f7d4e289dfa05e272d4c125))
* **deps:** Update dependency @radix-ui/react-dialog to v1.1.6 (main) ([#16301](https://github.com/grafana/loki/issues/16301)) ([548c8c2](https://github.com/grafana/loki/commit/548c8c20689c5faf17ee9c6f6e216953e8f50af6))
* **deps:** Update dependency @radix-ui/react-dropdown-menu to v2.1.6 (main) ([#16303](https://github.com/grafana/loki/issues/16303)) ([5fedc3c](https://github.com/grafana/loki/commit/5fedc3c711d3bfdef12a779b4893996d18c21a12))
* **deps:** Update dependency @radix-ui/react-hover-card to v1.1.6 (main) ([#16304](https://github.com/grafana/loki/issues/16304)) ([f11fd12](https://github.com/grafana/loki/commit/f11fd128fb8cae2227d6f978ff86d8ad13e0ad41))
* **deps:** Update dependency @radix-ui/react-label to v2.1.2 (main) ([#16305](https://github.com/grafana/loki/issues/16305)) ([13bfb12](https://github.com/grafana/loki/commit/13bfb1202018e3b00ec2121c65bbb79cb52ab38c))
* **deps:** Update dependency @radix-ui/react-popover to v1.1.6 (main) ([#16306](https://github.com/grafana/loki/issues/16306)) ([abb3a94](https://github.com/grafana/loki/commit/abb3a94818460971029e382ffc96ac1e93791a1f))
* **deps:** Update dependency @radix-ui/react-progress to v1.1.2 (main) ([#16307](https://github.com/grafana/loki/issues/16307)) ([18210e9](https://github.com/grafana/loki/commit/18210e93fe3a650b090d75adbd3ff5fba8263900))
* **deps:** Update dependency @radix-ui/react-scroll-area to v1.2.3 (main) ([#16308](https://github.com/grafana/loki/issues/16308)) ([9136ba3](https://github.com/grafana/loki/commit/9136ba32b4ee7f5d2d10c0e3a567f63d4d97c552))
* **deps:** Update dependency @radix-ui/react-select to v2.1.6 (main) ([#16310](https://github.com/grafana/loki/issues/16310)) ([1115a77](https://github.com/grafana/loki/commit/1115a7743cec6897bee5a00c660dfba5cfba4547))
* **deps:** Update dependency @radix-ui/react-separator to v1.1.2 (main) ([#16311](https://github.com/grafana/loki/issues/16311)) ([aee6e88](https://github.com/grafana/loki/commit/aee6e88f31b3318bfbd166aa55ecf34750f149ac))
* **deps:** Update dependency @radix-ui/react-slot to v1.1.2 (main) ([#16313](https://github.com/grafana/loki/issues/16313)) ([9b5f33c](https://github.com/grafana/loki/commit/9b5f33c0ee0790b4c42167240b8abd16456a17e3))
* **deps:** Update dependency @radix-ui/react-switch to v1.1.3 (main) ([#16314](https://github.com/grafana/loki/issues/16314)) ([7fa049e](https://github.com/grafana/loki/commit/7fa049e3310561e5a8352e7deed70249ea16307c))
* **deps:** Update dependency @radix-ui/react-tabs to v1.1.3 (main) ([#16316](https://github.com/grafana/loki/issues/16316)) ([96b4e8f](https://github.com/grafana/loki/commit/96b4e8fd67bf76e5edab785f6637146079017fa5))
* **deps:** Update dependency @radix-ui/react-toast to v1.2.6 (main) ([#16320](https://github.com/grafana/loki/issues/16320)) ([9b47b19](https://github.com/grafana/loki/commit/9b47b19f1822736d93bce760bcffa6812bfe6def))
* **deps:** Update dependency @radix-ui/react-toggle to v1.1.2 (main) ([#16323](https://github.com/grafana/loki/issues/16323)) ([8fd8697](https://github.com/grafana/loki/commit/8fd8697b9320ee63663ec340480010bd75312d63))
* **deps:** Update dependency @radix-ui/react-toggle-group to v1.1.2 (main) ([#16326](https://github.com/grafana/loki/issues/16326)) ([677faad](https://github.com/grafana/loki/commit/677faadcb5a876ebb37a6dce9ede277f2ecf9b3d))
* **deps:** Update dependency @radix-ui/react-tooltip to v1.1.8 (main) ([#16327](https://github.com/grafana/loki/issues/16327)) ([c084144](https://github.com/grafana/loki/commit/c084144d29b97ee027a442c3a8477ed621d3855b))
* **deps:** Update dependency @tanstack/react-query to v5.66.5 (main) ([#16328](https://github.com/grafana/loki/issues/16328)) ([dbf0161](https://github.com/grafana/loki/commit/dbf0161d5ab5d71bfebf2dcf8457dc83aeb6ce15))
* **deps:** Update dependency @tanstack/react-query to v5.66.6 (main) ([#16341](https://github.com/grafana/loki/issues/16341)) ([24137bf](https://github.com/grafana/loki/commit/24137bf8dbf7163530c8fe5d473b8e06e698225b))
* **deps:** Update dependency @tanstack/react-query to v5.66.7 (main) ([#16352](https://github.com/grafana/loki/issues/16352)) ([877243c](https://github.com/grafana/loki/commit/877243cc2dd4690577053c2b2a47e61097e39dcf))
* **deps:** Update dependency @tanstack/react-query to v5.66.9 (main) ([#16421](https://github.com/grafana/loki/issues/16421)) ([d8f0c06](https://github.com/grafana/loki/commit/d8f0c06e5ad22bf6a640698cab07d74acc4cffd2))
* **deps:** Update dependency @tanstack/react-query to v5.67.1 (main) ([#16561](https://github.com/grafana/loki/issues/16561)) ([8641a02](https://github.com/grafana/loki/commit/8641a026d8a52c901a37a6ee68fdcdaf873e6aec))
* **deps:** Update dependency @tanstack/react-query to v5.67.2 (main) ([#16621](https://github.com/grafana/loki/issues/16621)) ([15c498c](https://github.com/grafana/loki/commit/15c498c05406692b1ac0c920bcd92cfe697d1279))
* **deps:** Update dependency @tanstack/react-query to v5.67.3 (main) ([#16685](https://github.com/grafana/loki/issues/16685)) ([2935a74](https://github.com/grafana/loki/commit/2935a749428611ea41cc91ee53476ef0ba7cc8eb))
* **deps:** Update dependency @tanstack/react-query to v5.68.0 (main) ([#16757](https://github.com/grafana/loki/issues/16757)) ([58840c5](https://github.com/grafana/loki/commit/58840c5102c0ab8b13fb668aadacaf527dd026df))
* **deps:** Update dependency @tanstack/react-query to v5.69.0 (main) ([#16808](https://github.com/grafana/loki/issues/16808)) ([4161c76](https://github.com/grafana/loki/commit/4161c764ad1ded10d91947f8fbdc8ff712d8edc9))
* **deps:** Update dependency @tanstack/react-query-devtools to v5.66.5 (main) ([#16331](https://github.com/grafana/loki/issues/16331)) ([dbb1716](https://github.com/grafana/loki/commit/dbb1716ad2a2775a7f18922fa560e47bdc8d1937))
* **deps:** Update dependency @tanstack/react-query-devtools to v5.66.6 (main) ([#16343](https://github.com/grafana/loki/issues/16343)) ([2d707d9](https://github.com/grafana/loki/commit/2d707d97792b25c39e082ede0962a3a11f8c86ea))
* **deps:** Update dependency @tanstack/react-query-devtools to v5.66.7 (main) ([#16353](https://github.com/grafana/loki/issues/16353)) ([412f733](https://github.com/grafana/loki/commit/412f73330114053b57cf269add8092134198445b))
* **deps:** Update dependency @tanstack/react-query-devtools to v5.66.9 (main) ([#16423](https://github.com/grafana/loki/issues/16423)) ([48e00b5](https://github.com/grafana/loki/commit/48e00b5823081406b6ed8982a30b60c6382f63bb))
* **deps:** Update dependency @tanstack/react-query-devtools to v5.67.1 (main) ([#16563](https://github.com/grafana/loki/issues/16563)) ([0f1dce8](https://github.com/grafana/loki/commit/0f1dce84d4457b441c498f675b0f9fc776ac85f1))
* **deps:** Update dependency @tanstack/react-query-devtools to v5.67.2 (main) ([#16622](https://github.com/grafana/loki/issues/16622)) ([7276ae2](https://github.com/grafana/loki/commit/7276ae2a5ec1987d62237c32b4fd6018e0106f8b))
* **deps:** Update dependency @tanstack/react-query-devtools to v5.67.3 (main) ([#16686](https://github.com/grafana/loki/issues/16686)) ([029abea](https://github.com/grafana/loki/commit/029abea14b9e1d4f616838bb553b926dfdcf2dc6))
* **deps:** Update dependency @tanstack/react-query-devtools to v5.68.0 (main) ([#16756](https://github.com/grafana/loki/issues/16756)) ([02a6800](https://github.com/grafana/loki/commit/02a6800523ceaf860847a4eff8f50c21de40c98a))
* **deps:** Update dependency @tanstack/react-query-devtools to v5.69.0 (main) ([#16810](https://github.com/grafana/loki/issues/16810)) ([6109de3](https://github.com/grafana/loki/commit/6109de36161d25f9270b7751fee871a4528d24e1))
* **deps:** Update dependency @types/lodash to v4.17.16 (main) ([#16516](https://github.com/grafana/loki/issues/16516)) ([7c9b44a](https://github.com/grafana/loki/commit/7c9b44a5b59474881546a2ea6c143e31a18ab1f5))
* **deps:** Update dependency @types/react-datepicker to v7 (main) ([#16480](https://github.com/grafana/loki/issues/16480)) ([1796269](https://github.com/grafana/loki/commit/1796269ccb9a1bc0ab0bd3be43bb6a056637ccc8))
* **deps:** Update dependency cmdk to v1.1.1 (main) ([#16766](https://github.com/grafana/loki/issues/16766)) ([06553d8](https://github.com/grafana/loki/commit/06553d8b96b4cef4d6dcf461abc3320b1000e60b))
* **deps:** Update dependency date-fns to v4 (main) ([#16481](https://github.com/grafana/loki/issues/16481)) ([5b7f1f0](https://github.com/grafana/loki/commit/5b7f1f0542bdec24f20f0809fc22b9ae88789319))
* **deps:** Update dependency lucide-react to ^0.475.0 (main) ([#16344](https://github.com/grafana/loki/issues/16344)) ([2f57d60](https://github.com/grafana/loki/commit/2f57d6051a3f6e083143d7ae6fe7300fdc3e4c91))
* **deps:** Update dependency lucide-react to ^0.476.0 (main) ([#16455](https://github.com/grafana/loki/issues/16455)) ([32fd332](https://github.com/grafana/loki/commit/32fd3326b44b14754c78b8e758f9edb8fdfbd654))
* **deps:** Update dependency lucide-react to ^0.477.0 (main) ([#16509](https://github.com/grafana/loki/issues/16509)) ([f84f0c7](https://github.com/grafana/loki/commit/f84f0c7f733a2226148e66e7f603dbd4fc0a7bda))
* **deps:** Update dependency lucide-react to ^0.478.0 (main) ([#16623](https://github.com/grafana/loki/issues/16623)) ([31d8648](https://github.com/grafana/loki/commit/31d8648ab7bb3397ab564c1968b902d0dd8f2cdb))
* **deps:** Update dependency lucide-react to ^0.479.0 (main) ([#16630](https://github.com/grafana/loki/issues/16630)) ([1e1e7a9](https://github.com/grafana/loki/commit/1e1e7a94d5a13b23dd53b45814574c6d4027f68f))
* **deps:** Update dependency lucide-react to ^0.481.0 (main) ([#16765](https://github.com/grafana/loki/issues/16765)) ([a354eee](https://github.com/grafana/loki/commit/a354eee0082223dde7fd34686b8e0ca76023902e))
* **deps:** Update dependency lucide-react to ^0.482.0 (main) ([#16767](https://github.com/grafana/loki/issues/16767)) ([901320c](https://github.com/grafana/loki/commit/901320c0669c9e6e351310b0559b547ad7e12f13))
* **deps:** Update dependency lucide-react to ^0.483.0 (main) ([#16811](https://github.com/grafana/loki/issues/16811)) ([de6f0e9](https://github.com/grafana/loki/commit/de6f0e96af4db35e476e896295d20260bfdb4a2a))
* **deps:** Update dependency next-themes to v0.4.5 (main) ([#16655](https://github.com/grafana/loki/issues/16655)) ([2507888](https://github.com/grafana/loki/commit/2507888c11e886e1c4db48e31d7808b1ff631561))
* **deps:** Update dependency next-themes to v0.4.6 (main) ([#16697](https://github.com/grafana/loki/issues/16697)) ([c515a4e](https://github.com/grafana/loki/commit/c515a4e2b465175dda50c3e334e9a4afa54a4f1e))
* **deps:** Update dependency react to v19 (main) ([#16000](https://github.com/grafana/loki/issues/16000)) ([1b35cb6](https://github.com/grafana/loki/commit/1b35cb64ef1d27609ab3ba53ecdc91a0272f4e7c))
* **deps:** Update dependency react-code-block to v1.1.3 (main) ([#16428](https://github.com/grafana/loki/issues/16428)) ([9691e94](https://github.com/grafana/loki/commit/9691e94f540f75fc78e75d11097c351250e7ccc9))
* **deps:** Update dependency react-datepicker to v8.1.0 (main) ([#16346](https://github.com/grafana/loki/issues/16346)) ([4a674b2](https://github.com/grafana/loki/commit/4a674b2ebd63bdbaa68bebb2058fbbc02fd0cc00))
* **deps:** Update dependency react-datepicker to v8.2.0 (main) ([#16662](https://github.com/grafana/loki/issues/16662)) ([7a70860](https://github.com/grafana/loki/commit/7a7086042b9c9b8f3575a5d74def0f0b15ce8c1f))
* **deps:** Update dependency react-datepicker to v8.2.1 (main) ([#16749](https://github.com/grafana/loki/issues/16749)) ([20eeecb](https://github.com/grafana/loki/commit/20eeecbe9eb2169ba841269731679d58c741761b))
* **deps:** Update dependency react-icons to v5.5.0 (main) ([#16456](https://github.com/grafana/loki/issues/16456)) ([bce5f55](https://github.com/grafana/loki/commit/bce5f55f40087853e62cb22d9280f5e9a143e0ab))
* **deps:** Update dependency react-router-dom to v6.29.0 (main) ([#16347](https://github.com/grafana/loki/issues/16347)) ([14ce844](https://github.com/grafana/loki/commit/14ce844a0e9576b09acec10a9808206ae73599a9))
* **deps:** Update dependency react-router-dom to v7 (main) ([#16515](https://github.com/grafana/loki/issues/16515)) ([976cbbb](https://github.com/grafana/loki/commit/976cbbb1a7ff94af8a01403c51cd549e78309ff5))
* **deps:** Update dependency react-router-dom to v7.1.4 (main) ([#16018](https://github.com/grafana/loki/issues/16018)) ([f163e44](https://github.com/grafana/loki/commit/f163e44034e9a625317a18c09f249927d4b58c9b))
* **deps:** Update dependency react-router-dom to v7.1.5 (main) ([#16046](https://github.com/grafana/loki/issues/16046)) ([45a0c59](https://github.com/grafana/loki/commit/45a0c596d0b30bdb7cfbb84183b4340bd52e8b64))
* **deps:** Update dependency react-router-dom to v7.3.0 (main) ([#16615](https://github.com/grafana/loki/issues/16615)) ([23acfcb](https://github.com/grafana/loki/commit/23acfcb2a2a2460dfd1539da33783fb189209bfa))
* **deps:** Update dependency tailwind-merge to v3 (main) ([#16518](https://github.com/grafana/loki/issues/16518)) ([c8f9474](https://github.com/grafana/loki/commit/c8f9474dfe729e7133b7624f9dfe28e75bed5118))
* **deps:** Update dependency zod to v3.24.2 (main) ([#16333](https://github.com/grafana/loki/issues/16333)) ([ec5bef4](https://github.com/grafana/loki/commit/ec5bef4a80f7b249d580ebf45fb128fa5e16d9b3))
* **deps:** Update github.com/prometheus/prometheus (main) ([#15950](https://github.com/grafana/loki/issues/15950)) ([b37eefe](https://github.com/grafana/loki/commit/b37eefe24f82eb7610b4e1b8e0ac7493f47127d7))
* **deps:** Update module cloud.google.com/go/pubsub to v1.46.0 (main) ([#16019](https://github.com/grafana/loki/issues/16019)) ([998da11](https://github.com/grafana/loki/commit/998da11c200588eb5b96388cf3a1fd3b72db59de))
* **deps:** Update module cloud.google.com/go/pubsub to v1.47.0 (main) ([#16047](https://github.com/grafana/loki/issues/16047)) ([8f9f92f](https://github.com/grafana/loki/commit/8f9f92f838e26b66f01eaacf124ee6679d2ffa96))
* **deps:** Update module cloud.google.com/go/pubsub to v1.48.0 (main) ([#16724](https://github.com/grafana/loki/issues/16724)) ([b07bb72](https://github.com/grafana/loki/commit/b07bb72e4536df0560318727dc2c9e2e9434b1b5))
* **deps:** Update module cloud.google.com/go/storage to v1.51.0 (main) ([#16720](https://github.com/grafana/loki/issues/16720)) ([b4abbf4](https://github.com/grafana/loki/commit/b4abbf46692f1f5175da00136a0f6c997ac364bf))
* **deps:** Update module github.com/aws/aws-sdk-go-v2 to v1.36.1 (main) ([#16024](https://github.com/grafana/loki/issues/16024)) ([542cff4](https://github.com/grafana/loki/commit/542cff47afbda2141c694aafeabc051cba874836))
* **deps:** Update module github.com/aws/aws-sdk-go-v2 to v1.36.2 (main) ([#16430](https://github.com/grafana/loki/issues/16430)) ([ba9afd6](https://github.com/grafana/loki/commit/ba9afd62f9bac5b693901011cb2b78fd19809f88))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/config to v1.29.3 (main) ([#16022](https://github.com/grafana/loki/issues/16022)) ([03f18ac](https://github.com/grafana/loki/commit/03f18acdffa1804d6149a9811d7939383befc484))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/config to v1.29.4 (main) ([#16042](https://github.com/grafana/loki/issues/16042)) ([0bf0253](https://github.com/grafana/loki/commit/0bf0253fdb1a2e7399d15ecff17e7a0359b0df10))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/config to v1.29.5 (main) ([#16093](https://github.com/grafana/loki/issues/16093)) ([c4e916d](https://github.com/grafana/loki/commit/c4e916da86fbd4ab9b1ee72468c74c82dac253e6))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/config to v1.29.6 (main) ([#16123](https://github.com/grafana/loki/issues/16123)) ([d28e627](https://github.com/grafana/loki/commit/d28e6272bf8bbd424ee0e1f36c373de2fad1988e))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/config to v1.29.7 (main) ([#16431](https://github.com/grafana/loki/issues/16431)) ([ca41297](https://github.com/grafana/loki/commit/ca412976fa4107ddcbfca6d4c309b911420a124d))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/config to v1.29.8 (main) ([#16503](https://github.com/grafana/loki/issues/16503)) ([364e80e](https://github.com/grafana/loki/commit/364e80ed66be96999bc34012cfabc6a28bdba52a))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/config to v1.29.9 (main) ([#16542](https://github.com/grafana/loki/issues/16542)) ([2cb3b3a](https://github.com/grafana/loki/commit/2cb3b3a14f65b561510d26d5ecf6b0d246c9d0bc))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.75.0 (main) ([#16003](https://github.com/grafana/loki/issues/16003)) ([e3d1054](https://github.com/grafana/loki/commit/e3d10542fd5741be97ed4261a7ee00e75ab8e4cb))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.75.1 (main) ([#16023](https://github.com/grafana/loki/issues/16023)) ([d2b7d82](https://github.com/grafana/loki/commit/d2b7d82dbbde8227a90f38485178cd6f8f383a09))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.75.2 (main) ([#16043](https://github.com/grafana/loki/issues/16043)) ([01358cf](https://github.com/grafana/loki/commit/01358cf2a33358640e75f6e5c92846d6b73a1a91))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.75.3 (main) ([#16094](https://github.com/grafana/loki/issues/16094)) ([1d6c0eb](https://github.com/grafana/loki/commit/1d6c0eba5160935ef34fe12ab7e2a7047894ef84))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.75.4 (main) ([#16124](https://github.com/grafana/loki/issues/16124)) ([0c44e76](https://github.com/grafana/loki/commit/0c44e761d7242636724cb9dac77dd253eeceb2ac))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.76.0 (main) ([#16140](https://github.com/grafana/loki/issues/16140)) ([f70984c](https://github.com/grafana/loki/commit/f70984ce4430dbf169af94b65b4bec21216ff5d4))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.76.1 (main) ([#16173](https://github.com/grafana/loki/issues/16173)) ([a93a3ab](https://github.com/grafana/loki/commit/a93a3abca1037177458d34ffe518c231e954c84b))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.77.0 (main) ([#16356](https://github.com/grafana/loki/issues/16356)) ([879168b](https://github.com/grafana/loki/commit/879168ba51d43e0e94a358d43404e46fea55db81))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.77.1 (main) ([#16433](https://github.com/grafana/loki/issues/16433)) ([92bdb9d](https://github.com/grafana/loki/commit/92bdb9d742350a4d6a057c11de28ec155d2c7ebf))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.78.0 (main) ([#16513](https://github.com/grafana/loki/issues/16513)) ([b7b5c76](https://github.com/grafana/loki/commit/b7b5c76743dd0141ab8e1c3800843a6936851855))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.78.1 (main) ([#16548](https://github.com/grafana/loki/issues/16548)) ([81aab19](https://github.com/grafana/loki/commit/81aab19acfc30b5c7e4b63d3242abb48bf0b1cd5))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.78.2 (main) ([#16694](https://github.com/grafana/loki/issues/16694)) ([9a99859](https://github.com/grafana/loki/commit/9a99859465f6e4306cbbf14526fdd7a17abd2a4f))
* **deps:** Update module github.com/axiomhq/hyperloglog to v0.2.5 (main) ([#16504](https://github.com/grafana/loki/issues/16504)) ([dcda340](https://github.com/grafana/loki/commit/dcda3401c3381a263182c4d459d28805c516c924))
* **deps:** Update module github.com/azure/go-autorest/autorest to v0.11.30 (main) ([#15987](https://github.com/grafana/loki/issues/15987)) ([543b7f3](https://github.com/grafana/loki/commit/543b7f32dd015e04446156c78b762ef0f85e41de))
* **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.216 (main) ([#16177](https://github.com/grafana/loki/issues/16177)) ([9690cad](https://github.com/grafana/loki/commit/9690cad210db26f9e4df8cc15dfc351520bc061b))
* **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.217 (main) ([#16276](https://github.com/grafana/loki/issues/16276)) ([b262a2b](https://github.com/grafana/loki/commit/b262a2b8f316f79a4e1f9ce7b47f7ae0a1ef9b3c))
* **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.219 (main) ([#16612](https://github.com/grafana/loki/issues/16612)) ([ade0de6](https://github.com/grafana/loki/commit/ade0de60e4a909f01cde7d4e809751682190652b))
* **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.221 (main) ([#16760](https://github.com/grafana/loki/issues/16760)) ([e0e7133](https://github.com/grafana/loki/commit/e0e71333e0933f3408c6239738a2e6a68434fba5))
* **deps:** Update module github.com/bmatcuk/doublestar/v4 to v4.8.1 (main) ([#15973](https://github.com/grafana/loki/issues/15973)) ([a8bd3a8](https://github.com/grafana/loki/commit/a8bd3a88bf6fcf7f2ae0977bf91ae4d7b0e82aa4))
* **deps:** Update module github.com/charmbracelet/bubbletea to v1.3.4 (main) ([#16730](https://github.com/grafana/loki/issues/16730)) ([2f99d84](https://github.com/grafana/loki/commit/2f99d84818eccaba08bfec5ebbe80bc9027f4af8))
* **deps:** Update module github.com/charmbracelet/lipgloss to v1.1.0 (main) ([#16742](https://github.com/grafana/loki/issues/16742)) ([53ae070](https://github.com/grafana/loki/commit/53ae0704787d63feb8a9e9438c9d3ad64d90d59c))
* **deps:** Update module github.com/datadog/sketches-go to v1.4.7 (main) ([#16436](https://github.com/grafana/loki/issues/16436)) ([1907238](https://github.com/grafana/loki/commit/19072387049400790b4c1de51640eeee6ee88184))
* **deps:** Update module github.com/docker/docker to v28 (main) ([#16520](https://github.com/grafana/loki/issues/16520)) ([722ce05](https://github.com/grafana/loki/commit/722ce05f250ad4c49c5e390244e6665236835127))
* **deps:** Update module github.com/fsouza/fake-gcs-server to v1.52.2 (main) ([#16334](https://github.com/grafana/loki/issues/16334)) ([20b9bd2](https://github.com/grafana/loki/commit/20b9bd2b776d294c3b03e3dc8b4ff1c39f76e881))
* **deps:** Update module github.com/golang/snappy to v1 (main) ([#16620](https://github.com/grafana/loki/issues/16620)) ([2306f0b](https://github.com/grafana/loki/commit/2306f0b1a96f8d7647152b4283771142c92e6cc3))
* **deps:** Update module github.com/google/go-cmp to v0.7.0 (main) ([#16457](https://github.com/grafana/loki/issues/16457)) ([4f7fd29](https://github.com/grafana/loki/commit/4f7fd2910aec2be8e58a44665da469d3732f5b23))
* **deps:** Update module github.com/grafana/loki/v3 to v3.4.0 (main) ([#16217](https://github.com/grafana/loki/issues/16217)) ([750fe66](https://github.com/grafana/loki/commit/750fe66695eb974b476eabaccf24e3c5e03c5ad8))
* **deps:** Update module github.com/grafana/loki/v3 to v3.4.1 (main) ([#16265](https://github.com/grafana/loki/issues/16265)) ([cc49ebc](https://github.com/grafana/loki/commit/cc49ebc633a2c9dd8cacdb541a120de644dc939a))
* **deps:** Update module github.com/grafana/loki/v3 to v3.4.2 (main) ([#16335](https://github.com/grafana/loki/issues/16335)) ([977fd54](https://github.com/grafana/loki/commit/977fd54e6e5e1df845d7219974a4412b4674f37c))
* **deps:** Update module github.com/grpc-ecosystem/go-grpc-middleware/v2 to v2.3.0 (main) ([#16357](https://github.com/grafana/loki/issues/16357)) ([5fe1fa7](https://github.com/grafana/loki/commit/5fe1fa7464b062cddd0ee36975d4959f6ba5476b))
* **deps:** Update module github.com/grpc-ecosystem/go-grpc-middleware/v2 to v2.3.1 (main) ([#16613](https://github.com/grafana/loki/issues/16613)) ([9d8a310](https://github.com/grafana/loki/commit/9d8a31069ebc73f096a2cdf668ab4ea97266b672))
* **deps:** Update module github.com/hashicorp/consul/api to v1.31.1 (main) ([#16230](https://github.com/grafana/loki/issues/16230)) ([70d3863](https://github.com/grafana/loki/commit/70d3863fb34f099653fbefbecde12aa2c767e403))
* **deps:** Update module github.com/hashicorp/consul/api to v1.31.2 (main) ([#16361](https://github.com/grafana/loki/issues/16361)) ([2f4f16d](https://github.com/grafana/loki/commit/2f4f16d579a6c409aa6879bd76644b8a17f9381b))
* **deps:** Update module github.com/heroku/x to v0.4.3 (main) ([#16204](https://github.com/grafana/loki/issues/16204)) ([12e436a](https://github.com/grafana/loki/commit/12e436a984f3d017cb4e6a2f141f12db61db5ffd))
* **deps:** Update module github.com/ibm/go-sdk-core/v5 to v5.19.0 (main) ([#16647](https://github.com/grafana/loki/issues/16647)) ([16e87a6](https://github.com/grafana/loki/commit/16e87a6ff2179940f17a6bf545a3e39c76dbbdc8))
* **deps:** Update module github.com/ibm/ibm-cos-sdk-go to v1.12.2 (main) ([#16728](https://github.com/grafana/loki/issues/16728)) ([22cdf61](https://github.com/grafana/loki/commit/22cdf61e9d73996c859adc5fa29e48df90b5cb13))
* **deps:** Update module github.com/ibm/sarama to v1.45.1 (main) ([#16522](https://github.com/grafana/loki/issues/16522)) ([bc9bbb8](https://github.com/grafana/loki/commit/bc9bbb8a01393f2caa54a907bee5760f6b348629))
* **deps:** Update module github.com/influxdata/telegraf to v1.33.2 (main) ([#16179](https://github.com/grafana/loki/issues/16179)) ([022b45c](https://github.com/grafana/loki/commit/022b45c7a9d596c28c9fd713d1950c5576d8c4e2))
* **deps:** Update module github.com/influxdata/telegraf to v1.33.3 (main) ([#16444](https://github.com/grafana/loki/issues/16444)) ([01c9117](https://github.com/grafana/loki/commit/01c9117921562173afb88f8799085c830c1c5e93))
* **deps:** Update module github.com/influxdata/telegraf to v1.34.0 (main) ([#16669](https://github.com/grafana/loki/issues/16669)) ([5d0e6fb](https://github.com/grafana/loki/commit/5d0e6fb410fc49068a04c387755d8115f59e6dd5))
* **deps:** Update module github.com/klauspost/compress to v1.18.0 (main) ([#16458](https://github.com/grafana/loki/issues/16458)) ([6c0f67d](https://github.com/grafana/loki/commit/6c0f67dc171acd8e6b788f1f873348ffb6332435))
* **deps:** Update module github.com/minio/minio-go/v7 to v7.0.85 (main) ([#16152](https://github.com/grafana/loki/issues/16152)) ([be047cb](https://github.com/grafana/loki/commit/be047cb73c291a231a5e50aa4426c06256d2b857))
* **deps:** Update module github.com/minio/minio-go/v7 to v7.0.87 (main) ([#16442](https://github.com/grafana/loki/issues/16442)) ([c92e339](https://github.com/grafana/loki/commit/c92e339bf176a96281c2c99b4cda04bc24017f63))
* **deps:** Update module github.com/minio/minio-go/v7 to v7.0.88 (main) ([#16657](https://github.com/grafana/loki/issues/16657)) ([bf99989](https://github.com/grafana/loki/commit/bf999899f52c549a370f49d7df586412175b0ffd))
* **deps:** Update module github.com/parquet-go/parquet-go to v0.25.0 (main) ([#16459](https://github.com/grafana/loki/issues/16459)) ([4d65435](https://github.com/grafana/loki/commit/4d65435baa5ebf74c9551ca7a3c7724e6dad9bea))
* **deps:** Update module github.com/prometheus/alertmanager to v0.28.1 (main) ([#16634](https://github.com/grafana/loki/issues/16634)) ([57c14ac](https://github.com/grafana/loki/commit/57c14ac5a58379d21c83efd6fbf8106f6e8d9322))
* **deps:** Update module github.com/prometheus/client_golang to v1.21.0 (main) ([#16446](https://github.com/grafana/loki/issues/16446)) ([c128aa8](https://github.com/grafana/loki/commit/c128aa819e4427c5bd7e112cef1a4952c8379ef8))
* **deps:** Update module github.com/prometheus/client_golang to v1.21.1 (main) ([#16552](https://github.com/grafana/loki/issues/16552)) ([0a697f9](https://github.com/grafana/loki/commit/0a697f9f30dcd54588a59c7db7a3ef14ca2c9095))
* **deps:** Update module github.com/prometheus/prometheus to v0.302.1 (main) ([#16475](https://github.com/grafana/loki/issues/16475)) ([9fe8b57](https://github.com/grafana/loki/commit/9fe8b5709913ab763e15239cd77f002e2579ef79))
* **deps:** Update module github.com/prometheus/prometheus to v2 (main) ([#16004](https://github.com/grafana/loki/issues/16004)) ([dc01eb9](https://github.com/grafana/loki/commit/dc01eb97d5b3fd8f5b092e7a851ce8b88144d0df))
* **deps:** Update module github.com/prometheus/sigv4 to v0.1.2 (main) ([#16447](https://github.com/grafana/loki/issues/16447)) ([946497e](https://github.com/grafana/loki/commit/946497ead809676112695b8b5a97e118e3fd5e87))
* **deps:** Update module github.com/redis/go-redis/v9 to v9.7.1 (main) ([#16448](https://github.com/grafana/loki/issues/16448)) ([2cc1873](https://github.com/grafana/loki/commit/2cc187381c74f489b104ec1148261489a310d2e7))
* **deps:** Update module github.com/shirou/gopsutil/v4 to v4.25.1 (main) ([#16049](https://github.com/grafana/loki/issues/16049)) ([319bb0f](https://github.com/grafana/loki/commit/319bb0f132f906204ac2e7d5dd8ff3808a513ec3))
* **deps:** Update module github.com/shirou/gopsutil/v4 to v4.25.2 (main) ([#16517](https://github.com/grafana/loki/issues/16517)) ([fcf1518](https://github.com/grafana/loki/commit/fcf1518513aa0aff9604e44454738d6202c8fbb9))
* **deps:** Update module github.com/spf13/afero to v1.14.0 (main) ([#16769](https://github.com/grafana/loki/issues/16769)) ([342fd40](https://github.com/grafana/loki/commit/342fd407422d273028d20480c54451388eedb9c6))
* **deps:** Update module github.com/tjhop/slog-gokit to v0.1.4 (main) ([#16505](https://github.com/grafana/loki/issues/16505)) ([c409a66](https://github.com/grafana/loki/commit/c409a66f8e332bf0ce2d235428294fa9a2a65523))
* **deps:** Update module go.etcd.io/bbolt to v1.4.0 (main) ([#16107](https://github.com/grafana/loki/issues/16107)) ([78becba](https://github.com/grafana/loki/commit/78becba7f79eb8dadc1b3cc11df58c8bf39e7b5a))
* **deps:** Update module go.opentelemetry.io/collector/pdata to v1.25.0 (main) ([#16076](https://github.com/grafana/loki/issues/16076)) ([2ec948c](https://github.com/grafana/loki/commit/2ec948c1f28c57fb542e96218b41727301445632))
* **deps:** Update module go.opentelemetry.io/collector/pdata to v1.26.0 (main) ([#16359](https://github.com/grafana/loki/issues/16359)) ([043617e](https://github.com/grafana/loki/commit/043617edd739832f74f9b677ef0f9c56b908ef8e))
* **deps:** Update module go.opentelemetry.io/collector/pdata to v1.27.0 (main) ([#16564](https://github.com/grafana/loki/issues/16564)) ([2b99806](https://github.com/grafana/loki/commit/2b998065db152c849e7953833ed6470a52b58ce2))
* **deps:** Update module go.opentelemetry.io/collector/pdata to v1.28.0 (main) ([#16797](https://github.com/grafana/loki/issues/16797)) ([65f90c7](https://github.com/grafana/loki/commit/65f90c75b501a9c8394b9d526db69c90d0aa2636))
* **deps:** Update module go.opentelemetry.io/otel to v1.35.0 (main) ([#16580](https://github.com/grafana/loki/issues/16580)) ([ec2c9dc](https://github.com/grafana/loki/commit/ec2c9dcd1c26c2c41d0fed5e339ea1c0b3ebf22e))
* **deps:** Update module go.opentelemetry.io/otel/trace to v1.35.0 (main) ([#16581](https://github.com/grafana/loki/issues/16581)) ([378af0c](https://github.com/grafana/loki/commit/378af0c40ca40bb47effcd0466affbe3b75d7b84))
* **deps:** Update module golang.org/x/crypto to v0.33.0 (main) ([#16149](https://github.com/grafana/loki/issues/16149)) ([0c20717](https://github.com/grafana/loki/commit/0c20717adaa810d8f649bda8a04e890f27a43194))
* **deps:** Update module golang.org/x/crypto to v0.35.0 (main) ([#16452](https://github.com/grafana/loki/issues/16452)) ([41c1cfc](https://github.com/grafana/loki/commit/41c1cfc2309a987909f501963c7ace4c600d9fcf))
* **deps:** Update module golang.org/x/crypto to v0.36.0 (main) ([#16575](https://github.com/grafana/loki/issues/16575)) ([e9f2ee1](https://github.com/grafana/loki/commit/e9f2ee14e79be0dec496e958f6c8c03bacf80f40))
* **deps:** Update module golang.org/x/net to v0.35.0 (main) ([#16167](https://github.com/grafana/loki/issues/16167)) ([f084f02](https://github.com/grafana/loki/commit/f084f02bbb98c1da038c0715b47607677a29cd63))
* **deps:** Update module golang.org/x/net to v0.36.0 (main) ([#16567](https://github.com/grafana/loki/issues/16567)) ([c84d7e9](https://github.com/grafana/loki/commit/c84d7e90c6ebc41690f8836b1ea2af47a076e2b9))
* **deps:** Update module golang.org/x/net to v0.37.0 (main) ([#16582](https://github.com/grafana/loki/issues/16582)) ([133c884](https://github.com/grafana/loki/commit/133c884d9e9e2749853e6784ae0768ad9a609d13))
* **deps:** Update module golang.org/x/oauth2 to v0.26.0 (main) ([#16085](https://github.com/grafana/loki/issues/16085)) ([9ddc756](https://github.com/grafana/loki/commit/9ddc756c1d18fff4c9f91b560a688e15292f9be4))
* **deps:** Update module golang.org/x/oauth2 to v0.27.0 (main) ([#16453](https://github.com/grafana/loki/issues/16453)) ([b731680](https://github.com/grafana/loki/commit/b731680bd68e24ceacc74fa95f709ea374dcae85))
* **deps:** Update module golang.org/x/oauth2 to v0.28.0 (main) ([#16568](https://github.com/grafana/loki/issues/16568)) ([f86c43c](https://github.com/grafana/loki/commit/f86c43cd7022e5a14759e2412bf6c817dc391f11))
* **deps:** Update module golang.org/x/sync to v0.11.0 (main) ([#16086](https://github.com/grafana/loki/issues/16086)) ([536dbc6](https://github.com/grafana/loki/commit/536dbc6e185c414acb90b0efec524683e21a098c))
* **deps:** Update module golang.org/x/sync to v0.12.0 (main) ([#16569](https://github.com/grafana/loki/issues/16569)) ([9052cc2](https://github.com/grafana/loki/commit/9052cc272593e3de9db95d40c8c6bbb164bc517e))
* **deps:** Update module golang.org/x/sys to v0.30.0 (main) ([#16087](https://github.com/grafana/loki/issues/16087)) ([6d49d91](https://github.com/grafana/loki/commit/6d49d911dc4bd802f350a99f5598c903cf236a90))
* **deps:** Update module golang.org/x/sys to v0.31.0 (main) ([#16570](https://github.com/grafana/loki/issues/16570)) ([173e75c](https://github.com/grafana/loki/commit/173e75c8ea962d79b954356c283ba5785e8d4f8b))
* **deps:** Update module golang.org/x/text to v0.22.0 (main) ([#16090](https://github.com/grafana/loki/issues/16090)) ([8c2406e](https://github.com/grafana/loki/commit/8c2406e579aaa0e45f13f48de91f25586e477bc0))
* **deps:** Update module golang.org/x/text to v0.23.0 (main) - abandoned ([#16576](https://github.com/grafana/loki/issues/16576)) ([97981a5](https://github.com/grafana/loki/commit/97981a517f03cf1b906994b66a93866373d672ea))
* **deps:** Update module golang.org/x/time to v0.10.0 (main) ([#16089](https://github.com/grafana/loki/issues/16089)) ([fe17cc0](https://github.com/grafana/loki/commit/fe17cc0c370bc06c0dffb913936edb0d91e90fd7))
* **deps:** Update module golang.org/x/time to v0.11.0 (main) ([#16571](https://github.com/grafana/loki/issues/16571)) ([a38bba9](https://github.com/grafana/loki/commit/a38bba9d90bd4357544cc147eaca62bfb3357395))
* **deps:** Update module google.golang.org/api to v0.219.0 (main) ([#15991](https://github.com/grafana/loki/issues/15991)) ([d2e6d99](https://github.com/grafana/loki/commit/d2e6d991b1ee290348549c4b5406f93952729f7c))
* **deps:** Update module google.golang.org/api to v0.220.0 (main) ([#16115](https://github.com/grafana/loki/issues/16115)) ([0528e40](https://github.com/grafana/loki/commit/0528e40189f8a68c218d2a4c8336f826ecbd4d76))
* **deps:** Update module google.golang.org/api to v0.221.0 (main) ([#16234](https://github.com/grafana/loki/issues/16234)) ([a66faf5](https://github.com/grafana/loki/commit/a66faf58d453379956332d45922095bece640b01))
* **deps:** Update module google.golang.org/api to v0.223.0 (main) ([#16460](https://github.com/grafana/loki/issues/16460)) ([861affe](https://github.com/grafana/loki/commit/861affecdbee4f57348a53b02dfa3f14b1f00c80))
* **deps:** Update module google.golang.org/api to v0.224.0 (main) ([#16614](https://github.com/grafana/loki/issues/16614)) ([7dfe4a5](https://github.com/grafana/loki/commit/7dfe4a5849a21f6d12e14d0d76a38faaff5ed42b))
* **deps:** Update module google.golang.org/api to v0.225.0 (main) ([#16690](https://github.com/grafana/loki/issues/16690)) ([b243d42](https://github.com/grafana/loki/commit/b243d42778524dd6521b4b317d8d79b6a69873e9))
* **deps:** Update module google.golang.org/api to v0.226.0 (main) ([#16752](https://github.com/grafana/loki/issues/16752)) ([745bf67](https://github.com/grafana/loki/commit/745bf67568211dc84baa8f136a7f09aff8706989))
* **deps:** Update module google.golang.org/grpc to v1.71.0 (main) ([#16578](https://github.com/grafana/loki/issues/16578)) ([f989bd0](https://github.com/grafana/loki/commit/f989bd01d927223de2d4b08f6074eeb8a2dcc163))
* **deps:** Update module google.golang.org/protobuf to v1.36.5 (main) ([#16130](https://github.com/grafana/loki/issues/16130)) ([b1c1c5a](https://github.com/grafana/loki/commit/b1c1c5ab0b52dded16f901ff6a4791aa4f31ea2f))
* **deps:** Update module k8s.io/apimachinery to v0.32.2 (main) ([#16267](https://github.com/grafana/loki/issues/16267)) ([abcd730](https://github.com/grafana/loki/commit/abcd7301a683e5c0b19c90749d624ca9e8b868a3))
* **deps:** Update module k8s.io/apimachinery to v0.32.3 (main) ([#16704](https://github.com/grafana/loki/issues/16704)) ([0be4897](https://github.com/grafana/loki/commit/0be4897a4126b30db38e2249bd948f97291cf9e1))
* **distributor:** Determine when all logs have been filtered ([#16073](https://github.com/grafana/loki/issues/16073)) ([f2bff20](https://github.com/grafana/loki/commit/f2bff201d7b3608133a6e0ef3bfa4a37d5e33c67))
* **distributor:** Fix blocked ingestion returned error when 260 ([#16387](https://github.com/grafana/loki/issues/16387)) ([3d6163a](https://github.com/grafana/loki/commit/3d6163abc7c099d1cd83753adade5eca95a84137))
* **distributor:** Increment UsageTracker.DiscardedBytesAdd() for ingestion policy discards ([#16528](https://github.com/grafana/loki/issues/16528)) ([a4c796d](https://github.com/grafana/loki/commit/a4c796d6ce8da8e9b37e0496df967f9ff0b621c8))
* **distributor:** Remove colons from level detection ([#16764](https://github.com/grafana/loki/issues/16764)) ([865c43a](https://github.com/grafana/loki/commit/865c43aad5d2ff99f14bc7c723a6c4a9a7d0e2c8))
* **distributor:** Zeroed stream hash when checking limits ([#16729](https://github.com/grafana/loki/issues/16729)) ([ab0cbce](https://github.com/grafana/loki/commit/ab0cbcedbca6c1f89423e587416c0bf5f319f4c9))
* **docs:** Add a note on docker configuration.md doc to explain accepted values for boolean type ([#16299](https://github.com/grafana/loki/issues/16299)) ([2071a50](https://github.com/grafana/loki/commit/2071a5098b4e5214fb8f8c646ae0081b39a563af))
* **engine:** Fix `Walk()` function implementation on various `Expr` implementations ([#16033](https://github.com/grafana/loki/issues/16033)) ([3888866](https://github.com/grafana/loki/commit/388886661699dfbe4fe9826dfecd33107fd3383a))
* **frontend:** Fix a panic in ServeHTTP where stream was nil ([#16818](https://github.com/grafana/loki/issues/16818)) ([ba7550a](https://github.com/grafana/loki/commit/ba7550a0345e9a90052efdd33dad5669386a0c97))
* **frontend:** Fix a data race I added changing this to a map ([#16814](https://github.com/grafana/loki/issues/16814)) ([f4893ee](https://github.com/grafana/loki/commit/f4893ee9ca81a23e80a6dbfb7e3c7299eb7914b9))
* **helm:** Change object_store.prefix to object_store.storage_prefix in helm ([#16266](https://github.com/grafana/loki/issues/16266)) ([b5ad4f0](https://github.com/grafana/loki/commit/b5ad4f09ee7ca93ab42f7d387eeb4db23767781f))
* **helm:** Disable service monitor for nginx service ([#12746](https://github.com/grafana/loki/issues/12746)) ([0c38b94](https://github.com/grafana/loki/commit/0c38b947648510894d0d8f50d5cccc9a57576298))
* **ksonnet:** Revert "feat: Assert for bad `max_query_lookback` configuration ([#16362](https://github.com/grafana/loki/issues/16362))" ([#16441](https://github.com/grafana/loki/issues/16441)) ([42ddb68](https://github.com/grafana/loki/commit/42ddb68aa73226580a30f4d32fec91ca3f6e57d3))
* **ksonnet:** Scheduling constraints ([#16045](https://github.com/grafana/loki/issues/16045)) ([b45a31e](https://github.com/grafana/loki/commit/b45a31e84f83a9921fb34fc4faf15abb4d144e54))
* **ksonnet:** Clean-up deprecated configurations ([#16007](https://github.com/grafana/loki/issues/16007)) ([76183fa](https://github.com/grafana/loki/commit/76183fa9ed245ca77926ee995c48ed315c715ed7))
* **logql:** Adjust with offset in last, first and quantile over time queries. ([#15915](https://github.com/grafana/loki/issues/15915)) ([83339cb](https://github.com/grafana/loki/commit/83339cbde08139eba6f08742f3c7749f901b79ce))
* **memchunk:** Skip a test that currently is not used in our main path ([#16474](https://github.com/grafana/loki/issues/16474)) ([196cbaa](https://github.com/grafana/loki/commit/196cbaabe87d64ee90aaafe0207446bee08ef863))
* **nix:** Build with go 1.24 ([#16579](https://github.com/grafana/loki/issues/16579)) ([f1ac3c4](https://github.com/grafana/loki/commit/f1ac3c4979fbe86ff2e00eac706b9b77986468ca))
* **operator:** Fix minimum available ingesters for 1x.pico size ([#16035](https://github.com/grafana/loki/issues/16035)) ([40cf074](https://github.com/grafana/loki/commit/40cf074fba0ed0016a8ca64bed554f3d628e7ec6))
* **operator:** Select non-zero delete worker count for all sizes ([#16492](https://github.com/grafana/loki/issues/16492)) ([1e5579a](https://github.com/grafana/loki/commit/1e5579abef02ed03f9dc87cf7d09f52f53768152))
* **operator:** Update maximum OpenShift version ([#16443](https://github.com/grafana/loki/issues/16443)) ([ddf3cfb](https://github.com/grafana/loki/commit/ddf3cfbba7a6529a6902036c486b523b588818e3))
* **operator:** Update OTLP user guide to reflect change in LokiStack ([#16057](https://github.com/grafana/loki/issues/16057)) ([14e2c87](https://github.com/grafana/loki/commit/14e2c875d2bc5d6678f964f1477cfafe6f37e496))
* **operator:** Update skipRange in OpenShift variant ([#15984](https://github.com/grafana/loki/issues/15984)) ([dfbe00c](https://github.com/grafana/loki/commit/dfbe00c88a2f17da11b726a3461c11324c21fcca))
* **pattern:** Correctly initialize overrides for pattern tee. ([#16112](https://github.com/grafana/loki/issues/16112)) ([8f38cf7](https://github.com/grafana/loki/commit/8f38cf77682c54fe917e63a0a6ddbea1e75e1bb6))
* **policies:** Make policy test non-flaky ([#16158](https://github.com/grafana/loki/issues/16158)) ([41d9c8b](https://github.com/grafana/loki/commit/41d9c8b5ecd0ea31cd12f8fce052b47abd341ff8))
* **promtail:** Remove flaky TestFileTarget_StopsTailersCleanly ([#16473](https://github.com/grafana/loki/issues/16473)) ([2ab63d2](https://github.com/grafana/loki/commit/2ab63d2e4780578535ae3fec51d95fb9f5a83361))
* **promtail:** Fix Windows forward event crash ([#16155](https://github.com/grafana/loki/issues/16155)) ([82cfaea](https://github.com/grafana/loki/commit/82cfaea5957d859f730656b7f5955842237cf6ed))
* **querier:** Fix label values query when server.http_path_prefix is set ([#15978](https://github.com/grafana/loki/issues/15978)) ([65f5990](https://github.com/grafana/loki/commit/65f59903a29a3cfc8a9e5915af45e4228667d40d))
* **querier:** Only allow aggregated metric queries from logs drilldown app ([#16670](https://github.com/grafana/loki/issues/16670)) ([6c6410c](https://github.com/grafana/loki/commit/6c6410cecd3b09b4550685614d4756b427e00dbd))
* **queryrange:** Properly handle context cancellation in Downstreamer ([#16471](https://github.com/grafana/loki/issues/16471)) ([5335a21](https://github.com/grafana/loki/commit/5335a215ef23a491f0bf7950ae3c71cdcac74e14))
* Reverts the tailwindcss upgrade that was breaking builds ([#16058](https://github.com/grafana/loki/issues/16058)) ([bb76772](https://github.com/grafana/loki/commit/bb76772f413125037fe76c98a10cd7f205c087ad))
* **ruler:** Ensure test service shutdown is properly awaited ([#16468](https://github.com/grafana/loki/issues/16468)) ([8976709](https://github.com/grafana/loki/commit/8976709f24dc9baa603d030fa23484382f7ba767))
* **structured metadata:** Export ExcludedMetadataLabels so it can be extended in GEL ([#16083](https://github.com/grafana/loki/issues/16083)) ([9d84a68](https://github.com/grafana/loki/commit/9d84a6868b4327a243c6f26e0c5c9954402ada23))
* **tsdb:** Export `exportTSInSecs` field in TSDB identifier to make sure correct gap calculation ([#13116](https://github.com/grafana/loki/issues/13116)) ([99d9f1c](https://github.com/grafana/loki/commit/99d9f1cffd8f3ec68d4e02ec1afe7bdb3cd2421e))

# v3.6

Grafana Labs and the Loki team are excited to announce the release of Loki 3.6. Here's a summary of new enhancements and important fixes.

For a full list of all changes and fixes, refer to the [CHANGELOG](https://github.com/grafana/loki/blob/release-3.6.x/CHANGELOG.md)

{{< admonition type="note" >}}
See the [deprecations](https://grafana.com/docs/loki/latest/release-notes/v3-6/#deprecations) section for important information about the Loki Helm charts.
{{< /admonition >}}

## Features and enhancements

Key features in Loki 3.6.0 include the following:

* **Horizontally scalable compactor:** The Compactor in Loki is a singleton service that compacts the index and applies retention, including processing delete requests. Because it can only be scaled vertically, it limits how many resources you can add to speed things up when the workload increases in bigger cells. This release adds a horizontally scalable compactor, to speed up processing large delete requests or catching up if there has been a lag in processing for some reason. Note that the horizontally scalable compactor delegates deletion work to the workers. Loki continues to perform compaction and apply retention in the main Compactor. For more information, refer to the [documentation](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/compactor-horizontal-scaling/).

* **OTel:** Loki has been refactored to use the OpenTelemetry tracing library instead of the OpenTracing library. ([#17859](https://github.com/grafana/loki/pull/17859 )). Note that Loki can still be configured using `JAEGER_ env` vars and still exports traces in `JAEGER_` format.

* **meta-monitoring:** Moves the meta-monitoring responsibilities from the [Grafana meta-monitoring Helm chart](https://github.com/grafana/meta-monitoring-chart) to the widely supported [Grafana Kubernetes Monitoring Helm chart](https://github.com/grafana/k8s-monitoring-helm). This was implemented in two PRs: Meta Monitoring Loki v2 (Part 1) - Telemetry Collection ([#16665](https://github.com/grafana/loki/issues/16665)) ([2d0bbfd](https://github.com/grafana/loki/commit/2d0bbfd70b612e2b62615ba19a0bd8e756aaca88)) and Meta Monitoring Loki v2 (Part 2) - Loki Mixin's ([#16962](https://github.com/grafana/loki/issues/16962)) ([93360f6](https://github.com/grafana/loki/commit/93360f6da138b4a63282c87e45567b36117f5f13)).

* **Helm charts:** The Grafana Developer Advocates launched a pilot Community Helm Maintainers group at the end of June made up of Grafana Champions who expressed interest in helping maintain the Loki Helm chart. We have resumed regular releases of the Loki Helm chart and reduced the backlog of open Loki Helm chart pull requests from around 150 to less than 40. For information about recent changes, refer to the Helm chart [CHANGELOG](https://github.com/grafana/loki/blob/main/production/helm/loki/CHANGELOG.md).

* **UI:** We have moved the Loki UI to a Grafana Plugin. The static Operational UI is not going away, and all the APIs that serve it will remain in Loki. The JavaScript that serves the UI, however, has been moved to the [Loki Operational UI](https://github.com/grafana/loki-operational-ui) Grafana plugin ([#19390](https://github.com/grafana/loki/issues/19390)).

* **Docs:** Published a third Cloud Deployment Guide, this one for [Google Cloud Platform](https://grafana.com/docs/loki/latest/setup/install/helm/deployment-guides/gcp/) ([#17503](https://github.com/grafana/loki/pull/17503)). Added links to three log related [Learning paths](https://grafana.com/docs/learning-paths/) to the Loki docs ([#17956](https://github.com/grafana/loki/pull/17956)). Revised the LogQL query page ([#16286](https://github.com/grafana/loki/pull/16286)) and added a LogQL video ([#18111](https://github.com/grafana/loki/pull/18111)). Added a caution to the Labels topic and added a [new topic](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/modify-default-labels/) with instructions how to modify the default OpenTelemetry resource attributes that Loki converts to labels ([#17958](https://github.com/grafana/loki/pull/17958)). Updated the [caching topic](https://grafana.com/docs/loki/latest/operations/caching/) ([#17420](https://github.com/grafana/loki/pull/17420)).

{{< admonition type="note" >}}
Note that when appropriate, documentation updates have been backported to previous releases.
{{< /admonition >}}

Other improvements include the following:

* **analytics:** Change how analytics proxy works ([#19098](https://github.com/grafana/loki/pull/19098))
* **API:** Add endpoint returning tenant configured limits ([#17101](https://github.com/grafana/loki/issues/17101)) ([6818c90](https://github.com/grafana/loki/commit/6818c90011627b83c50eb9f23c53e1769abb46a7))
* **API:** Loki Improvement Document (LID) for query splitting ([#16958](https://github.com/grafana/loki/issues/16958)) ([66f0f0b](https://github.com/grafana/loki/commit/66f0f0bf596f0004cfcffa01684d82bb1ec51d9c))
* **compactor HS:** Add a dashboard for monitoring deletion with horizontally scalable compactor ([#18588](https://github.com/grafana/loki/issues/18588)) ([10db8ab](https://github.com/grafana/loki/commit/10db8abb58742a252e2a0b47355ca715031b0e9f))
* **compactor HS:** Add a job runner for processing of deletion jobs ([#18058](https://github.com/grafana/loki/issues/18058))
* **compactor HS:** Add job queue and deletion job builder ([#17843](https://github.com/grafana/loki/issues/17843))
* **compactor HS:** Add jsonnet for deploying horizontally scalable compactor ([#18550](https://github.com/grafana/loki/issues/18550)) ([5e28950](https://github.com/grafana/loki/commit/5e28950556c6ba0f97db8b5450bec89c82fbd682))
* **compactor HS:** Add support for applying storage updates after finishing processing of deletion manifest ([#18294](https://github.com/grafana/loki/issues/18294)) ([ba3c670](https://github.com/grafana/loki/commit/ba3c67076a87d7916faef49e10573511988387b7))
* **compactor HS:** Add support for worker for processing of jobs from the compactor's job queue ([#18165](https://github.com/grafana/loki/issues/18165)) ([d05c4bc](https://github.com/grafana/loki/commit/d05c4bc500be779f35b6e25823da940a6e236229))
* **compactor HS:** Fixes and changes for getting horizontally scalable compactor to work well ([#18526](https://github.com/grafana/loki/issues/18526)) ([000f90d](https://github.com/grafana/loki/commit/000f90d74b28b81257c51db6c0ffefa2f87fdbca))
* **compactor HS:** Refactor interfaces and build a link between delete requests manager and compactor for HS of compactor ([#18326](https://github.com/grafana/loki/issues/18326)) ([3646607](https://github.com/grafana/loki/commit/364660748d0d391392dd43f699f0b8962ad00541))
* **compactor HS:** Store index updates per processed segment in the manifest to the object storage ([#18212](https://github.com/grafana/loki/issues/18212)) ([8007e22](https://github.com/grafana/loki/commit/8007e22db5ae293468403aa2af06b88efeb2f760))
* **compactor HS:** Tune compactor worker config and add experimental feature warning to the docs ([#18539](https://github.com/grafana/loki/issues/18539)) ([5bf3f74](https://github.com/grafana/loki/commit/5bf3f74ab20af9f392c3e3f311637426c59d7d03))
* **compactor HS:** Use streaming RPCs for interacting with job queue over grpc ([#18125](https://github.com/grafana/loki/issues/18125)) ([385e317](https://github.com/grafana/loki/commit/385e3175cf46deeb066de6e5e04cb4698adb929d))
* **compactor HS:** Wire things up and add some metrics ([#18421](https://github.com/grafana/loki/issues/18421)) ([bd29320](https://github.com/grafana/loki/commit/bd293206cd78df5ded520187633738f0744b7c9a))
* **compactor HS:** Add a metric to track the number of jobs left to conclude ongoing unit of work ([#19506](https://github.com/grafana/loki/issues/19506))
* **distributor:** Add MaxRecvMsgSize config for uncompressed message size limits ([#16915](https://github.com/grafana/loki/issues/16915)) ([1d99f4d](https://github.com/grafana/loki/commit/1d99f4d86d25a361fc85162fadb21a78308d65d4))
* **distributor:** Log ingestion policy when log_push_request is set ([#18712](https://github.com/grafana/loki/issues/18712)) ([0ec787f](https://github.com/grafana/loki/commit/0ec787f22a04620727ca4dc3fac762da2498c3e0))
* **distributor:** Mark truncated log lines with identifier ([#18262](https://github.com/grafana/loki/issues/18262)) ([0ee8e76](https://github.com/grafana/loki/commit/0ee8e76ba28065e2eb03c6908146522700ed6aa7))
* **distributor:** Stop pushing to the metadata topic from distributors ([#17525](https://github.com/grafana/loki/issues/17525)) ([e0f015c](https://github.com/grafana/loki/commit/e0f015c623819e2df4357bc578a38bba5ca21e58))
* **distributor:** Remove tenant topic tee ([#19082](https://github.com/grafana/loki/pull/19082))
* **fluentbit:** Update fluent/fluent-bit to v4 which adds support for structured metadata to the out_loki plugin ([#17200](https://github.com/grafana/loki/issues/17200)) ([f108e1c](https://github.com/grafana/loki/commit/f108e1cd71a969050d870582e9249ed72d3b456c))
* **fluentd:** Add 'compress gzip' setting to fluentd plugin ([#18995](https://github.com/grafana/loki/issues/18995))
* **helm:** Add `block_builder` config to helm chart ([#17451](https://github.com/grafana/loki/issues/17451)) ([a9808a1](https://github.com/grafana/loki/commit/a9808a1f7db9e04ffbbabf30fca20f3287641886))
* **helm:** Add `startupProbe` to memcached([#19166](https://github.com/grafana/loki/issues/19166))
* **helm:** Add `startupProbe` to memcached exporter([#19165](https://github.com/grafana/loki/issues/19165))
* **helm:** Add configurable livenessProbe to single binary StatefulSet([#19072](https://github.com/grafana/loki/issues/19072))
* **helm:** Add dnsconfig to all missing resources([#18903](https://github.com/grafana/loki/issues/18903))
* **helm:** Add FOLDER_ANNOTATION logic for sidecar container in loki helm chart, making multi-tenant alerting easier ([#13289](https://github.com/grafana/loki/issues/13289)) ([833fcbc](https://github.com/grafana/loki/commit/833fcbce39fe956a7e006ea6618fbbd8d7ccfaa8))
* **helm:** Add health probes to admin-api ([#18546](https://github.com/grafana/loki/issues/18546)) ([358228a](https://github.com/grafana/loki/commit/358228a22295189d86c16d749a966eb12c7189e3))
* **helm:** Add health probes to bloom-builder ([#18549](https://github.com/grafana/loki/issues/18549))
* **helm:** Add health probes to bloom-gateway([#18551](https://github.com/grafana/loki/issues/18551))
* **helm:** Add health probes to bloom-planner([#18552](https://github.com/grafana/loki/issues/18552))
* **helm:** Add health probes to gateway component([#18545](https://github.com/grafana/loki/issues/18545))
* **helm:** Add health probes to loki in backend([#18548](https://github.com/grafana/loki/issues/18548))
* **helm:** Add location snippet to nginx config ([#18105](https://github.com/grafana/loki/issues/18105)) ([5737751](https://github.com/grafana/loki/commit/573775194c400ea58b37583546b45afc1846f038))
* **helm:** Add missing revisionHistoryLimit ([#18638](https://github.com/grafana/loki/issues/18638)) ([d0e0d1a](https://github.com/grafana/loki/commit/d0e0d1a4e47145b5e178f84fd554c4d26b9bb47b))
* **helm:** Add more specificity to affinity and topology spread constraints([#19061](https://github.com/grafana/loki/issues/19061))
* **helm:** Add namespaceOverride([#18707](https://github.com/grafana/loki/issues/18707))
* **helm:** Add non-default-values.yaml to increase test coverage ([#18360](https://github.com/grafana/loki/issues/18360)) ([b0e04f4](https://github.com/grafana/loki/commit/b0e04f41b40d1fbf7b09f858fec772eb0774e23f))
* **helm:** Add option to use deployment type instead of daemonset in canary ([#17364](https://github.com/grafana/loki/issues/17364)) ([a6563cf](https://github.com/grafana/loki/commit/a6563cfdaa21620d9cd0a337d7cddf22f2ff006c))
* **helm:** Add PDB for patternIngester ([#17058](https://github.com/grafana/loki/issues/17058)) ([7fad38d](https://github.com/grafana/loki/commit/7fad38d38c92b3d82385c771bdd4136ecf476375))
* **helm:** Add readiness and liveness support for all memcached components ([#18525](https://github.com/grafana/loki/issues/18525)) ([9b54d9f](https://github.com/grafana/loki/commit/9b54d9f1a8667ee09f43d86065a6b327dfb3f223))
* **helm:** Add readiness probe for memcached ([#15609](https://github.com/grafana/loki/issues/15609)) ([6ed1461](https://github.com/grafana/loki/commit/6ed1461d14ae2d196566618a02f4310936da6db0))
* **helm:** Add replicas to loki-canary deployment ([#19095](https://github.com/grafana/loki/issues/19095))
* **helm:** Add startupProbe to loki-sc-rules ([#18547](https://github.com/grafana/loki/issues/18547)) ([121e84e](https://github.com/grafana/loki/commit/121e84ec1ea8ea90783f832735a0d7752a63e55e))
* **helm:** Add support for custom alert rule annotations ([#19193](https://github.com/grafana/loki/issues/19193))
* **helm:** Add tpl support for pattern_ingester and ingester_client ([#16759](https://github.com/grafana/loki/issues/16759)) ([901b2b0](https://github.com/grafana/loki/commit/901b2b01f651fbbb1a1fb1765a2fc8cee774b307))
* **helm:** Added trafficDistribution for service-distributor ([#17742](https://github.com/grafana/loki/issues/17742)) ([78f154f](https://github.com/grafana/loki/commit/78f154f77d656d356b1b9e78e6b0c17605290934))
* **helm:** Adds a flag for tokengen rbac components so that they can be disabled optionally ([#15882](https://github.com/grafana/loki/issues/15882)) ([10536b2](https://github.com/grafana/loki/commit/10536b24c54190d5363b34eae7b5f120c3bd9587))
* **helm:** Allow configuring service types ([#18708](https://github.com/grafana/loki/issues/18708))
* **helm:** Allow enabling user namespaces ([#18661](https://github.com/grafana/loki/issues/18661))
* **helm:** Allow external memcached setup ([#17432](https://github.com/grafana/loki/issues/17432)) ([013aa17](https://github.com/grafana/loki/commit/013aa17716dd96b7bd2004d8388f1f03f5b6efee))
* **helm:** Allow extraObjects to be defined as either a list or dictionary.([#13252](https://github.com/grafana/loki/issues/13252))
* **helm:** Allow passing tenant password hash instead of password ([#17049](https://github.com/grafana/loki/issues/17049)) ([20b97f7](https://github.com/grafana/loki/commit/20b97f77b0ae381f0c6dc16368f34e27f0e81141))
* **helm:** Allow setting labels for ingesters ([#18536](https://github.com/grafana/loki/issues/18536)) ([ab426be](https://github.com/grafana/loki/commit/ab426be4592da204c0e0aa668e0b5670aac99eac))
* **helm:** Enable rules sidecar for the ruler pods ([#17231](https://github.com/grafana/loki/issues/17231)) ([2c5ad02](https://github.com/grafana/loki/commit/2c5ad028a1f2e36590560a8091b162644bd06a36))
* **helm:** Expose all storage configuration ([#18509](https://github.com/grafana/loki/issues/18509)) ([db5e021](https://github.com/grafana/loki/commit/db5e02158ed04a2e45671698b129b51555acb548))
* **helm:** Expose labels on volumeClaimTemplates ([#13317](https://github.com/grafana/loki/issues/13317)) ([cdefbbf](https://github.com/grafana/loki/commit/cdefbbf3ac15dfede5e8e301f55041efd6fd920f))
* **helm:** Expose Loki UI in pure ingress ([#18400](https://github.com/grafana/loki/issues/18400)) ([21f3d09](https://github.com/grafana/loki/commit/21f3d09caab2cc6d64fbb07ceeaea8efbce25814))
* **helm:** Helm issue templates ([#18693](https://github.com/grafana/loki/issues/18693)) ([e0ec815](https://github.com/grafana/loki/commit/e0ec8153a3002a841899d47355fc562f9901d915))
* **helm:** Implementing support of l2 chunksCache memcache ([#17556](https://github.com/grafana/loki/issues/17556)) ([039e7c1](https://github.com/grafana/loki/commit/039e7c112f7c7f70060d934a7eb99e3609ef7e0f))
* **helm:** Integrate the enterprise-provisioner into Loki helm chart ([#17616](https://github.com/grafana/loki/issues/17616))
* **helm:** Make access modes for persistence on all PVCs and StatefulSets editable ([#13474](https://github.com/grafana/loki/issues/13474))
* **helm:** Only delete PVC on statefulset deletion, not scaling ([#19097](https://github.com/grafana/loki/issues/19097))
* **helm:** Support bypass all s3/gcs/azure config in object_store ([#17597](https://github.com/grafana/loki/issues/17597)) ([d6c2a8e](https://github.com/grafana/loki/commit/d6c2a8e08fe36a7a316a05f893d18ea6d6a0a434))
* **helm:** Support separate loki ruler storage configuration ([#18510](https://github.com/grafana/loki/issues/18510))
* **helm:** Support templated loki.operational_config ([#17045](https://github.com/grafana/loki/issues/17045)) ([078f353](https://github.com/grafana/loki/commit/078f353a85fdb9900e4a4505b9e8af0176ba108e))
* **helm:** Update Grafana Loki version to 3.5.3 ([#18619](https://github.com/grafana/loki/issues/18619)) ([907bb61](https://github.com/grafana/loki/commit/907bb61cc348f1c946c08ff361f9d60a0a504658))
* **helm:** Update Loki version in charts ([#18447](https://github.com/grafana/loki/issues/18447)) ([e2770ae](https://github.com/grafana/loki/commit/e2770ae6d7794d722a215b3fc4206be3d3ee3770))
* **helm:** Use named ports for memcached services targetports ([#18990](https://github.com/grafana/loki/issues/18990))
* **helm:** Delete statefulset if volumeClaimTemplates changes ([#19217](https://github.com/grafana/loki/issues/19217))
* **helm:** Added affinity property to the loki-canary DaemonSet ([#17720](https://github.com/grafana/loki/issues/17720))
* **ingester:** Add ingester_chunks_flush_requests_total ([#18696](https://github.com/grafana/loki/issues/18696))
* **ingester:** Add per policy overridable stream limits ([#18994](https://github.com/grafana/loki/pull/18994))
* **kafka:** Support multiple Kafka clients in component ([#17757](https://github.com/grafana/loki/issues/17757))
* **kafka:** Adds partition-ingester push latency histogram ([#17385](https://github.com/grafana/loki/issues/17385)) ([01cf36d](https://github.com/grafana/loki/commit/01cf36df78d53abedd76eb5162df88cff3017895))
* **kafka:** Optionally enable Kafka histograms for read/write latency ([#17089](https://github.com/grafana/loki/issues/17089)) ([e962398](https://github.com/grafana/loki/commit/e962398462cb434e252a1e41e1be44436757a435))
* **kafka:** Refactors kafka/client to improve metric collection ([#17308](https://github.com/grafana/loki/issues/17308)) ([ab65ac5](https://github.com/grafana/loki/commit/ab65ac5e8ef5ee0b46cab7c63ad8eb13fa51d0ba))
* **kafka:** Support consuming records from Kafka ([#17691](https://github.com/grafana/loki/pull/17691))
* **limits:** Add ability to filter tenant limits endpoint by allowlist ([#18521](https://github.com/grafana/loki/issues/18521)) ([73de7b5](https://github.com/grafana/loki/commit/73de7b5378f7bc9f629933ba2c7cd20fbf9a0b3a))
* **logcli:** Add delete commands to logcli ([#18375](https://github.com/grafana/loki/issues/18375)) ([f38950a](https://github.com/grafana/loki/commit/f38950a02b4e26d7e694d107f8b41e121555a1b1))
* **logcli:** Add `--log.level` parameter to help with diagnosing errors when using logcli. ([#19136](https://github.com/grafana/loki/issues/19136))
* **logs drilldown:** Return metric query partial results for drilldown ([#18286](https://github.com/grafana/loki/issues/18286)) ([c0c2265](https://github.com/grafana/loki/commit/c0c22652838af2080345cdef5d5967a5cbdb1bd5))
* **logs drilldown:** Drilldown config endpoint ([#19028](https://github.com/grafana/loki/pull/19028))
* **logs drilldown:** Implement `unwrap` as a projection
([#19409](https://github.com/grafana/loki/issues/19409))
* **logs drilldown:** Persist patterns as aggregated metrics ([#17737](https://github.com/grafana/loki/pull/17737))
* **logs drilldown:** Query persisted patterns ([#17980](https://github.com/grafana/loki/pull/17980))
* **loki-canary:** Update loki canary to support batched log push ([#17558](https://github.com/grafana/loki/pull/17558))
* **metrics:** Adds distributor lag counter to push.go ([#18012](https://github.com/grafana/loki/pull/18012))
* **metrics:** Adds User-Agent to distributor push logging ([#17764](https://github.com/grafana/loki/pull/17764))
* **mixins:** Allow disabling Thanos dashboard and fix dashboard's file name ([#18749](https://github.com/grafana/loki/issues/18749))
* **operator:** Add alert LokiIngesterFlushFailureRateCritical ([#18698](https://github.com/grafana/loki/issues/18698))
* **operator:** Add warning alert for when LokiStack is not getting ready ([#19258](https://github.com/grafana/loki/issues/19258))
* **operator:** Allow users to configure virtual-host-style for S3 secrets ([#17363](https://github.com/grafana/loki/issues/17363)) ([c4dace5](https://github.com/grafana/loki/commit/c4dace5c57646c0878fd8558d42ab1f7efaa0518))
* **operator:** Allow using resource requests as limits ([#17484](https://github.com/grafana/loki/issues/17484)) ([0b2bae8](https://github.com/grafana/loki/commit/0b2bae87f3646492129c141695811eac9e0b1ffe))
* **operator:** Deploy network policies with LokiStack ([#19099](https://github.com/grafana/loki/issues/19099))
* **operator:** Extend LokiStack authorization to support OTel Semantics ([#16031](https://github.com/grafana/loki/issues/16031))
* **operator:** S3 validation reject endpoints that contain a URL path ([#19356](https://github.com/grafana/loki/issues/19356))
* **operator:** Update Loki operand to v3.5.5 ([#19187](https://github.com/grafana/loki/issues/19187))
* **pattern ingester:** Pattern ingester pushes detected level as structured metadata ([#16924](https://github.com/grafana/loki/issues/16924)) ([66033bb](https://github.com/grafana/loki/commit/66033bb967ffbf0bbadb463b09e4509ce8c2242d))
* **pattern ingester:** Implement volume-based pattern filtering ([#18838](https://github.com/grafana/loki/pull/18838))
* **policies:** Default ingestion policy mappings merged with per-tenant mappings ([#18926](https://github.com/grafana/loki/pull/18926))
* **thanos:** Allow dashes in storage_prefix config ([#16934](https://github.com/grafana/loki/issues/16934)) ([c01d9c7](https://github.com/grafana/loki/commit/c01d9c7512f5c29230278ad3142b82d3553bd792))

### 3.6.4 (2025-01-21)

* **ruler:** Add rules check for namespace and group ([#20437](https://github.com/grafana/loki/issues/20437)) ([#20463](https://github.com/grafana/loki/issues/20463)) ([7733ab1](https://github.com/grafana/loki/commit/7733ab110ab9dc02704fec30f6afdeaa3b4180fb)).

### 3.6.5 (2025-02-05)

* **loki:** Add loki health command (backport release-3.6.x) ([#20590](https://github.com/grafana/loki/issues/20590)) ([dfdbe2a](https://github.com/grafana/loki/commit/dfdbe2a351dd0a203513ee66b6dd0b6b983332b0))

### 3.6.16 (2026-08-27)

* **compactor:** Add flag to ignore missing chunks during deletion of logs with line filter ([#24232](https://github.com/grafana/loki/issues/24232)) ([b87159f](https://github.com/grafana/loki/commit/b87159fa8cd7f93e2445e48e0c0409b403e56db7)).


## Deprecations

### 3.6.5 (2025-02-05)

- [Simple Scalable Deployment (SSD)](https://grafana.com/docs/loki/latest/get-started/deployment-modes/#simple-scalable) mode is being deprecated. The deprecation notice was posted in the CHANGELOG for Helm charts version 6.52.0. The timeline for the deprecation is to be determined (TBD), but will happen before Loki 4.0 is released.

- Loki Helm charts deprecations: The following Community maintained Helm charts have been deprecated:

  - [LGTM-distributed](https://github.com/grafana/helm-charts/tree/main/charts/lgtm-distributed)
  - [loki-canary](https://github.com/grafana/helm-charts/tree/main/charts/loki-canary)
  - [loki-distributed](https://github.com/grafana/helm-charts/tree/main/charts/loki-distributed)
  - [loki-simple-scalable](https://github.com/grafana/helm-charts/tree/main/charts/loki-simple-scalable)

   {{< admonition type="note" >}}
   In June 2025, Grafana recruited three [Grafana Champions](https://grafana.com/community/champions/) and launched a three month pilot program to maintain the Loki Helm chart. The pilot proved successful, attracting more Grafana champions, and led to the Champions asking to take over maintenance of the chart entirely. The [grafana-community/helm-charts](https://github.com/grafana-community/helm-charts) repository launched on January 30, 2026. Effective [March 16, 2026](https://github.com/grafana/loki/issues/20705), the Grafana Loki Helm chart will be forked to the new repository grafana-community/helm-charts. The motivation behind this change is to accelerate development and enable direct community maintenance, as outlined in [grafana/helm-charts#3955](https://github.com/grafana/helm-charts/issues/3955). The Champions will attempt to make this transition as seamless as possible.
   {{< /admonition >}}

### 3.6.0 (2025-11-17)

One of the focuses of Loki 3.0 was cleaning up unused code and old features that had been previously deprecated but not removed. Loki 3.0 removed a number of previous deprecations and introduces some new deprecations. Some of the main areas with changes include:

- Grafana Agent - Long term support for Grafana Agent ended October 31, 2025. For information on how to migrate to Grafana Alloy, see the [migration docs](https://grafana.com/docs/grafana-cloud/send-data/alloy/set-up/migrate/).

- [Promtail](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/promtail/) is deprecated, as the code has been merged into Grafana Alloy. Commercial support will end February 28, 2026. You can find migration documentation and a utility to convert your Promtail configuration to Alloy configuration in the [Alloy documentation](https://grafana.com/docs/alloy/latest/set-up/migrate/from-promtail/). Note that this deprecation does NOT include the lambda-promtail client.

- [Deprecated storage options](https://grafana.com/docs/loki/<LOKI_VERSION>/storage/) including the deprecation of the BoltDB store.

- [Deprecated configuration options](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/).

- [API endpoint deprecations](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/api/#deprecated-endpoints).

To learn more about breaking changes in this release, refer to the [Upgrade guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

## Upgrade Considerations

For important upgrade guidance, refer to the [Upgrade Guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

- **BREAKING CHANGE - Loki UI:** The Loki UI has been moved to a [Grafana Plugin](https://grafana.com/grafana/plugins/grafana-lokioperational-app/). Enabling the UI in the helm chart will now only enable the APIs needed by the plugin, and will host them on the querier. The gateway will now forward all UI requests to the queriers.([#19390](https://github.com/grafana/loki/issues/19390))

## Bug fixes

### 3.6.19 (2026-10-20)

* **security:** Bump Go to 1.26.10 [SECURITY] (release-3.6.x) ([#25457](https://github.com/grafana/loki/issues/25457)) ([de41836](https://github.com/grafana/loki/commit/de41836f1cfe918cc2c053deed98a751a6f639ee))

### 3.6.18 (2026-10-15)

* **deps:** Update dependencies related to otel for 3.6 ([#25180](https://github.com/grafana/loki/issues/25180)) ([603de5a](https://github.com/grafana/loki/commit/603de5ad370c2b45ca38f031a8116257b018d89c)).
* **security/LOW/:** Update module go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc to v1.45.0 ([#24619](https://github.com/grafana/loki/issues/24619)) ([3e7c0ed](https://github.com/grafana/loki/commit/3e7c0ed5c6aaf287325dfda43c1cb1e90eeceaa8)).
* **security/LOW/:** Update module go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp to v1.45.0 ([#24620](https://github.com/grafana/loki/issues/24620)) ([ad5a87c](https://github.com/grafana/loki/commit/ad5a87c611d386ac9b33ae04dfdd674a4620f72d)).
* **security:** Bump Go to 1.26.9 ([#25193](https://github.com/grafana/loki/issues/25193)) ([c969f5c](https://github.com/grafana/loki/commit/c969f5cf4b4ca6073d29e0a74b236f4d05d7b79a)).

### 3.6.17 (2026-09-09)

* **security/HIGH/pkg/push:** Update module google.golang.org/grpc to v1.83.1 ([#24330](https://github.com/grafana/loki/issues/24330)) ([d8d78f6](https://github.com/grafana/loki/commit/d8d78f6e86d80942b3b9438777c4bf324cae1184)).
* **security/HIGH/pkg/push:** Update module google.golang.org/grpc to v1.83.2 ([#24444](https://github.com/grafana/loki/issues/24444)) ([6d21d6b](https://github.com/grafana/loki/commit/6d21d6b0a32cef7cd7e6599219d56745d065bbba)).
* **security/HIGH/:** Update module google.golang.org/grpc to v1.83.1 ([#24329](https://github.com/grafana/loki/issues/24329)) ([ed9df58](https://github.com/grafana/loki/commit/ed9df58cc7ca6c76178f0f707c7dd53d5a20a6d0)).
* **security/HIGH/:** Update module google.golang.org/grpc to v1.83.2 ([#24430](https://github.com/grafana/loki/issues/24430)) ([ee0ef56](https://github.com/grafana/loki/commit/ee0ef56505bfd4a95da450a36235a53ca1c94aad)).
* **security/MEDIUM/:** Update module github.com/containerd/containerd/v2 to v2.0.12 ([#24461](https://github.com/grafana/loki/issues/24461)) ([7fa1224](https://github.com/grafana/loki/commit/7fa1224d9f01d83656dc4754456a727b7fb572a0)).
* **security/UNKNOWN/:** Update module golang.org/x/crypto to v0.55.0 ([#24256](https://github.com/grafana/loki/issues/24256)) ([85b2d4f](https://github.com/grafana/loki/commit/85b2d4fb134fe97e51ea6ef25e8da999d732bb8d)).
* **security/UNKNOWN/:** Update module golang.org/x/crypto to v0.56.0 ([#24336](https://github.com/grafana/loki/issues/24336)) ([57bc922](https://github.com/grafana/loki/commit/57bc92283c01e1eba7744e111c881459a4c11e49)).
* **security/UNKNOWN/:** Update module golang.org/x/mod to v0.40.0 ([#23963](https://github.com/grafana/loki/issues/23963)) ([c08bf30](https://github.com/grafana/loki/commit/c08bf30195ba8e59a4b79729ad79fd634fdc5b70)).

### 3.6.16 (2026-08-27)

* **security/UNKNOWN/:** Update module go.etcd.io/etcd/client/pkg/v3 to v3.5.33 ([#24062](https://github.com/grafana/loki/issues/24062)) ([bf4b367](https://github.com/grafana/loki/commit/bf4b3670ff3e2eb9f29183d6dfb4d729b83f62ec)).
* **storage:** Pre-compute SHA-256 to avoid aws-chunked on PutObject ([#24214](https://github.com/grafana/loki/issues/24214)) ([0a29895](https://github.com/grafana/loki/commit/0a29895fe461acbf848376a451896d9f8a542b3d)).

### 3.6.15 (2026-08-05)

* **queryrange:** Preserve sketch in MergeLabels ([#23771](https://github.com/grafana/loki/issues/23771)) ([20087cd](https://github.com/grafana/loki/commit/20087cd091329dc7184b51cfedf5ab330a21795e))

### 3.6.14 (2026-08-04)

* **ci:** Backport deb/rpm packaging fix to release-3.6.x ([#23680](https://github.com/grafana/loki/issues/23680)) ([#23722](https://github.com/grafana/loki/issues/23722)) ([d7ee4de](https://github.com/grafana/loki/commit/d7ee4de3b8bd4015a50ad8e135a45a9513695230))
* **ingester:** Backport WAL replay hang fix to release-3.6.x ([#21176](https://github.com/grafana/loki/issues/21176)) ([#23630](https://github.com/grafana/loki/issues/23630)) ([355e2f3](https://github.com/grafana/loki/commit/355e2f35dbcb56913f6fdac9a76def563299e68b))
* **security/UNKNOWN/:** Update module github.com/klauspost/compress to v1.18.7 (release-3.6.x) ([#23614](https://github.com/grafana/loki/issues/23614)) ([8308a12](https://github.com/grafana/loki/commit/8308a123582d6eea1c947566e0dcb30f3a6a422f))
* **security/UNKNOWN/cmd/chunks-inspect:** Update module github.com/klauspost/compress to v1.18.7 (release-3.6.x) ([#23615](https://github.com/grafana/loki/issues/23615)) ([a6cc2e5](https://github.com/grafana/loki/commit/a6cc2e5b24f918109d05c0583cb498f83e44f1a6))
* **security/UNKNOWN/cmd/dataobj-inspect:** Update module github.com/klauspost/compress to v1.18.7 (release-3.6.x) ([#23616](https://github.com/grafana/loki/issues/23616)) ([a71c910](https://github.com/grafana/loki/commit/a71c91023d3d3b7eddb3652265bf2bd53ff0072b))
* **security/UNKNOWN/operator:** Update module github.com/klauspost/compress to v1.18.7 (release-3.6.x) ([#23618](https://github.com/grafana/loki/issues/23618)) ([59970a9](https://github.com/grafana/loki/commit/59970a9be7a439cb26063f2e49e0dc3fc7cec201))

### 3.6.13 (2026-07-23)

* **ci:** Fix zizmor findings for operator-images in Loki 3.6 ([#22821](https://github.com/grafana/loki/issues/22821)) ([050f742](https://github.com/grafana/loki/commit/050f7422d671599830e9e59e89cca281039b6021))
* **ci:** Helm CI warning fix ([#22606](https://github.com/grafana/loki/issues/22606)) ([4bc2586](https://github.com/grafana/loki/commit/4bc258651f36107086fc7028c4af1105618e67a4))
* **deps:** Update objstore to include fix for GCS Exists ([#23381](https://github.com/grafana/loki/issues/23381)) ([87383d8](https://github.com/grafana/loki/commit/87383d801fa71a1cc7d3732b6e8a07372aee7558))
* **security/CRITICAL/clients/cmd/fluentd/docker:** Update dependency fluentd to v1.19.3 (release-3.6.x) ([#22694](https://github.com/grafana/loki/issues/22694)) ([692f2b1](https://github.com/grafana/loki/commit/692f2b1183670b4e6df0436081a00b8dcc2d640a))
* **security/HIGH/:** Bump Go to 1.26.5 to address CVE-2026-39822 and CVE-2026-42505 ([#23398](https://github.com/grafana/loki/issues/23398)) ([fc478e1](https://github.com/grafana/loki/commit/fc478e1d3491aea695080498d7ac3c0f065061c3))
* **security/HIGH/:** Update golang.org/x/net, golang.org/x/text and google.golang.org/grpc ([70f75b7](https://github.com/grafana/loki/commit/70f75b7fbd0c6bab35a84700ac2ee531cfd483ed))
* **security/HIGH/pkg/push:** Update module google.golang.org/grpc to v1.82.1 ([70f75b7](https://github.com/grafana/loki/commit/70f75b7fbd0c6bab35a84700ac2ee531cfd483ed))
* **security/UNKNOWN/:** Update module github.com/containerd/containerd/v2 to v2.0.10 (release-3.6.x) ([#22479](https://github.com/grafana/loki/issues/22479)) ([697bd83](https://github.com/grafana/loki/commit/697bd835b91ae9a04c6476bb17d86975c624a642))
* **security/UNKNOWN/cmd/chunks-inspect:** Update go toolchain directive to v1.25.12 (release-3.6.x) ([#23131](https://github.com/grafana/loki/issues/23131)) ([b327439](https://github.com/grafana/loki/commit/b3274394654e3f75996843924d54e3a8ea4c120f))
* **security/UNKNOWN/pkg/push:** Update module golang.org/x/net to v0.56.0 ([70f75b7](https://github.com/grafana/loki/commit/70f75b7fbd0c6bab35a84700ac2ee531cfd483ed))
* **security/UNKNOWN/pkg/push:** Update module golang.org/x/text to v0.39.0 ([70f75b7](https://github.com/grafana/loki/commit/70f75b7fbd0c6bab35a84700ac2ee531cfd483ed))
* **security:** Backport security updates to release-3.6.x ([#23403](https://github.com/grafana/loki/issues/23403)) ([70f75b7](https://github.com/grafana/loki/commit/70f75b7fbd0c6bab35a84700ac2ee531cfd483ed))

### 3.6.12 (2026-06-24)

* **deps:** Update `go.opentelemetry.io/otel/sdk` to v1.43.0 ([#21480](https://github.com/grafana/loki/issues/21480)) ([47fb29e](https://github.com/grafana/loki/commit/47fb29e)).
* **deps:** Update `golang.org/x/crypto` to v0.52.0 ([#22212](https://github.com/grafana/loki/issues/22212)) ([424e5ff](https://github.com/grafana/loki/commit/424e5ff)).
* **deps:** Update `golang.org/x/net` to v0.55.0 ([#22213](https://github.com/grafana/loki/issues/22213)) ([90ae8f3](https://github.com/grafana/loki/commit/90ae8f3)).
* **deps:** Update `golang.org/x/sys` to v0.44.0 ([#22214](https://github.com/grafana/loki/issues/22214)) ([49b9a22](https://github.com/grafana/loki/commit/49b9a22)).
* **deps:** Update `golang.org/x/sys` to v0.44.0 in cmd/dataobj-inspect (release-3.6.x) ([#22215](https://github.com/grafana/loki/issues/22215)) ([c696c66](https://github.com/grafana/loki/commit/c696c66)).
* **deps:** Update `golang.org/x/crypto` to v0.52.0 in cmd/segment-inspect (release-3.6.x) ([#22216](https://github.com/grafana/loki/issues/22216)) ([807ffd7](https://github.com/grafana/loki/commit/807ffd7)).
* **deps:** Update `golang.org/x/net` to v0.55.0 in cmd/segment-inspect (release-3.6.x) ([#22217](https://github.com/grafana/loki/issues/22217)) ([7081875](https://github.com/grafana/loki/commit/7081875)).
* **deps:** Update `golang.org/x/sys` to v0.44.0 in cmd/segment-inspect (release-3.6.x) ([#22218](https://github.com/grafana/loki/issues/22218)) ([36deca1](https://github.com/grafana/loki/commit/36deca1)).
* **deps:** Update `golang.org/x/net` to v0.55.0 in pkg/push (release-3.6.x) ([#22224](https://github.com/grafana/loki/issues/22224)) ([0c53ce1](https://github.com/grafana/loki/commit/0c53ce1)).
* **deps:** Update go toolchain to v1.25.11 in cmd/chunks-inspect (release-3.6.x) ([#22202](https://github.com/grafana/loki/issues/22202)) ([90e12c9](https://github.com/grafana/loki/commit/90e12c9)).
* **deps:** Update go toolchain to v1.25.11 in cmd/segment-inspect (release-3.6.x) ([#22203](https://github.com/grafana/loki/issues/22203)) ([2c67a33](https://github.com/grafana/loki/commit/2c67a33)).
* **security/HIGH:** Update `github.com/containerd/containerd/v2` to v2.0.9 ([#22204](https://github.com/grafana/loki/issues/22204)) ([ed38a68](https://github.com/grafana/loki/commit/ed38a68)).
* **security/HIGH:** Update `github.com/prometheus/prometheus` to v0.311.3 in cmd/segment-inspect (release-3.6.x) ([#22205](https://github.com/grafana/loki/issues/22205)) ([bfd37e4](https://github.com/grafana/loki/commit/bfd37e4)).
* **security/HIGH:** Update `go.opentelemetry.io/otel` to v1.41.0 in cmd/segment-inspect (release-3.6.x) ([#22206](https://github.com/grafana/loki/issues/22206)) ([0ed7bce](https://github.com/grafana/loki/commit/0ed7bce)).
* **security/MEDIUM:** Update `github.com/grafana/loki/v3` to v3.6.4 in cmd/dataobj-inspect (release-3.6.x) ([#22209](https://github.com/grafana/loki/issues/22209)) ([d6117ed](https://github.com/grafana/loki/commit/d6117ed)).
* **security/MEDIUM:** Update `github.com/grafana/loki/v3` to v3.6.4 in cmd/segment-inspect (release-3.6.x) ([#22210](https://github.com/grafana/loki/issues/22210)) ([186b9a9](https://github.com/grafana/loki/commit/186b9a9)).

### 3.6.11 (2025-05-13)

* **deps:** CVEs in release 3.7.x ([#21771](https://github.com/grafana/loki/issues/21771)) ([bb4c5d8](https://github.com/grafana/loki/commit/bb4c5d8758b8540cb742466683143a9ea93743c8))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.97.3 ([#21457](https://github.com/grafana/loki/issues/21457)) ([7bc9450](https://github.com/grafana/loki/commit/7bc945082fd7ef7632a9bfd18cc22f23130ea64a)).
* **ruler:** Fix ruler panic related to unset validation scheme ([#21401](https://github.com/grafana/loki/issues/21401)) ([cf65729](https://github.com/grafana/loki/commit/cf65729674b1f0be8011223c474df3e2e5253216)).
* **storage:** Attach SHA-256 checksum on PutObject for Object Lock buckets ([#21849](https://github.com/grafana/loki/issues/21849)) ([7df13d9](https://github.com/grafana/loki/commit/7df13d9ad3993700c3aff23edf5dfa2966281416)).

### 3.6.10 (2025-04-03)

* **ci** Backporting [#19989](https://github.com/grafana/loki/pull/19989) Add build tags to Promtail build commands, into 3.6 ([#21356](https://github.com/grafana/loki/issues/21356)) ([0f56890](https://github.com/grafana/loki/commit/0f56890f0536200d1ebb22bcc3488d0e79d9285b)).

### 3.6.9 (2025-04-01)

This release does not exist. We triggered the release pipeline, but it decided to play an April Fool's joke on us and did not generate the release.

### 3.6.8 (2025-03-25)

* **deps:** Upgrade `go.opentelemetry.io/otel/sdk` from v1.38.0 to v1.40.0 ([#21115](https://github.com/grafana/loki/issues/21115)) ([d1ab148](https://github.com/grafana/loki/commit/d1ab148aa279e9e5263f699fa1b1a0e1eec14f1a)).
* **deps:** Upgrade Go to 1.25.8 (release-3.6.x) ([#21240](https://github.com/grafana/loki/issues/21240)) ([10d2666](https://github.com/grafana/loki/commit/10d266699420c158410e5af2521fff6ac4440d61)).
* **deps:** Upgrade Go used by querytee/promtail to 3.6 ([#21244](https://github.com/grafana/loki/issues/21244)) ([41a4e0c](https://github.com/grafana/loki/commit/41a4e0c702d35c52181ffb32d34bf8ad98cd7717)).
* **deps:** Use different debian version for fluent-bit ([#21247](https://github.com/grafana/loki/issues/21247)) ([138c391](https://github.com/grafana/loki/commit/138c391fd0237b8bf1eb9ecf9eb14d8ce04727c5)).
* **deps:** Update module github.com/buger/jsonparser to v1.1.2 ([#21201](https://github.com/grafana/loki/issues/21201)) ([3185466](https://github.com/grafana/loki/commit/318546620179a198d9af21baab9c601f5cc367c1)).
* **deps:** Update module go.opentelemetry.io/otel/sdk to v1.40.0 ([#20887](https://github.com/grafana/loki/issues/20887)) ([d267ad3](https://github.com/grafana/loki/commit/d267ad368873ab3886519b975c86d76ded05741b)).
* **deps:** Update module google.golang.org/grpc to v1.79.3 ([#21193](https://github.com/grafana/loki/issues/21193)) ([87dff41](https://github.com/grafana/loki/commit/87dff4138d72c6616c47bcd2d46f04692af8d01f)).

### 3.6.7 (2025-02-23)

* **alloc:** Set a limit on preallocations (backport release-3.6.x) ([#20920](https://github.com/grafana/loki/issues/20920)) ([7e1daf3](https://github.com/grafana/loki/commit/7e1daf3a36f68639a5d06c1ac057a2fcbf0dce40)).

### 3.6.6 (2025-02-16)

* **deps:** Update Alpine Docker tag to v3.23.3 (main) ([#20630](https://github.com/grafana/loki/issues/20630)) ([#20832](https://github.com/grafana/loki/issues/20832)) ([fb20246](https://github.com/grafana/loki/commit/fb202465c6e9fdda198e06d6588de8381ded79e7)).

### 3.6.5 (2025-02-05)

* **deps:** Bump go version for 3.6.x ([#20667](https://github.com/grafana/loki/issues/20667)) ([b06b508](https://github.com/grafana/loki/commit/b06b508e821a22e7913d3caefb6a61f56ad69089))
* **logql:** LineFilterLabelFilter.String() regexp correct delimiters (backport release-3.6.x) ([#20649](https://github.com/grafana/loki/issues/20649)) ([9d5fb5f](https://github.com/grafana/loki/commit/9d5fb5fa5eceacec96be55c36c3091d5aba552ba))

### 3.6.4 (2025-01-21)

* **deps:** Update dskit to resolve Otel conflict ([#20368](https://github.com/grafana/loki/issues/20368)) ([fb05a36](https://github.com/grafana/loki/commit/fb05a368466e6b16ac70105f69e460871c0aa42e)).
* **distributor:** Backport gzip fix release 3.6.x ([#20514](https://github.com/grafana/loki/issues/20514)) ([d805266](https://github.com/grafana/loki/commit/d80526692af3bf28c35622a59a3231933b193bc2)).

### 3.6.3 (2025-12-11)

* **deps:** Update to Go 1.25 ([#20204](https://github.com/grafana/loki/issues/20204)).
* **storage:** Do not override S3 region if already specified in configuration chain (backport release-3.6.x) ([#20128](https://github.com/grafana/loki/issues/20128)) ([38582ac](https://github.com/grafana/loki/commit/38582ac8dcce1397580dddd1ecac51692bad7413)).
* **storage:** Fix regression in S3 client configuration (backport release-3.6.x) ([#20118](https://github.com/grafana/loki/issues/20118)) ([7d8176f](https://github.com/grafana/loki/commit/7d8176f4dd9c00ded84444360e31d7118abf66f0)).

### 3.6.2 (2025-11-25)

* **deps:** Loki 3.6.1 CVE updates.

### 3.6.1 (2025-11-21)

* **docker:** Missing permissions to start Docker ([#19948](https://github.com/grafana/loki/issues/19948)) ([48b507f](https://github.com/grafana/loki/commit/48b507f62f4d5a92cbf2fcb5025a1f1cdc199411)).
* **docker:** Set WORKDIR to root in Loki Dockerfiles ([#19952](https://github.com/grafana/loki/issues/19952)) ([6040a09](https://github.com/grafana/loki/commit/6040a09db017fa4b66e0805b0e08092b66a93a51)).

### 3.6.0 (2025-11-17)

* **aggregated metrics:** Hide `__aggregated_metric__` in /series and /labels. This will alleviate confusion for people using a tool like logcli series --analyze-labels for cardinality analysis. ([#14677](https://github.com/grafana/loki/issues/14677)) ([4b0aaaa](https://github.com/grafana/loki/commit/4b0aaaaf2e22575381d2cfe90564a2cee6ddc7e8))
* **build:** Update purgo package ([#17807](https://github.com/grafana/loki/pull/17807))
* **build:** Remove busybox from dockerfile ([#19502](https://github.com/grafana/loki/issues/19502))
   {{< admonition type="note" >}}
   You cannot exec into the container with a shell anymore. For more information see the [upgrade topic](https://grafana.com/docs/loki/latest/setup/upgrade/#loki-358).
   {{< /admonition >}}
* **build:** Remove UI from docker build ([#19425](https://github.com/grafana/loki/issues/19425))
* **build:** RPM signature config ([#19476](https://github.com/grafana/loki/issues/19476))
* **build:** Update ckit ([#18997](https://github.com/grafana/loki/issues/18997))
* **chunkenc:** Avoid memory allocations when loading symbolizer from a flushed chunk ([#17953](https://github.com/grafana/loki/issues/17953))
* **ci:** Fix deprecation errors from staticcheck linter ([#17210](https://github.com/grafana/loki/issues/17210)) ([cfe3675](https://github.com/grafana/loki/commit/cfe36752952cfa1aac6f59bb5804999e09ef1e41))
* **ci:** Fix lambda-promtail image build ([#18443](https://github.com/grafana/loki/issues/18443)) ([8f0ba2e](https://github.com/grafana/loki/commit/8f0ba2eae6e01d96523d8a0d71fb0bf1ae29a224))
* **ci:** Fix workflow timeout added in wrong format ([#18348](https://github.com/grafana/loki/issues/18348)) ([53ba9c7](https://github.com/grafana/loki/commit/53ba9c7fd63465209e9919ef1e5346dbeb66b2aa))
* **ci:** Read releaseLibRef from jsonnet.json lockfile ([#18538](https://github.com/grafana/loki/issues/18538)) ([26417b5](https://github.com/grafana/loki/commit/26417b594593269339e440df2d3cfbdf6bb06541))
* **compactor:** Return request ID in response header ([#19444](https://github.com/grafana/loki/issues/19444))
* **compactor:** Concurrent map access for processedSeries in delete requests manager ([#17469](https://github.com/grafana/loki/issues/17469)) ([32c5088](https://github.com/grafana/loki/commit/32c50888b04de299380895585dce1c77755c488c))
* **compactor:** Fix timeout and series progress marker for same requests with different shards ([#17125](https://github.com/grafana/loki/issues/17125)) ([288ec8c](https://github.com/grafana/loki/commit/288ec8c64dca6457fb2287deed9ba841e70c61ae))
* **compactor:** Move the check for duplicate series up the call stack to fix an issue when retention is enabled ([#17663](https://github.com/grafana/loki/issues/17663)) ([a5e95ba](https://github.com/grafana/loki/commit/a5e95bacee50ce4d2512945f697a7aaff4403139))
* **compactor:** Wire up deletion series progress tracking ([#17099](https://github.com/grafana/loki/issues/17099)) ([db59d0e](https://github.com/grafana/loki/commit/db59d0e9b2c463d85238e13a0c8dfa8a66e3967d))
* **compactor:** Do not try to merge the already consolidated delete requests while listing them ([#18544](https://github.com/grafana/loki/issues/18544)) ([8054076](https://github.com/grafana/loki/commit/8054076873f925a539119e46422a4129766b9541))
* **compactor:** Ensure to return delete requests only for the requested tenant ([#18589](https://github.com/grafana/loki/issues/18589)) ([cdb190b](https://github.com/grafana/loki/commit/cdb190be96eeed8087c9692726abae01ce6e7208))
* **compactor:** Fix handling of duplicate delete requests ([#18460](https://github.com/grafana/loki/issues/18460)) ([255fce2](https://github.com/grafana/loki/commit/255fce20d813fc0b39758b6dbf51b23b987f0609))
* **compactor:** Fix lint failure in retention.go ([#17894](https://github.com/grafana/loki/issues/17894))
* **compactor:** Use the custom objectExists function to work around bug in Thanos object client ([#18820](https://github.com/grafana/loki/issues/18820))
* **compactor:** Fix panic while applying storage updates when a whole chunk is deleted by line filters ([#19262](https://github.com/grafana/loki/issues/19262))
* **compactor:** Set delete request batch to empty when we load to fail requests ([#17810](https://github.com/grafana/loki/issues/17810))
* **config_wrapper:** Apply `instance_enable_ipv6` from common to all components ([#18254](https://github.com/grafana/loki/issues/18254)) ([5bc5853](https://github.com/grafana/loki/commit/5bc58538384a0895db47f57338a36cbe16049e52))
* **deps:** Update to Go 1.24.2 (#17804)
* **deps:** Update go toolchain to 1.24.6 ([#19449](https://github.com/grafana/loki/issues/19449))
* **deps:** Patch mapstructure dependency to remove CVE (#19447)
* **deps:** Remove workspace files from ignore and regenerate vendor (#17234)
* **deps:** Update dskit dependency ([#18711](https://github.com/grafana/loki/issues/18711)) ([19233d0](https://github.com/grafana/loki/commit/19233d0461e7028e8bdb394f775cc22339c2c38c))
* **deps:** Update dskit to fix linux/arm build ([#18715](https://github.com/grafana/loki/issues/18715)) ([f39fc1e](https://github.com/grafana/loki/commit/f39fc1e6e623bc88bf4d5d8e2e0c2ca039f5967f))
* **deps:** Update dependency @hookform/resolvers to v5.2.2 (main) (#19276)
* **deps:** Update dependency @radix-ui.react-checkbox to v1.3.3 (main) ([#18836](https://github.com/grafana/loki/issues/18836))
* **deps:** Update dependency @radix-ui/react-collapsible to v1.1.8 (main) ([#17382](https://github.com/grafana/loki/issues/17382)) ([0a4aec6](https://github.com/grafana/loki/commit/0a4aec6ea74745a194fd5b0de833f027928322f3))
* **deps:** Update dependency @radix-ui.react-dialog to v1.1.15 (main) ([#18841](https://github.com/grafana/loki/issues/18841))
* **deps:** Update dependency @radix-ui.react-dropdown-menu to v2.1.16 (main) ([#18845](https://github.com/grafana/loki/issues/18845))
* **deps:** Update dependency @radix-ui.react-hover-card to v1.1.15 (main)([#18849](https://github.com/grafana/loki/issues/18849))
* **deps:** Update dependency @radix-ui/react-label to v2.1.7 (main) ([#17877](https://github.com/grafana/loki/issues/17877))
* **deps:** Update dependency @radix-ui.react-popover to v1.1.15 (main) ([#18850](https://github.com/grafana/loki/issues/18850))
* **deps:** Update dependency @radix-ui/react-progress to v1.1.7 (main) ([#17879](https://github.com/grafana/loki/issues/17879))
* **deps:** Update dependency @radix-ui.react-scroll-area to v1.2.10 (main) ([#18852](https://github.com/grafana/loki/issues/18852))
* **deps:** Update dependency @radix-ui.react-select to v2.2.6 (main) ([#18853](https://github.com/grafana/loki/issues/18853))
* **deps:** Update dependency @radix-ui/react-separator to v1.1.7 (main) ([#17882](https://github.com/grafana/loki/issues/17882))
* **deps:** Update dependency @radix-ui/react-slot to v1.2.3 (main) ([#17883](https://github.com/grafana/loki/issues/17883))
* **deps:** Update dependency @radix-ui.react-switch to v1.2.6 (main)([#18854](https://github.com/grafana/loki/issues/18854))
* **deps:** Update dependency @radix-ui/react-tabs to v1.1.9 (main) ([#17389](https://github.com/grafana/loki/issues/17389)) ([23a2f2d](https://github.com/grafana/loki/commit/23a2f2d0efde80d48acfb7f2561ad2cafe02b62f))
* **deps:** Update dependency @radix-ui/react-toast to v1.2.7 (main) ([#17148](https://github.com/grafana/loki/issues/17148)) ([1a1cbb3](https://github.com/grafana/loki/commit/)
* **deps:** Update dependency @radix-ui.react-toggle to v1.1.10 (main)([#18859](https://github.com/grafana/loki/issues/18859))
* **deps:** Update dependency @radix-ui.react-toggle-group to v1.1.11 (main) ([#18860](https://github.com/grafana/loki/issues/18860))
* **deps:** Update dependency @radix-ui.react-tooltip to v1.2.8 (main) ([#18862](https://github.com/grafana/loki/issues/18862))
* **deps:** Update dependency @tanstack.react-query to v5.85.5 (main) ([#18962](https://github.com/grafana/loki/issues/18962))
* **deps:** Update dependency @tanstack/react-query-devtools to v5.85.5 (main) ([#18963](https://github.com/grafana/loki/issues/18963))
* **deps:** Update dependency @types/lodash to v4.17.20 (main) ([#18305](https://github.com/grafana/loki/issues/18305)) ([90ca918](https://github.com/grafana/loki/commit/90ca91834ebc01a733a3b866d6322df6ba514a2d))
* **deps:** Update module cloud.google.com/go/bigtable to v1.40.1 (main) ([#19337](https://github.com/grafana/loki/issues/19337))
* **deps:** Update module cloud.google.com/go/pubsub to v1.49.0 (main) ([#17187](https://github.com/grafana/loki/issues/17187)) ([cbd73d0](https://github.com/grafana/loki/commit/cbd73d0a883f9f9ac0d7151e02785841062bdf4c))
* **deps:** Update module cloud.google.com/go/storage to v1.57.0 (main) ([#19312](https://github.com/grafana/loki/issues/19312))
* **deps:** Update module dario.cat/mergo to v1.0.2 (main) ([#17651](https://github.com/grafana/loki/issues/17651)) ([bb8a9d9](https://github.com/grafana/loki/commit/bb8a9d9187c16773c4455197222911b6fc5c69d8))
* **deps:** Update module github.com.prometheus/otlptranslator to v0.0.2 (main) (#18782)
* **deps:** Update module github.com/alecthomas/chroma/v2 to v2.20.0 (main) ([#18710](https://github.com/grafana/loki/issues/18710)) ([c8e87dd](https://github.com/grafana/loki/commit/c8e87ddfe5ef2139b2396b6d904f6afca3eca03a))
* **deps:** Update module github.com/alicebob/miniredis/v2 to v2.35.0 (main) (#18029)
* **deps:** Update module github.com/apache/arrow-go/v18 to v18.4.0 (main) ([#18519](https://github.com/grafana/loki/issues/18519)) ([600fc9e](https://github.com/grafana/loki/commit/600fc9e7ede39625f7afff278e17744a5df6b793))
* **deps:** Update module github.com/aws/aws-lambda-go to v1.49.0 (main) ([#18036](https://github.com/grafana/loki/issues/18036))
* **deps:** Update module github.com/aws/aws-sdk-go to v1.55.7 (main) ([#17402](https://github.com/grafana/loki/issues/17402)) ([06a803d](https://github.com/grafana/loki/commit/06a803d6550c74d821b34d42db3e84558b66054f))
* **deps:** Update module github.com/aws/aws-sdk-go-v2 to v1.36.6 (main) ([#18506](https://github.com/grafana/loki/issues/18506)) ([1d51304](https://github.com/grafana/loki/commit/1d51304359f96fb57d23d6f90787b5583acb8d9f))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/config to v1.29.18 (main) ([#18507](https://github.com/grafana/loki/issues/18507)) ([4ea7409](https://github.com/grafana/loki/commit/4ea7409b5bdc619c6bf9d9493567e6743e475540))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/credentials to v1.18.16 (main) ([#19352](https://github.com/grafana/loki/issues/19352))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/dynamodb to v1.51.0 (main) ([#19388](https://github.com/grafana/loki/issues/19388))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.84.1 (main) ([#18508](https://github.com/grafana/loki/issues/18508)) ([4fdae62](https://github.com/grafana/loki/commit/4fdae62915990afec358a9724894a5b23e201b52))
* **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.245 (main) ([#19278](https://github.com/grafana/loki/issues/19278))
* **deps:** Update module github.com/bmatcuk/doublestar/v4 to v4.9.1 (main) ([#18599](https://github.com/grafana/loki/issues/18599)) ([17b377f](https://github.com/grafana/loki/commit/17b377fd17b70342816362cd5f338dec9e6f288b))
* **deps:** Update module github.com/charmbracelet/bubbles to v0.21.0 (main) ([#17191](https://github.com/grafana/loki/issues/17191)) ([3e9c07e](https://github.com/grafana/loki/commit/3e9c07e400df29cada15e62f8fef897bc8fc9347))
* **deps:** Update module github.com/charmbracelet/bubbletea to v1.3.10 (main) ([#19279](https://github.com/grafana/loki/issues/19279))
* **deps:** Update module github.com/coder/quartz to v0.2.1 (main) ([#17922](https://github.com/grafana/loki/issues/17922)) ([3cb216c](https://github.com/grafana/loki/commit/3cb216c33c948338014db91ff49b26aa0800eeac))
* **deps:** Update module github.com/docker/docker to v28.5.0+incompatible (main) ([#19414](https://github.com/grafana/loki/issues/19414))
* **deps:** Update module github.com/fsnotify/fsnotify to v1.9.0 (main) ([#17192](https://github.com/grafana/loki/issues/17192)) ([eb4b651](https://github.com/grafana/loki/commit/eb4b651adf810b0c1b8b7dcd2be0040ff37f5a05))
* **deps:** Update module github.com/fsouza/fake-gcs-server to v1.52.3 (main) (#18999)
* **deps:** Update module github.com/go-logfmt/logfmt to v0.6.1 (main) (#19412)
* **deps:** Update module github.com/grafana/loki/v3 to v3.5.5 (main) ([#18444](https://github.com/grafana/loki/issues/18444))
* **deps:** Update module github.com/grpc-ecosystem/go-grpc-middleware/v2 to v2.3.2 (main) ([#17652](https://github.com/grafana/loki/issues/17652)) ([988857e](https://github.com/grafana/loki/commit/988857ea45a6ccb64999791cb694b79427948a7a))
* **deps:** Update module github.com/hashicorp/consul/api to v1.32.1 (main) ([#17653](https://github.com/grafana/loki/issues/17653)) ([44b9b64](https://github.com/grafana/loki/commit/44b9b6422c9a3f1bbbd5bbb65ca83ee9dbf6853d))
* **deps:** Update module github.com/heroku/x to v0.5.2 (main) ([#18809](https://github.com/grafana/loki/issues/18809))
* **deps:** Update module github.com/ibm/go-sdk-core/v5 to v5.21.0 (main) ([#18587](https://github.com/grafana/loki/issues/18587)) ([86a31ec](https://github.com/grafana/loki/commit/86a31eca0eee97cfd27065289e7e29b36627a502))
* **deps:** Update module github.com/ibm/ibm-cos-sdk-go to v1.12.3 (main) ([#19281](https://github.com/grafana/loki/issues/19281))
* **deps:** Update module github.com/ibm/sarama to v1.46.1 (main) ([#19282](https://github.com/grafana/loki/issues/19282))
* **deps:** Update module github.com/minio/minio-go/v7 to v7.0.95 (main) ([#18514](https://github.com/grafana/loki/issues/18514)) ([70c176c](https://github.com/grafana/loki/commit/70c176cd9984f1908cbe184c62fb7af197072021))
* **deps:** Update module github.com/ncw/swift/v2 to v2.0.4 (main) ([#18011](https://github.com/grafana/loki/issues/18011))
* **deps:** Update module github.com/oklog/run to v1.2.0 (main) ([#18263](https://github.com/grafana/loki/issues/18263)) ([3011a6c](https://github.com/grafana/loki/commit/3011a6c7d67640356146aa4a9858d6dac7cc6fe7))
* **deps:** Update module github.com/opentracing-contrib/go-grpc to v0.1.2 (main) ([#17164](https://github.com/grafana/loki/issues/17164)) ([23b47ce](https://github.com/grafana/loki/commit/23b47ce7a5c707dc046547165d9c2e8d812ec8ba))
* **deps:** Update module github.com/oschwald/geoip2-golang to v1.13.0 (main) ([#18354](https://github.com/grafana/loki/issues/18354))
* **deps:** Update module github.com/parquet-go/parquet-go to v0.25.1 (main) ([#18013](https://github.com/grafana/loki/issues/18013))
* **deps:** Update module github.com/pressly/goose/v3 to v3.26.0 (main) ([#19417](https://github.com/grafana/loki/issues/19417))
* **deps:** Update module github.com/prometheus/client_golang to v1.23.0 (main) ([#18674](https://github.com/grafana/loki/issues/18674)) ([07a627a](https://github.com/grafana/loki/commit/07a627a40d59f405f140d13d425e1b15b4fd1a8b))
* **deps:** Update module github.com/prometheus/client_model to v0.6.2 (main) ([#17171](https://github.com/grafana/loki/issues/17171)) ([fa84382](https://github.com/grafana/loki/commit/fa8438278835f82af329fb5a00df7d9b0a398965))
* **deps:** Update module github.com/prometheus/common to v0.65.0 (main) ([#18196](https://github.com/grafana/loki/issues/18196)) ([3ad1077](https://github.com/grafana/loki/commit/3ad1077cc6c8cbcfeadd3e32ffc169b865724fb9))
* **deps:** Update module github.com/prometheus/prometheus to v0.304.2 (main) ([#18248](https://github.com/grafana/loki/issues/18248)) ([b2e1d7f](https://github.com/grafana/loki/commit/b2e1d7f267328203de078a065f7b4101d740192f))
* **deps:** Update module github.com/prometheus/sigv4 to v0.2.1 (main) ([#18454](https://github.com/grafana/loki/issues/18454)) ([9ea365a](https://github.com/grafana/loki/commit/9ea365a96007541e062e1f7a469d90f6e64436de))
* **deps:** Update module github.com/redis/go-redis/v9 to v9.12.1 (main) ([#18218](https://github.com/grafana/loki/issues/18218))
* **deps:** Update module github.com/shirou/gopsutil/v4 to v4.25.9 (main) ([#19366](https://github.com/grafana/loki/issues/19366))
* **deps:** Update module github.com/sony/gobreaker/v2 to v2.3.0 (main) ([#19418](https://github.com/grafana/loki/issues/19418))
* **deps:** Update module github.com/spf13/afero to v1.15.0 (main) ([#19317](https://github.com/grafana/loki/issues/19317))
* **deps:** Update module github.com/stretchr/testify to v1.11.1 (main) ([#19042](https://github.com/grafana/loki/issues/19042))
* **deps:** Update module github.com/twmb/franz-go to v1.19.5 (main) ([#18039](https://github.com/grafana/loki/issues/18039))
* **deps:** Update module github.com/twmb/franz-go/pkg/kadm to v1.16.1 (main) ([#18733](https://github.com/grafana/loki/issues/18733))
* **deps:** Update module github.com/twmb/franz-go/pkg/kmsg to v1.11.2 (main) ([#17258](https://github.com/grafana/loki/issues/17258)) ([7c47a3e](https://github.com/grafana/loki/commit/7c47a3e46d6d976b397c0e9f191e4d4f2a369e35))
* **deps:** Update module github.com/twmb/franz-go/plugin/kotel to v1.6.0 (main) ([#17194](https://github.com/grafana/loki/issues/17194)) ([9491fbb](https://github.com/grafana/loki/commit/9491fbb5b8399c5c4c3fdc4a46cae03b01c0ecea))
* **deps:** Update module github.com/twmb/franz-go/plugin/kprom to v1.3.0 (main) ([#18596](https://github.com/grafana/loki/issues/18596)) ([5405a83](https://github.com/grafana/loki/commit/5405a834973397a4b3871f9052a815a54fbca2c8))
* **deps:** Update module github.com/workiva/go-datastructures to v1.1.6 (main) ([#19287](https://github.com/grafana/loki/issues/19287))
* **deps:** Update module go.etcd.io/bbolt to v1.4.3 (main) ([#18973](https://github.com/grafana/loki/issues/18973))
* **deps:** Update module go.opentelemetry.io/collector/pdata to v1.43.0 (main) ([#19419](https://github.com/grafana/loki/issues/19419))
* **deps:** Update module go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc to v0.63.0 (main) ([#18455](https://github.com/grafana/loki/issues/18455))
* **deps:** Update module go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace to v0.63.0 (main) ([#19324](https://github.com/grafana/loki/issues/19324))
* **deps:** Update module go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp to v0.63.0 (main) ([#19327](https://github.com/grafana/loki/issues/19327))
* **deps:** Update module golang.org/x.crypto to v0.41.0 (main) ([#18767](https://github.com/grafana/loki/issues/18767))
* **deps:** Update module golang.org/x.net to v0.43.0 (main) ([#18769](https://github.com/grafana/loki/issues/18769))
* **deps:** Update module golang.org/x/crypto to v0.40.0 (main) ([#18406](https://github.com/grafana/loki/issues/18406)) ([8fa91c6](https://github.com/grafana/loki/commit/8fa91c6f6a39e10838b346b84c93ae307fd81a9f))
* **deps:** Update module golang.org/x/net to v0.45.0 (main) ([#19420](https://github.com/grafana/loki/issues/19420))
* **deps:** Update module golang.org/x/oauth2 to v0.31.0 (main) ([#19329](https://github.com/grafana/loki/issues/19329))
* **deps:** Update module golang.org/x/sync to v0.16.0 (main) ([#18383](https://github.com/grafana/loki/issues/18383)) ([6b1204a](https://github.com/grafana/loki/commit/6b1204a0de38389788434413a8777fe100d96622))
* **deps:** Update module golang.org/x/sys to v0.35.0 (main) ([#18759](https://github.com/grafana/loki/issues/18759)) ([9c748e0](https://github.com/grafana/loki/commit/9c748e0358b7ab495fc3d10334236b91d4db1e46))
* **deps:** Update module golang.org/x/text to v0.28.0 (main) ([#18762](https://github.com/grafana/loki/issues/18762)) ([e6fb85c](https://github.com/grafana/loki/commit/e6fb85cdfc6c44158544c0b0b766e9ddc86a9b41))
* **deps:** Update module golang.org/x/time to v0.13.0 (main) ([#19330](https://github.com/grafana/loki/issues/19330))
* **deps:** Update module google.golang.org/api to v0.252.0 (main) ([#19421](https://github.com/grafana/loki/issues/19421))
* **deps:** Update module google.golang.org/grpc to v1.75.1 (main) ([#19288](https://github.com/grafana/loki/issues/19288))
* **deps:** Update module google.golang.org/protobuf to v1.36.9 (main) ([#19289](https://github.com/grafana/loki/issues/19289))
* **deps:** Update module k8s.io/apimachinery to v0.33.4 (main) ([#18868](https://github.com/grafana/loki/issues/18868))
* **deps:** Update dependency lucide-react to ^0.544.0 (main) ([#19310](https://github.com/grafana/loki/issues/19310))
* **deps:** Update dependency react to v19.1.0 (main) ([#16949](https://github.com/grafana/loki/issues/16949)) ([404fd13](https://github.com/grafana/loki/commit/404fd13a9c53e5a5586938199b0f05bd42edbcea))
* **deps:** Update dependency react-datepicker to v8.7.0 (main) ([#18984](https://github.com/grafana/loki/issues/18984))
* **deps:** Update dependency react-dom to v19.1.0 (main) ([#16950](https://github.com/grafana/loki/issues/16950)) ([8d1dc9c](https://github.com/grafana/loki/commit/8d1dc9ce7fdcf45fa433bb3056dbfdd82f45a915))
* **deps:** Update dependency react-hook-form to v7.62.0 (main) ([#18704](https://github.com/grafana/loki/issues/18704)) ([4bc9162](https://github.com/grafana/loki/commit/4bc916274b4108a6fff662730fb0c3469d216f68))
* **deps:** Update dependency react-router-dom to v7.8.2 (main) ([#18971](https://github.com/grafana/loki/issues/18971))
* **deps:** Update dependency recharts to v2.15.4 (main) ([#18187](https://github.com/grafana/loki/issues/18187)) ([7593bf5](https://github.com/grafana/loki/commit/7593bf5129664e56a572256f7e5cc2771474e573))
* **deps:** Update dependency tailwind-merge to v3.3.1 (main) ([#18061](https://github.com/grafana/loki/issues/18061))
* **deps:** Update dependency zod to v3.25.76 (main) ([#18359](https://github.com/grafana/loki/issues/18359)) ([eb1e039](https://github.com/grafana/loki/commit/eb1e03962463ba8fe120fe44ce02f1f8b6598d91))
* **deps:** Update dependency zod to v4 (main) ([#18392](https://github.com/grafana/loki/issues/18392))
* **deps:** Update module zombiezen.com/go/sqlite to v1.4.2 (main) ([#17903](https://github.com/grafana/loki/issues/17903))
* **distributor:** Disable metadata topic writes ([#17437](https://github.com/grafana/loki/issues/17437)) ([46b2271](https://github.com/grafana/loki/commit/46b2271aacd26734979667da61d5c9d24a8efb2b))
* **distributor:** Revert remove colons from level detection ([#16999](https://github.com/grafana/loki/issues/16999)) ([e678b61](https://github.com/grafana/loki/commit/e678b615c18f52a23862d5d3ff39102989ec8374))
* **distributor:** Short circuit level detection when already detected ([#17028](https://github.com/grafana/loki/issues/17028)) ([58aa00a](https://github.com/grafana/loki/commit/58aa00a5f47bfd851fbd01d94f0523b82968f896))
* **distributor:** Skip streams over limits in dry-run mode ([#17114](https://github.com/grafana/loki/issues/17114)) ([2cde9b1](https://github.com/grafana/loki/commit/2cde9b12e9336531b54241437150d3cd83e7529a))
* **distributor:** Prevent panic when ContentLength is negative in Distributor RequestBodyTooLarge metrics ([#17054](https://github.com/grafana/loki/issues/17054)) ([9e9f534](https://github.com/grafana/loki/commit/9e9f534460527f25d28fa3f3e17cd1eb4148b39d))
* **distributor:** Detect "Information" log level as "info" ([#18195](https://github.com/grafana/loki/issues/18195)) ([82263d3](https://github.com/grafana/loki/commit/82263d383a0ac6efac50c681f00d26cb2848d367))
* **distributor:** Correctly register allowed log level fields ([#19261](https://github.com/grafana/loki/issues/19261))
* **distributor:** Extended detected level for debug and critical level ([#18370](https://github.com/grafana/loki/issues/18370))
* **docs:** Update configuration.md ([#17269](https://github.com/grafana/loki/issues/17269)) ([70de523](https://github.com/grafana/loki/commit/70de5235b7c0e5a549f4bbc7cd127956d595badb))
* **docs:** Added instructions for how to upgrade zone-aware ingesters ([#18658](https://github.com/grafana/loki/issues/18658)) ([aff2cb0](https://github.com/grafana/loki/commit/aff2cb0694cae99847b73da914ee4abd2432cdd0))
* **engine:** Don't attempt to release batch reference on read for merge node ([#18913](https://github.com/grafana/loki/issues/18913))
* **engine:** Fix bug in `SortMerge` which caused rows to be skipped due to incorrect sorting ([#18334](https://github.com/grafana/loki/issues/18334)) ([4fbe7c1](https://github.com/grafana/loki/commit/4fbe7c1a21a6443951bca3ad5e40712b24b4ef73))
* **engine:** Fix out-of-bounds panic in SortMerge implementation ([#17967](https://github.com/grafana/loki/issues/17967))
* **frontend:** Allow resolution of v6 addresses. ([#18251](https://github.com/grafana/loki/issues/18251)) ([8481b34](https://github.com/grafana/loki/commit/8481b34dd583d021e0dfa89d64298d2f36f75176))
* **helm:** Add flush=true to preStop hook ([#16063](https://github.com/grafana/loki/issues/16063)) ([a375751](https://github.com/grafana/loki/commit/a375751e4dc126a27cd8784c802e07871803868d))
* **helm:** Add init container configuration for backend, bloom builder, distributor, query-frontend, query-scheduler, read, write. ([#18709](https://github.com/grafana/loki/issues/18709))
* **helm:** Add livenessProbe to read pods in Simple Scalable mode ([#17063](https://github.com/grafana/loki/issues/17063))
* **helm:** Add missing log deletion endpoint to native ingress paths ([#14390](https://github.com/grafana/loki/issues/14390)) ([6db8b1f](https://github.com/grafana/loki/commit/6db8b1f60bdbd07b8d8e1628fd3ef6030e064e77))
* **helm:** Add release namespace metadata to HorizontalPodAutoscaling that lack it ([#18453](https://github.com/grafana/loki/issues/18453)) ([faae40a](https://github.com/grafana/loki/commit/faae40ab788d91dc4b7dbf110bbc1d3fcf4bf29c))
* **helm:** Add validation for loki.storage.bucketNames ([#13781](https://github.com/grafana/loki/issues/13781)) ([831c814](https://github.com/grafana/loki/commit/831c814f6c9356784a3aa2b248c7be150b241cbd))
* **helm:** Admin-api should only be deployed in microservice mode ([#13655](https://github.com/grafana/loki/issues/13655))
* **helm:** Allow loki to use hostPath volumes ([#17680](https://github.com/grafana/loki/issues/17680)) ([8e61a5b](https://github.com/grafana/loki/commit/8e61a5bd23a9d388f9111ea824aaf73aa37366ff))
* **helm:** Allow metrics networkpolicy only from namespace ([#17555](https://github.com/grafana/loki/issues/17555)) ([3a9b8e8](https://github.com/grafana/loki/commit/3a9b8e88a3595d27b52b79dab7c3bb6351b86b99))
* **helm:** Avoid double zonal ingester scrapping by not scrapping headless svc ([#19000](https://github.com/grafana/loki/issues/19000))
* **helm:** Create namespaced RBAC when sidecar is enabled ([#16776](https://github.com/grafana/loki/issues/16776)) ([7c9003e](https://github.com/grafana/loki/commit/7c9003eae54ab9b5497b3abe14fab7f754057b64))
* **helm:** Do not strip whitespace in target definitions for querier and read deployments ([#19464](https://github.com/grafana/loki/issues/19464))
* **helm:** Ensure global.extraEnv and global.extraEnvFrom applied to all resources consistently ([#16828](https://github.com/grafana/loki/issues/16828)) ([ee479d0](https://github.com/grafana/loki/commit/ee479d098000690f09372136d08ee9ba99b9a853))
* **helm:** Explicitly set registry for k8s-sidecar image ([#19233](https://github.com/grafana/loki/issues/19233))
* **helm:** Fix global extra* values ([#17020](https://github.com/grafana/loki/issues/17020))
* **helm:** Fix incorrect context references in Helm storage templates ([#18740](https://github.com/grafana/loki/issues/18740)) ([1f362c8](https://github.com/grafana/loki/commit/1f362c8182b95db2f8d4a3e84b624ee528c738d4))
* **helm:** Fix PDB settings for chunksCache and resultsCache ([#18321](https://github.com/grafana/loki/issues/18321)) ([e03b9b9](https://github.com/grafana/loki/commit/e03b9b9f9210201ce593f31eb90b12c4f9e2b1b8))
* **helm:** Fix setting X-Scope-OrgID header ([#18414](https://github.com/grafana/loki/issues/18414)) ([2f461c9](https://github.com/grafana/loki/commit/2f461c90e133fe139d75a2aa90ef4a534ddcae3a))
* **helm:** Fix storage_config when use_thanos_objstore is set to true ([#17024](https://github.com/grafana/loki/issues/17024)) ([81866d7](https://github.com/grafana/loki/commit/81866d790cb1f3140d5ddf7f9e50a0083c7accd8))
* **helm:** Fixed statement logic to enable annotations ([#17756](https://github.com/grafana/loki/issues/17756))
* **helm:** Gateway Ingester endpoints points to inexistent service when zone aware replication is enabled ([#17362](https://github.com/grafana/loki/issues/17362)) ([395a711](https://github.com/grafana/loki/commit/395a71117275c10385799c1226ca1fda1ec0c003))
* **helm:** Make loki.storage.bucketNames are optional, if builtin minio is enabled. ([#18653](https://github.com/grafana/loki/issues/18653)) ([409ccbd](https://github.com/grafana/loki/commit/409ccbdc4e686895e6795888ba6b56b7ae93f332))
* **helm:** Missing S3 field in lokiStorageConfig templated value ([#18791](https://github.com/grafana/loki/issues/18791))
* **helm:** Move loki-sc-rules to second position in containers ([#17937](https://github.com/grafana/loki/issues/17937)) ([6e72df3](https://github.com/grafana/loki/commit/6e72df395b8c5f3d6bb822d232ab2432be1c5767))
* **helm:** Only validate the ruler key exists under loki.storage.bucketNames when an object storage bucket is being used ([#18665](https://github.com/grafana/loki/issues/18665)) ([0604477](https://github.com/grafana/loki/commit/0604477af256110e43dd467974f1afade028f605))
* **helm:** Remove flaky enterprise test ([#19362](https://github.com/grafana/loki/issues/19362))
* **helm:** Removing deprecated admin_api_directory to avoid config crash ([#18731](https://github.com/grafana/loki/issues/18731)) ([8cce03c](https://github.com/grafana/loki/commit/8cce03c534255a5ca38878bbdaf65da490fd49d6))
* **helm:** Roll back change to Thanos ruler storage ([#18907](https://github.com/grafana/loki/issues/18907))
* **helm:** Un-deprecate several features in `monitoring` block ([#19012](https://github.com/grafana/loki/issues/19012))
* **helm:** Update querier, read, and single binary target definitions for UI enablement ([#19461](https://github.com/grafana/loki/issues/19461))
* **helm:** Use correct serviceName in zone-aware ingester statefulsets ([#18558](https://github.com/grafana/loki/issues/18558)) ([2706302](https://github.com/grafana/loki/commit/2706302caced15fa5cd2b32247e74f556850e032))
* **helm:** In helm chart fix indentation in nginx gateway config template handling ([#18167](https://github.com/grafana/loki/issues/18167)) ([4fe20a7](https://github.com/grafana/loki/commit/4fe20a7e506290a6b4f4c51571251f64dd9b5f56))
* **helm:** Loki chart fails to render proper YAML when add more than one extra manifest ([#12911](https://github.com/grafana/loki/issues/12911)) ([cf12656](https://github.com/grafana/loki/commit/cf12656a148cee6a605c19c45b8c27e4050f2ece))
* **helm:** Make helm use grpc for compactor address ([#17454](https://github.com/grafana/loki/issues/17454)) ([90003f6](https://github.com/grafana/loki/commit/90003f6d9b9c90249d360aa0648ed197aef1a2cd))
* **helm:** Use gateway container port as nginx server port ([#18774](https://github.com/grafana/loki/issues/18774))
* **helm:** Use strings in stead of integers for ports in CiliumNetworkPolicies ([#19252](https://github.com/grafana/loki/issues/19252))
* **helm:** Websocket related proxy_set_header to locations back to resolve high CPU usage. ([#18800](https://github.com/grafana/loki/issues/18800))
* **helm:** Use UDP/53 for DNS egress instead of named port ([#19073](https://github.com/grafana/loki/issues/19073))
* **helm:** Add single-binary component to ingress NetworkPolicy ([#19229](https://github.com/grafana/loki/issues/19229))
* **helm:** Canary only worked when gateway enabled ([#16758](https://github.com/grafana/loki/issues/16758))
* **helm:** Enable ui in helm deployments ([#17562](https://github.com/grafana/loki/issues/17562))
* **helm:** Update loki-helm-test image tag to latest commit ([#19227](https://github.com/grafana/loki/issues/19227))
* **ingester:** Fix missing series in monolithic mode ([#17067](https://github.com/grafana/loki/issues/17067)) ([5625464](https://github.com/grafana/loki/commit/5625464332fc0b3371b744d60d36acaf2600665a))
* **json parser:** Fix possible JSON log line corruption caused by `json` parser on query path ([#18056](https://github.com/grafana/loki/issues/18056))
* **json, structured metadata:** Correctly handle escaping in json parsing API ([#17068](https://github.com/grafana/loki/issues/17068))
* **json, structured metadata:** JSON-parsing cleanup ([#17072](https://github.com/grafana/loki/issues/17072)) ([2aed4c3](https://github.com/grafana/loki/commit/2aed4c3da5e97ea3c40091961395568f693bf1cf))
* **kafka:** Remove duplicated metric from Kafka producer ([#18614](https://github.com/grafana/loki/issues/18614)) ([a67a460](https://github.com/grafana/loki/commit/a67a460e9d7db43a793b180f2e73584fbe2497bb))
* **kafka:** Disable defaults address defaults ([#17825](https://github.com/grafana/loki/issues/17825))
* **kafka:** Use cooperative active sticky load balancer ([#19160](https://github.com/grafana/loki/issues/19160))
* **limits:** Adapt defaults and expose evict interval ([#17808](https://github.com/grafana/loki/issues/17808))
* **limits:** Fix ingest-limits eviction metric ([#17779](https://github.com/grafana/loki/issues/17779))
* **limits:** Read the consumer group and topic from the ingest-limits config ([#17831](https://github.com/grafana/loki/issues/17831))
* **limits:** Set ready when all partitions ready ([#18092](https://github.com/grafana/loki/issues/18092)) ([a07bee5](https://github.com/grafana/loki/commit/a07bee5dc2aae2c0618aba8d049630597b21b611))
* **limits:** Counter variables should not have total at the end (([#17982](https://github.com/grafana/loki/issues/17982))
* **logql:** Fix inconsistency with parsed field short circuiting ([#17104](https://github.com/grafana/loki/issues/17104)) ([88beefb](https://github.com/grafana/loki/commit/88beefb02acca7651d57e69dd5118598d829a904))
* **logql:** While validating logql expression, detect and validate expression in label_replace ([#18470](https://github.com/grafana/loki/issues/18470)) ([d379de5](https://github.com/grafana/loki/commit/d379de501f93be62cb0f72948794276e817d2599))
* **logql:** Implement approx-topk function on querier ([#17816](https://github.com/grafana/loki/issues/17816))
* **logql:** Reduce allocations for JSON and logfmt parser ([#18637](https://github.com/grafana/loki/issues/18637))
* **loki:** Return defaults for non-existent tenants on applied limits endpoint ([#17942](https://github.com/grafana/loki/issues/17942))
* **memberlist:** Allow resolution of advertise address from v6 interface ([#18250](https://github.com/grafana/loki/issues/18250)) ([8594d1c](https://github.com/grafana/loki/commit/8594d1ca282a9378091c122a2aa31d4b3af3f817))
* **mixin:** Fix typo in Loki Reads dashboard TSDB row ([#18872](https://github.com/grafana/loki/issues/18872))
* **mixins:** Fix label generation for Loki logs dashboard ([#17412](https://github.com/grafana/loki/issues/17412)) ([da2b620](https://github.com/grafana/loki/commit/da2b620964e2b452598d2817107433e2e0092cc3))
* **objstore:** Ruler not starting and incorrect mkdir path when Thanos Store is enabled ([#16555](https://github.com/grafana/loki/issues/16555)) ([3da9f2c](https://github.com/grafana/loki/commit/3da9f2cae6cb6c01e185ef9a596cae8e3f782873))
* **oltp:** Apply global otlp config to tenant config only when it is updated in the overrides ([#19213](https://github.com/grafana/loki/issues/19213))
* **operator:** Fix type of maximum OpenShift version property ([#18066](https://github.com/grafana/loki/issues/18066)) ([f7c7dfa](https://github.com/grafana/loki/commit/f7c7dfa910d03e2ddc9c95c8d22fb3bff5c78e6b))
* **operator:** Fix typo in docs regarding forcepathstyle ([#17725](https://github.com/grafana/loki/issues/17725))
* **operator:** Update maximum OpenShift version ([#17954](https://github.com/grafana/loki/issues/17954))
* **operator:** Update memberlist when ingester becomes unhealthy ([#17026](https://github.com/grafana/loki/issues/17026)) ([c63f0a9](https://github.com/grafana/loki/commit/c63f0a9d8d93398fc47d1992ac6370fe57c8e58f))
* **operator:** Update webhook validator for alerts/rules ([#17824](https://github.com/grafana/loki/issues/17824))
* **operator:** Updated AlertingRule sample to make it comply with the validation we apply ([#18671](https://github.com/grafana/loki/issues/18671))
* **operator:** Upgrade OPA policy syntax for v1+ ([#18795](https://github.com/grafana/loki/issues/18795))
* **otlp:** Calculate entry metadata size before adding resource/scope attributes ([#17629](https://github.com/grafana/loki/issues/17629)) ([2161c8c](https://github.com/grafana/loki/commit/2161c8c52c36ac2d6657dbc670472be2022ca90f))
* **patterns:** Pattern persistence feature flag ([#18285](https://github.com/grafana/loki/issues/18285)) ([a206324](https://github.com/grafana/loki/commit/a206324ae3640d5f7615c2115fb3272256e95c6d))
* **patterns:** Limit volume and frequency of persisted patterns ([#18362](https://github.com/grafana/loki/issues/18362)) ([c690827](https://github.com/grafana/loki/commit/c690827c6a655ada93806237d81643025ab53494))
* **patterns:** Fix feature flag for enabling pattern persistence ([#18216](https://github.com/grafana/loki/issues/18216)) ([c167800](https://github.com/grafana/loki/commit/c167800b3f6e7edd7b3f06a9105245c98b7391dd))
* **push:** Add guard clauses to prevent negative counter values ([#17056](https://github.com/grafana/loki/issues/17056)) ([9000de1](https://github.com/grafana/loki/commit/9000de1aa7a6508234ee58adb346549ac3df2648))
* **push:** Fix guard clauses to prevent error spam in logs ([#17372](https://github.com/grafana/loki/issues/17372)) ([d70b20e](https://github.com/grafana/loki/commit/d70b20e37ffb788297bb42933aabc3fa5723f78c))
* **push:** Fix push stats calculation ([#19319](https://github.com/grafana/loki/issues/19319))
* **querier:** Fixes panic when filtering agg metrics from nil resp ([#17662](https://github.com/grafana/loki/issues/17662))
* **querier:** Allow boolean numeric values in detected labels ([#16997](https://github.com/grafana/loki/issues/16997)) ([bfb935d](https://github.com/grafana/loki/commit/bfb935d893b6c3dec8d5fee539478c64d3012dd3))
* **querier:** Fix integer overrun when calculating parallelism for very long time range queries ([#17428](https://github.com/grafana/loki/issues/17428)) ([82acbd5](https://github.com/grafana/loki/commit/82acbd58b22481df3999f34edcace200940ce79b))
* **querier:** Better fix integer overrun when calculating parallelism for very long time range queries ([#17430](https://github.com/grafana/loki/issues/17430)) ([d660af3](https://github.com/grafana/loki/commit/d660af37fbc7dc9c7426999d14555c9bc688c565))
* **querier:** Revert "perf: Fix memory leak in cachedIterator ([#17628](https://github.com/grafana/loki/issues/17628))" ([#18687](https://github.com/grafana/loki/issues/18687)) ([0316740](https://github.com/grafana/loki/commit/03167408063088542d251a136d314471f09f33e9))
* **ruler:** Correct log level verbosity in rule evaluation ([#19519](https://github.com/grafana/loki/issues/19519))
* **ruler:** Return StatusBadRequest on multiple org IDs ([#17850](https://github.com/grafana/loki/issues/17850))
* **storage:** Fix nil pointer dereference in Volume method of the composite store ([#18064](https://github.com/grafana/loki/issues/18064)) ([fc7c018](https://github.com/grafana/loki/commit/fc7c0189c063d59d8933159faa9ab8f3e9df7f06))
* **storage:** Use default config when building S3 client (backport k277) ([#19559](https://github.com/grafana/loki/issues/19559))
* **stream-generator:** Split create/keep-alive streams routines ([#17815](https://github.com/grafana/loki/issues/17815))
* **structured metadata:** Improve structured metadata label normalization performance ([#17332](https://github.com/grafana/loki/issues/17332)) ([87922da](https://github.com/grafana/loki/commit/87922da28d332115f41d514f98c47fafb26fc7c6))
* **structured metadata:** Unescape JSON structured metadata string values ([#13919](https://github.com/grafana/loki/issues/13919)) ([9629d07](https://github.com/grafana/loki/commit/9629d07c60a25b27aeddb81af3ea6ceb9031fd52))
* **structured metadata:** Ensure we do not Add duplicate structured metadata from stream labels and extracted map ([#18523](https://github.com/grafana/loki/issues/18523)) ([db72d63](https://github.com/grafana/loki/commit/db72d63211c2cc5208d207b5795d5109b74c16ca))
* **tests:** Update kfake to fix tests and revert go-redis ([#18958](https://github.com/grafana/loki/issues/18958))
* **tools:** Correct typo in appenPath function name ([#17917](https://github.com/grafana/loki/issues/17917))
* **tsdb:** Avoid copying label values from tsdb unless required ([#17077](https://github.com/grafana/loki/issues/17077)) ([8e4b104](https://github.com/grafana/loki/commit/8e4b10408dacf7891d48b70eba451a95bcf72853))
* **ui:** Fix UI build ([#17717](https://github.com/grafana/loki/issues/17717)) ([e01a2a9](https://github.com/grafana/loki/commit/e01a2a908a83006e2ecd71c1dffbf0982ec89d00))
* **ui:** Downgrade UI's @swc/core dep back to v1.13.5 ([#19345](https://github.com/grafana/loki/issues/19345))
* **WAL:** Handle WAL corruption properly on startup ([#18175](https://github.com/grafana/loki/issues/18175)) ([1954f67](https://github.com/grafana/loki/commit/1954f67a50ca0dc40e77c49e2c6cc74014e5998a))

# v3.7

Grafana Labs and the Loki team are excited to announce the release of Loki 3.7. Here's a summary of new enhancements and important fixes.

For a full list of all changes and fixes, refer to the [CHANGELOG](https://github.com/grafana/loki/blob/release-3.7.x/CHANGELOG.md)

{{< admonition type="note" >}}
See the [deprecations](https://grafana.com/docs/loki/latest/release-notes/v3-7/#deprecations) section for important information about the Loki Helm charts.
{{< /admonition >}}

## Features and enhancements

Key features in Loki 3.7.0 include the following:

* **Helm charts:** - Effective March 16, 2026, the Grafana Loki Helm chart has been be forked to the new repository [grafana-community/helm-charts](https://github.com/grafana-community/helm-charts). The motivation behind this change is to accelerate development and enable direct community maintenance, as outlined in [grafana/helm-charts#3955](https://github.com/grafana/helm-charts/issues/3955). The Grafana Community now owns maintenance of the Loki Helm Chart, while Grafana Labs continues to maintain the Helm Chart for Grafana Enterprise Logs (GEL). The Loki Helm Charts documentation has been updated to point to the new [Grafana-Community repo](https://github.com/grafana/loki/pull/21330).

* **Docs:** The documentation now includes a new, comprehensive [Troubleshooting](https://grafana.com/docs/loki/latest/operations/troubleshooting/) section, documenting the Loki and Logs Drilldown error messages.

{{< admonition type="note" >}}
Note that when appropriate, documentation updates have been backported to previous releases.
{{< /admonition >}}

Other improvements include the following:

* **canary:** Support passing arbitrary set of labels to use for the query ([#17008](https://github.com/grafana/loki/issues/17008)) ([993b3ae](https://github.com/grafana/loki/commit/993b3ae65dad2607d0d20459aac16de0ba2c1d5f))
* **compactor:** Add support for storing chunk deletion markers in object storage instead of local disk ([#19689](https://github.com/grafana/loki/issues/19689)) ([856c11d](https://github.com/grafana/loki/commit/856c11dad209b05694626982230bbd7fa336e299))
* **distributor:** Add gauge to track in-flight bytes ([#20091](https://github.com/grafana/loki/issues/20091)) ([23ef8ec](https://github.com/grafana/loki/commit/23ef8eccce9b727509894b0056c65bd8083eea2e))
* **distributor:** Add resolved policy to blocked and enforced label error ([#19826](https://github.com/grafana/loki/issues/19826)) ([48d13d1](https://github.com/grafana/loki/commit/48d13d15db864b524635823b432bbd45714714cd))
* **distributor:** Add segmentation keys and resolver ([#19927](https://github.com/grafana/loki/issues/19927)) ([c853f2c](https://github.com/grafana/loki/commit/c853f2ced09b47f859a200718364df98def6e4c4))
* **distributor:** Add support for UpdateRates RPC to distributors ([#19918](https://github.com/grafana/loki/issues/19918)) ([9018886](https://github.com/grafana/loki/commit/90188869a145bd640446b6ae7c123d03fc7f7a2a))
* **distributor:** Randomly distribute requests to the ingest-limits frontend ([#19840](https://github.com/grafana/loki/issues/19840)) ([1605a38](https://github.com/grafana/loki/commit/1605a387b765478fcb850e1e5a64dd5b924b751b))
* **distributor:** Shuffle shard on tenant rate limit ([#19990](https://github.com/grafana/loki/issues/19990)) ([3904c2b](https://github.com/grafana/loki/commit/3904c2b8f77252f6204dbec9ef4d995ad7b1ef68))
* **distributor:** Write to dataobj partitions based on segmentation key ([#19946](https://github.com/grafana/loki/issues/19946)) ([3a24f5d](https://github.com/grafana/loki/commit/3a24f5d291c8398cd43346d8169e8a0646d8bf4f))
* **helm:** `nameOverride` now passed through helm tpl function. ([#19590](https://github.com/grafana/loki/issues/19590)) ([7f56fd2](https://github.com/grafana/loki/commit/7f56fd23df5f3fb02d3a40b6c421536e526cb221))
* **helm:** Add ability to toggle grpclb port for query frontend service ([#19609](https://github.com/grafana/loki/issues/19609)) ([9c4f022](https://github.com/grafana/loki/commit/9c4f0222b778d013ba20262cffcb93825bb3e6db))
* **helm:** Add startupProbe to distributor ([#20073](https://github.com/grafana/loki/issues/20073)) ([5b76589](https://github.com/grafana/loki/commit/5b76589e100e704f1f29f062c78ab52b619f9ede))
* **helm:** Allow configuration of service trafficDistribution parameter ([#19558](https://github.com/grafana/loki/issues/19558)) ([55f95e3](https://github.com/grafana/loki/commit/55f95e3d1d425b495f0bbd0012665d3c60e4142a))
* **helm:** Allow set topologySpreadConstraints on singleBinary ([#19534](https://github.com/grafana/loki/issues/19534)) ([265601f](https://github.com/grafana/loki/commit/265601f8310f5327aab14702437bc8cc3ebda04c))
* **helm:** Make loki-canary readinessProbe configurable via values.yaml ([#19328](https://github.com/grafana/loki/issues/19328)) ([7231766](https://github.com/grafana/loki/commit/723176669ad3c0318adeeffa821bfd1ae36f88af))
* **helm:** Use fsGroupChangePolicy=OnRootMismatch to speed up pod starts ([#13942](https://github.com/grafana/loki/issues/13942)) ([c7cec3a](https://github.com/grafana/loki/commit/c7cec3aa81924b58ddd755ce2f48e16252e80bfa))
* **index-gateway:** Client side index gateway shuffle sharding ([#20124](https://github.com/grafana/loki/issues/20124)) ([326c7d1](https://github.com/grafana/loki/commit/326c7d1ea0c5309568570d1128cc0e93d9479679))
* **ingest-limits:** Add UpdateRates RPC, update rates from the frontend, return no-op in the service ([#19894](https://github.com/grafana/loki/issues/19894)) ([e173cf4](https://github.com/grafana/loki/commit/e173cf4fb1fc54069b23e9959af6fc148a81b859))
* **ingest-limits:** Check partition state in parallel ([#19884](https://github.com/grafana/loki/issues/19884)) ([b8536aa](https://github.com/grafana/loki/commit/b8536aaa666271278a31362b96d34fcb1692d060))
* **ingester:** Handle state change lock in prepare downscale ([#20141](https://github.com/grafana/loki/issues/20141)) ([de092da](https://github.com/grafana/loki/commit/de092daf0ae6cc7656b0242348346aa1582f6656))
* **logcli:** Allow custom headers to be passed ([#20231](https://github.com/grafana/loki/issues/20231)) ([c524203](https://github.com/grafana/loki/commit/c524203a6e37cf30f02f0c8eee0c9eef65e31c6b))
* **loki:** Add loki health command ([#20313](https://github.com/grafana/loki/issues/20313)) ([ef69cfd](https://github.com/grafana/loki/commit/ef69cfd97cf1d5bd58758952cbc3eb48a99d060e))
* **lokitool:** Add regex namespace filtering ([#20209](https://github.com/grafana/loki/issues/20209)) ([0c1561d](https://github.com/grafana/loki/commit/0c1561d15e9c9075cfb935efb0fc786664e68583))
* **operator:** Add option to disable ingress ([#19382](https://github.com/grafana/loki/issues/19382)) ([9dc71a6](https://github.com/grafana/loki/commit/9dc71a642569b64b6c21b23f23671b0f18f6285d))
* **querier:** Ability to send query context for limit enforcement ([#19900](https://github.com/grafana/loki/issues/19900)) ([1a66d2d](https://github.com/grafana/loki/commit/1a66d2ddab11e7d0219040477ed2c0b95f87bfdb))

Key features in Loki 3.7.3 include the following:

* **logql:** Add case-insensitive regex optimization ([#20578](https://github.com/grafana/loki/issues/20578)) ([f6a6a4f](https://github.com/grafana/loki/commit/f6a6a4f))
* **loki:** Allow disabling cache for `query_range` requests ([#21112](https://github.com/grafana/loki/issues/21112)) ([1242aa5](https://github.com/grafana/loki/commit/1242aa5))
* **operator:** Add option to customize gateway server cert ([#20325](https://github.com/grafana/loki/issues/20325)) ([3c8b9e8](https://github.com/grafana/loki/commit/3c8b9e8))
* **operator:** Update metrics authentication to remove dependency on kube-rbac-proxy ([#20853](https://github.com/grafana/loki/issues/20853)) ([2de6e79](https://github.com/grafana/loki/commit/2de6e79))

Key features in Loki 3.7.7 include the following:

* **compactor:** Add flag to ignore missing chunks during deletion of logs with line filter ([#24233](https://github.com/grafana/loki/issues/24233)) ([7a40404](https://github.com/grafana/loki/commit/7a40404f32b3e6464c9cfc6cc7dd75a40f3931da)).


## Deprecations

One of the focuses of Loki 3.0 was cleaning up unused code and old features that had been previously deprecated but not removed. Loki 3.0 removed a number of previous deprecations and introduces some new deprecations. Some of the main areas with changes include:

- [Promtail](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/promtail/) was deprecated in Loki 3.0 and has been removed as of Loki 3.7.3 ([#21248](https://github.com/grafana/loki/issues/21248)). The code has been merged into Grafana Alloy. You can find migration documentation and a utility to convert your Promtail configuration to Alloy configuration in the [Alloy documentation](https://grafana.com/docs/alloy/latest/set-up/migrate/from-promtail/). Note that this removal does NOT include the lambda-promtail client.

- [Deprecated storage options](https://grafana.com/docs/loki/<LOKI_VERSION>/storage/) including the deprecation of the BoltDB store.

- [Deprecated configuration options](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/).

- [API endpoint deprecations](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/api/#deprecated-endpoints).

To learn more about breaking changes in this release, refer to the [Upgrade guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

## Upgrade Considerations

For important upgrade guidance, refer to the [Upgrade Guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/).

- **BREAKING CHANGE - engine:** Make scheduler aware of total compute capacity ([#19876](https://github.com/grafana/loki/issues/19876))
- **BREAKING CHANGE - engine:** Share worker threads across all scheduler connections ([#20229](https://github.com/grafana/loki/issues/20229))
- **BREAKING CHANGE - labels:** parsed labels should not override structured metadata ([#19991](https://github.com/grafana/loki/issues/19991))
- **BREAKING CHANGE - operator:** Switch default for OpenShift stream labels ([#21001](https://github.com/grafana/loki/issues/21001))

## Bug fixes

### 3.7.9 (2026-10-20)

* **deps:** Update github.com/thanos-io/objstore digest to 72d3b9f (release-3.7.x) ([#25389](https://github.com/grafana/loki/issues/25389)) ([e7d6809](https://github.com/grafana/loki/commit/e7d68093e0be6cf96fdc81c801057beaca4f212d)).
* **deps:** Update go4.org/netipx digest to 3075585 (release-3.7.x) ([#25390](https://github.com/grafana/loki/issues/25390)) ([24be2f4](https://github.com/grafana/loki/commit/24be2f4dbe6ba67269c5518f3e3f15979d6d1b8a)).
* **deps:** Update k8s.io/utils digest to cf1189d (release-3.7.x) ([#25391](https://github.com/grafana/loki/issues/25391)) ([a5218df](https://github.com/grafana/loki/commit/a5218dffae9ac78224b691daecd67690f4784dd4)).
* **ingester:** Concurrent WAL `Append()` calls could lead to inconsistent record order and irrecoverable WAL state ([#25125](https://github.com/grafana/loki/issues/25125)) ([72aa93e](https://github.com/grafana/loki/commit/72aa93ed048c289c924ebdef7236ed04df3b6e19)).
* **logql:** Keep entries while stream accumulator has free room ([#24703](https://github.com/grafana/loki/issues/24703)) ([4390fb1](https://github.com/grafana/loki/commit/4390fb1166fd829a0bb24bca1146bfe8466b0edd)).
* **ruler:** Limit request body size for ruler "CreateRuleGroup" API and LogQLAnalyzer API ([#24838](https://github.com/grafana/loki/issues/24838)) ([6c6e9dc](https://github.com/grafana/loki/commit/6c6e9dcafb0bfafc988a4a2f5a0b006e4d2f99aa)).
* **security/LOW/:** Update module go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc to v1.45.0 ([#24614](https://github.com/grafana/loki/issues/24614)) ([bab6de2](https://github.com/grafana/loki/commit/bab6de213675f0105c7efd3d772d1b4c00da5d21)).
* **security/LOW/:** Update module go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp to v1.45.0 ([#24615](https://github.com/grafana/loki/issues/24615)) ([7aee5f1](https://github.com/grafana/loki/commit/7aee5f1980b85848e94df163d644381eec29bcd3)).
* **security/LOW/:** Update module go.opentelemetry.io/otel/sdk to v1.45.0 ([#24616](https://github.com/grafana/loki/issues/24616)) ([b787d9a](https://github.com/grafana/loki/commit/b787d9a4bf438fdf873f07ed87f8f47b910fc0ae)).
* **security/MEDIUM/:** Update module github.com/containerd/containerd/v2 to v2.2.9 ([#24806](https://github.com/grafana/loki/issues/24806)) ([bafb1b8](https://github.com/grafana/loki/commit/bafb1b85014e40854424cb14e7dc7dbdae6f03d7)).
* **security:** Bump Go to 1.26.10 ([#25458](https://github.com/grafana/loki/issues/25458)) ([d3b4a2f](https://github.com/grafana/loki/commit/d3b4a2f76bfc5625abee7ceb00b8896fa3c31549)).

### 3.7.8 (2026-08-27)

* **security/HIGH/pkg/push:** Update module google.golang.org/grpc to v1.83.2 ([#24433](https://github.com/grafana/loki/issues/24433)) ([4d5dc2b](https://github.com/grafana/loki/commit/4d5dc2b69863c155d29c2125f66f31f09166035a)).
* **security/HIGH/:** Update module google.golang.org/grpc to v1.83.1 ([#24328](https://github.com/grafana/loki/issues/24328)) ([0a01797](https://github.com/grafana/loki/commit/0a01797da781f4d715d48028ff45a63fc273cd6b)).
* **security/HIGH/:** Update module google.golang.org/grpc to v1.83.2 ([#24429](https://github.com/grafana/loki/issues/24429)) ([14b5da8](https://github.com/grafana/loki/commit/14b5da8339b001e765cb60c0cc15eaf44fb69fbd)).
* **security/MEDIUM/:** Update module github.com/containerd/containerd/v2 to v2.2.8 ([#24460](https://github.com/grafana/loki/issues/24460)) ([3e917f2](https://github.com/grafana/loki/commit/3e917f2086d97438017c3acae392a58799aa4198)).
* **security/UNKNOWN/pkg/push:** Update module golang.org/x/net to v0.56.0 ([#23422](https://github.com/grafana/loki/issues/23422)) ([41064da](https://github.com/grafana/loki/commit/41064da6e4c696b548d7a1dbb4e279d241c744f5)).
* **security/UNKNOWN/pkg/push:** Update module google.golang.org/grpc to v1.83.1 ([#23416](https://github.com/grafana/loki/issues/23416)) ([cd8e76d](https://github.com/grafana/loki/commit/cd8e76da123e93756448a2f8d56f94a9a86d7346)).
* **security/UNKNOWN/:** Update module golang.org/x/crypto to v0.56.0 ([#24335](https://github.com/grafana/loki/issues/24335)) ([47149cb](https://github.com/grafana/loki/commit/47149cbb5c76384ee1599e2df82ad3e0cba5a824)).

### 3.7.7 (2026-08-27)

* **security/UNKNOWN/:** Update module github.com/containerd/containerd/v2 to v2.2.5 ([#24097](https://github.com/grafana/loki/issues/24097)) ([07cd1f0](https://github.com/grafana/loki/commit/07cd1f0a43d5f742525249aa21f0e47b9d51c4ff)).
* **security/UNKNOWN/:** Update module go.etcd.io/etcd/client/pkg/v3 to v3.6.14 ([#24061](https://github.com/grafana/loki/issues/24061)) ([9d1c9ee](https://github.com/grafana/loki/commit/9d1c9ee0164c020f8125046a7a9d67986c59320b)).
* **security/UNKNOWN/:** Update module golang.org/x/mod to v0.40.0 ([#23962](https://github.com/grafana/loki/issues/23962)) ([1d5e027](https://github.com/grafana/loki/commit/1d5e027b64370c19abf1495928fd0b89363680d7)).
* **storage:** Pre-compute SHA-256 to avoid aws-chunked on PutObject ([#24215](https://github.com/grafana/loki/issues/24215)) ([3d6f245](https://github.com/grafana/loki/commit/3d6f245b74c52841e8e2ccc6e7883962bfdec7c0)).

### 3.7.6 (2026-08-05)

* **queryrange:** Preserve sketch in MergeLabels ([#23770](https://github.com/grafana/loki/issues/23770)) ([d48329d](https://github.com/grafana/loki/commit/d48329d090320f668cd8bd7ecd636d3dc0e664d2))

### 3.7.5 (2026-08-05)

* **ci:** Build deb/rpm packages again in the release workflow ([#23598](https://github.com/grafana/loki/issues/23598)) ([6f49272](https://github.com/grafana/loki/commit/6f4927210024a9e5496fbc760837bf2fd7dbf5f1))
* **ci:** Disable docker plugin publishing on release-3.7.x ([#23440](https://github.com/grafana/loki/issues/23440)) ([6095f71](https://github.com/grafana/loki/commit/6095f71eeba604627fbc08a4e87ed315c38d7b07))
* **ingester:** Fix flush race in ingester ([#23682](https://github.com/grafana/loki/issues/23682)) ([ff152dc](https://github.com/grafana/loki/commit/ff152dc114a5adad5fbc797e3c8af43b97c0b920))
* **security/UNKNOWN/:** Update module github.com/klauspost/compress to v1.18.7 (release-3.7.x) ([#23610](https://github.com/grafana/loki/issues/23610)) ([941b557](https://github.com/grafana/loki/commit/941b5575c1b0e26626db924258ea3bbd8ccd47f6))
* **security/UNKNOWN/:** Update module golang.org/x/text to v0.39.0 (release-3.7.x) ([#23418](https://github.com/grafana/loki/issues/23418)) ([2347a9a](https://github.com/grafana/loki/commit/2347a9a50f8272f849af07f88d6269f9e8af9988))
* **security/UNKNOWN/:** Update module google.golang.org/grpc to v1.82.1 (release-3.7.x) ([#23405](https://github.com/grafana/loki/issues/23405)) ([f290af0](https://github.com/grafana/loki/commit/f290af0357a9162ab9bb8209ac4dd2426e17a36a))
* **security/UNKNOWN/cmd/chunks-inspect:** Update module github.com/klauspost/compress to v1.18.7 (release-3.7.x) ([#23611](https://github.com/grafana/loki/issues/23611)) ([11c4b29](https://github.com/grafana/loki/commit/11c4b29fef7483c40649f65bccf01e1cca2b42bd))
* **security/UNKNOWN/cmd/dataobj-inspect:** Update module github.com/klauspost/compress to v1.18.7 (release-3.7.x) ([#23612](https://github.com/grafana/loki/issues/23612)) ([1e3108e](https://github.com/grafana/loki/commit/1e3108ecf15de374fb292f81a775b6812ffc46c3))
* **security/UNKNOWN/cmd/dataobj-inspect:** Update module go.opentelemetry.io/otel to v1.42.0 (release-3.7.x) ([#23518](https://github.com/grafana/loki/issues/23518)) ([28917d3](https://github.com/grafana/loki/commit/28917d3828aae7f3150c5c1a8965467521c6a70f))
* **security/UNKNOWN/cmd/dataobj-inspect:** Update module golang.org/x/text to v0.39.0 (release-3.7.x) ([#23419](https://github.com/grafana/loki/issues/23419)) ([5d2b9d4](https://github.com/grafana/loki/commit/5d2b9d4faa9b291101ee446156829932eae685c0))
* **security/UNKNOWN/operator:** Update module github.com/klauspost/compress to v1.18.7 (release-3.7.x) ([#23613](https://github.com/grafana/loki/issues/23613)) ([8d71c59](https://github.com/grafana/loki/commit/8d71c59de908c1868cbffa2ceb5d26b7a8cb7f51))

### 3.7.4 (2026-07-22)

* **ci:** Fix zizmor findings for operator-images in Loki 3.7 ([#22820](https://github.com/grafana/loki/issues/22820)) ([abc1186](https://github.com/grafana/loki/commit/abc11864e661555bfee967067691e27fe8ec43ec))
* **ci:** Helm CI warning fix ([#22605](https://github.com/grafana/loki/issues/22605)) ([7389428](https://github.com/grafana/loki/commit/7389428ea0952bfa1b53b1ca9dc0ac88d6437b14))
* **ci:** Re-enable docker plugin publishing ([#22818](https://github.com/grafana/loki/issues/22818)) ([28f44a8](https://github.com/grafana/loki/commit/28f44a829588de96c3af0f48644a6d9d3643efa6))
* **compactor:** Fix delete request when using Thanos objstore client with filesystem backend ([#22811](https://github.com/grafana/loki/issues/22811)) ([dd6fdb5](https://github.com/grafana/loki/commit/dd6fdb500a47f6a5b9015e06d2aac52eee384503))
* **deps:** Update module charm.land/bubbletea/v2 to v2.0.7 (release-3.7.x) ([#22768](https://github.com/grafana/loki/issues/22768)) ([e11f935](https://github.com/grafana/loki/commit/e11f935ec029919d680c45309c1aa703cc7b2448))
* **deps:** Update module charm.land/lipgloss/v2 to v2.0.4 (release-3.7.x) ([#22769](https://github.com/grafana/loki/issues/22769)) ([10213c2](https://github.com/grafana/loki/commit/10213c2a6651561b28409587cc49b872c1f65e59))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/credentials to v1.19.24 (release-3.7.x) ([#22776](https://github.com/grafana/loki/issues/22776)) ([3daf198](https://github.com/grafana/loki/commit/3daf19818e715fb671c0fb54b2e0a6f54346db67))
* **deps:** Update module github.com/aws/smithy-go to v1.27.3 (release-3.7.x) ([#22795](https://github.com/grafana/loki/issues/22795)) ([1f6a191](https://github.com/grafana/loki/commit/1f6a19144b5109356dbe73122f9f9f48afe1177e))
* **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.270 (release-3.7.x) ([#22783](https://github.com/grafana/loki/issues/22783)) ([103ccd8](https://github.com/grafana/loki/commit/103ccd87924cbf0946947cdc213e04bd66637f78))
* **deps:** Update module github.com/coder/quartz to v0.3.1 (release-3.7.x) ([#22784](https://github.com/grafana/loki/issues/22784)) ([3509e31](https://github.com/grafana/loki/commit/3509e3166ef9b52c38ce6b7b10d098fe6ecaa771))
* **deps:** Update module github.com/IBM/ibm-cos-sdk-go to v1.14.1 (release-3.7.x) ([#22785](https://github.com/grafana/loki/issues/22785)) ([d0abe72](https://github.com/grafana/loki/commit/d0abe727a5b77e6d8afa7087c6b93f92e723c363))
* **deps:** Update module github.com/klauspost/compress to v1.18.6 (release-3.7.x) ([#22797](https://github.com/grafana/loki/issues/22797)) ([327da7c](https://github.com/grafana/loki/commit/327da7c077a234934c727b5405a4cb27f275374d))
* **deps:** Update module github.com/pierrec/lz4/v4 to v4.1.27 (release-3.7.x) ([#22798](https://github.com/grafana/loki/issues/22798)) ([6b3297f](https://github.com/grafana/loki/commit/6b3297fd78aa991dd220fb8e47fb2b39a6946feb))
* **deps:** Update module github.com/shirou/gopsutil/v4 to v4.26.5 (release-3.7.x) ([#22800](https://github.com/grafana/loki/issues/22800)) ([cb7f17d](https://github.com/grafana/loki/commit/cb7f17dcbc41c01ce7dc7ad4919a45339961ae91))
* **security/CRITICAL/clients/cmd/fluentd/docker:** Update dependency fluentd to v1.19.3 (release-3.7.x) ([#22693](https://github.com/grafana/loki/issues/22693)) ([6d8f1b5](https://github.com/grafana/loki/commit/6d8f1b57bbb5cc6dcb9173ad959f5e068691a8d2))
* **security/HIGH/:** Bump Go to 1.26.5 to address CVE-2026-39822 and CVE-2026-42505 ([#23393](https://github.com/grafana/loki/issues/23393)) ([b318f28](https://github.com/grafana/loki/commit/b318f2829f0ae2094ab3a1e90780450e9e4b03be))
* **security/HIGH/:** Update module github.com/apache/thrift to v0.24.0 (release-3.7.x) ([#22767](https://github.com/grafana/loki/issues/22767)) ([36eb5f7](https://github.com/grafana/loki/commit/36eb5f7d1849d4a260f135958e0d8ca0a71abe92))
* **security/UNKNOWN/:** Update module github.com/containerd/containerd/v2 to v2.0.10 (release-3.7.x) ([#22478](https://github.com/grafana/loki/issues/22478)) ([6fa6165](https://github.com/grafana/loki/commit/6fa616585599ba4d3b66e3e4b9c56065915d5da4))
* **security/UNKNOWN/pkg/push:** Update module golang.org/x/net to v0.55.0 (release-3.7.x) ([#22200](https://github.com/grafana/loki/issues/22200)) ([dd7a124](https://github.com/grafana/loki/commit/dd7a1243e6e1f87503753000b1d6a6c96c0fd109))
* **storage:** Fix index filename issue with legacy S3 client and `chunk_delimiter` ([#23165](https://github.com/grafana/loki/issues/23165)) ([0eca83c](https://github.com/grafana/loki/commit/0eca83ca93a866efbe51a500b3b20748a823758c))

### 3.7.3 (2026-06-24)

* **deps:** Move to Go 1.26 ([#20988](https://github.com/grafana/loki/issues/20988)) ([4fb960c](https://github.com/grafana/loki/commit/4fb960c)).
* **deps:** Update go toolchain directive to v1.25.11 ([#22192](https://github.com/grafana/loki/issues/22192)) ([d5d7a54](https://github.com/grafana/loki/commit/d5d7a54)).
* **deps:** Update golang.org/x/crypto to v0.52.0 ([#22189](https://github.com/grafana/loki/issues/22189)) ([6383bfe](https://github.com/grafana/loki/commit/6383bfe)).
* **deps:** Update golang.org/x/net to v0.55.0 ([#22190](https://github.com/grafana/loki/issues/22190)) ([3d93fb8](https://github.com/grafana/loki/commit/3d93fb8)).
* **deps:** Update golang.org/x/sys to v0.44.0 ([#22191](https://github.com/grafana/loki/issues/22191)) ([3379538](https://github.com/grafana/loki/commit/3379538)).
* **deps:** Update module github.com/containerd/containerd/v2 to v2.0.9 ([#22187](https://github.com/grafana/loki/issues/22187)) ([365c7e9](https://github.com/grafana/loki/commit/365c7e9)).
* **deps:** Update module go.opentelemetry.io/otel to v1.41.0 ([#22188](https://github.com/grafana/loki/issues/22188)) ([dc8f4de](https://github.com/grafana/loki/commit/dc8f4de)).
* **distributor:** Sanitize User-Agent to valid UTF-8 before using as Prometheus label value ([#21162](https://github.com/grafana/loki/issues/21162)) ([ed3cf08](https://github.com/grafana/loki/commit/ed3cf08)).
* **engine:** Align range-query evaluation timestamps to the step grid ([#20904](https://github.com/grafana/loki/issues/20904)) ([28d03ec](https://github.com/grafana/loki/commit/28d03ec)).
* **engine:** Fix additional instances of writing/reading on a closed conn ([#20982](https://github.com/grafana/loki/issues/20982)) ([1f7cacf](https://github.com/grafana/loki/commit/1f7cacf)).
* **ingester:** Duplicate lines with different structured metadata ([#21039](https://github.com/grafana/loki/issues/21039)) ([60a1567](https://github.com/grafana/loki/commit/60a1567)).
* **ingester:** WAL replay hang when flush doesn't progress ([#21176](https://github.com/grafana/loki/issues/21176)) ([fb58ab8](https://github.com/grafana/loki/commit/fb58ab8)).
* **logql:** Return error on way-too-long label string ([#20937](https://github.com/grafana/loki/issues/20937)) ([9c9660a](https://github.com/grafana/loki/commit/9c9660a)).
* **operator:** Fix incorrect image reference in OLM bundle ([#20966](https://github.com/grafana/loki/issues/20966)) ([84bc9bc](https://github.com/grafana/loki/commit/84bc9bc)).
* **operator:** Properly handle the deprecated replicationFactor ([#20935](https://github.com/grafana/loki/issues/20935)) ([1b96366](https://github.com/grafana/loki/commit/1b96366)).
* **operator:** Provide AWS region in environment variable for STS mode ([#21136](https://github.com/grafana/loki/issues/21136)) ([cb261d9](https://github.com/grafana/loki/commit/cb261d9)).
* **operator:** Set ingester minimum available pods based on replication factor ([#19517](https://github.com/grafana/loki/issues/19517)) ([efc63e8](https://github.com/grafana/loki/commit/efc63e8)).
* **querier:** Prevent goroutine leak in downstreamer on context cancellation ([#21062](https://github.com/grafana/loki/issues/21062)) ([2891919](https://github.com/grafana/loki/commit/2891919)).
* **query-engine:** Fix OR operations being silently dropped ([#21204](https://github.com/grafana/loki/issues/21204)) ([20ea556](https://github.com/grafana/loki/commit/20ea556)).
* **security:** Mask all credentials ([#20766](https://github.com/grafana/loki/issues/20766)) ([f8d94cb](https://github.com/grafana/loki/commit/f8d94cb)).
* **storage:** Broken get and replace for S3 client ([#21163](https://github.com/grafana/loki/issues/21163)) ([e6abd15](https://github.com/grafana/loki/commit/e6abd15)).

### 3.7.2 (2026-05-13)

* **deps:** CVEs in release 3.7.x ([#21771](https://github.com/grafana/loki/issues/21771)) ([bb4c5d8](https://github.com/grafana/loki/commit/bb4c5d8758b8540cb742466683143a9ea93743c8)).
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.97.3 ([#21457](https://github.com/grafana/loki/issues/21457)) ([7bc9450](https://github.com/grafana/loki/commit/7bc945082fd7ef7632a9bfd18cc22f23130ea64a)).
* **ruler:** Fix ruler panic related to unset validation scheme (backport release-3.7.x) ([#21401](https://github.com/grafana/loki/issues/21401)) ([cf65729](https://github.com/grafana/loki/commit/cf65729674b1f0be8011223c474df3e2e5253216)).
* **storage:** Attach SHA-256 checksum on PutObject for Object Lock buckets ([#21849](https://github.com/grafana/loki/issues/21849)) ([7df13d9](https://github.com/grafana/loki/commit/7df13d9ad3993700c3aff23edf5dfa2966281416)).

### 3.7.1 (2026-03-27)

* **deps:** Upgrade Go and gRPC versions on 3.7.x ([#21282](https://github.com/grafana/loki/issues/21282)) ([2c8fff2](https://github.com/grafana/loki/commit/2c8fff222bab6813374b973ae0eb49043d3ed14e)).

### 3.7.0 (2026-03-26)

* **cd:** Add loki-image to needs ([#19870](https://github.com/grafana/loki/issues/19870)) ([a2c4ea6](https://github.com/grafana/loki/commit/a2c4ea66ac1aa8b0da077cdd91a20a171ee706c2))
* **compactor:** Compactor file descriptor leak ([#20077](https://github.com/grafana/loki/issues/20077)) ([0c3dd8c](https://github.com/grafana/loki/commit/0c3dd8ce7efaebfc2d011b02c4dbd809e6955fec))
* **config:** Migrate renovate config ([#19436](https://github.com/grafana/loki/issues/19436)) ([97745fe](https://github.com/grafana/loki/commit/97745fed6c384a695c4bf6d267d0b092c434ac64))
* **dataobj:** Flush into multiple index objects when ErrBuilderFull ([#19223](https://github.com/grafana/loki/issues/19223)) ([32dbef9](https://github.com/grafana/loki/commit/32dbef99b8ed8bf220eef569fd90acdaf624ca50))
* **deps:** Update dataobj-inspect transitive deps version ([#19813](https://github.com/grafana/loki/issues/19813)) ([5b212b7](https://github.com/grafana/loki/commit/5b212b7469945afa15a5a3a62ec69c63a09d2198))
* **deps:** Update module cloud.google.com/go/bigtable to v1.41.0 (main) ([#20352](https://github.com/grafana/loki/issues/20352)) ([6102309](https://github.com/grafana/loki/commit/610230906ecd4f2a4c1a1ed5d39ce7ad294c4700))
* **deps:** Update module cloud.google.com/go/pubsub to v2 (main) ([#19803](https://github.com/grafana/loki/issues/19803)) ([d47dde3](https://github.com/grafana/loki/commit/d47dde3e5aa66761d3bfc40d0bff86ae6e63eeb6))
* **deps:** Update module cloud.google.com/go/storage to v1.59.0 (main) ([#20407](https://github.com/grafana/loki/issues/20407)) ([5c71db6](https://github.com/grafana/loki/commit/5c71db648a5c32f809e216da6d7a48bc79f8c8f5))
* **deps:** Update module github.com/alecthomas/chroma/v2 to v2.22.0 (main) ([#20409](https://github.com/grafana/loki/issues/20409)) ([c64f044](https://github.com/grafana/loki/commit/c64f0443b599d47ae98936d1c1f6e2a352fc395b))
* **deps:** Update module github.com/apache/arrow-go/v18 to v18.5.0 (main) ([#20354](https://github.com/grafana/loki/issues/20354)) ([d0861a1](https://github.com/grafana/loki/commit/d0861a1f3475fd257d212f2f9c0548cfe0d635f6))
* **deps:** Update module github.com/aws/aws-sdk-go-v2 to v1.40.1 (main) ([#20137](https://github.com/grafana/loki/issues/20137)) ([e106809](https://github.com/grafana/loki/commit/e10680982a83ea4d1e112553e2d43cc8ab818f65))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/config to v1.32.7 (main) ([#20401](https://github.com/grafana/loki/issues/20401)) ([50ce71a](https://github.com/grafana/loki/commit/50ce71a5bfeb44ff7b8eff433930a6d23cac14b6))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/credentials to v1.19.7 (main) ([#20402](https://github.com/grafana/loki/issues/20402)) ([f20228d](https://github.com/grafana/loki/commit/f20228dc547126239878028a0158d3bdebfd38d9))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/dynamodb to v1.53.6 (main) ([#20403](https://github.com/grafana/loki/issues/20403)) ([31a870c](https://github.com/grafana/loki/commit/31a870cc64615e163618c6afef4f828071d3f4cd))
* **deps:** Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.95.1 (main) ([#20406](https://github.com/grafana/loki/issues/20406)) ([c7c1411](https://github.com/grafana/loki/commit/c7c14114f44cfc77cb8f8635a5b7340ce3a9e329))
* **deps:** Update module github.com/aws/smithy-go to v1.24.0 (main) ([#20117](https://github.com/grafana/loki/issues/20117)) ([b0efa70](https://github.com/grafana/loki/commit/b0efa70f4b1707cda25bb4324d02af71fdc23e77))
* **deps:** Update module github.com/axiomhq/hyperloglog to v0.2.6 (main) ([#20341](https://github.com/grafana/loki/issues/20341)) ([4469f82](https://github.com/grafana/loki/commit/4469f826d2df54486b6c51a1a3e7b899c5d5a1d6))
* **deps:** Update module github.com/baidubce/bce-sdk-go to v0.9.256 (main) ([#20342](https://github.com/grafana/loki/issues/20342)) ([69e6254](https://github.com/grafana/loki/commit/69e625405628ac25b7fabe07a809df0d4e6b73e1))
* **deps:** Update module github.com/bits-and-blooms/bloom/v3 to v3.7.1 (main) ([#19755](https://github.com/grafana/loki/issues/19755)) ([af47e1f](https://github.com/grafana/loki/commit/af47e1f4feabb62824376e51d1ae80a4a28c2d89))
* **deps:** Update module github.com/bmatcuk/doublestar/v4 to v4.9.2 (main) ([#20418](https://github.com/grafana/loki/issues/20418)) ([078dc94](https://github.com/grafana/loki/commit/078dc945551d4ba4f918653c4c758f4fc6525931))
* **deps:** Update module github.com/coder/quartz to v0.3.0 (main) ([#19786](https://github.com/grafana/loki/issues/19786)) ([6f784f9](https://github.com/grafana/loki/commit/6f784f99d4ca208835a488874e9d12dea5020ad5))
* **deps:** Update module github.com/docker/docker to v28.5.2+incompatible (main) ([#19756](https://github.com/grafana/loki/issues/19756)) ([1007ee4](https://github.com/grafana/loki/commit/1007ee4a89c439791fb1b1a7b5e155ab91e8b6d2))
* **deps:** Update module github.com/gocql/gocql to v2 (main) ([#19794](https://github.com/grafana/loki/issues/19794)) ([898b6d2](https://github.com/grafana/loki/commit/898b6d20374015dd5cf12571fae91393c6e22c9b))
* **deps:** Update module github.com/google/renameio/v2 to v2.0.2 (main) ([#20411](https://github.com/grafana/loki/issues/20411)) ([cc43074](https://github.com/grafana/loki/commit/cc4307424575386914b6c40bc605155710f46e06))
* **deps:** Update module github.com/grafana/loki/v3 to v3.6.3 (main) ([#20343](https://github.com/grafana/loki/issues/20343)) ([d1ae7a1](https://github.com/grafana/loki/commit/d1ae7a1ba35e9775dcbf58b8081e214157d1a16d))
* **deps:** Update module github.com/grpc-ecosystem/go-grpc-middleware/v2 to v2.3.3 (main) ([#19758](https://github.com/grafana/loki/issues/19758)) ([8133da9](https://github.com/grafana/loki/commit/8133da968d8e8eb4b21d254368a40c01acd68ccb))
* **deps:** Update module github.com/hashicorp/consul/api to v1.33.0 (main) ([#19788](https://github.com/grafana/loki/issues/19788)) ([e417259](https://github.com/grafana/loki/commit/e417259980d7edf2f75eefeac94efd351421d99e))
* **deps:** Update module github.com/ibm/go-sdk-core/v5 to v5.21.2 (main) ([#19988](https://github.com/grafana/loki/issues/19988)) ([d8ab970](https://github.com/grafana/loki/commit/d8ab970b1cf5bfb2864e6c92b2de05bbd66feb0e))
* **deps:** Update module github.com/ibm/ibm-cos-sdk-go to v1.13.0 (main) ([#20364](https://github.com/grafana/loki/issues/20364)) ([52d1d8d](https://github.com/grafana/loki/commit/52d1d8d96669075d6685b64508766fb4549cf009))
* **deps:** Update module github.com/ibm/sarama to v1.46.3 (main) ([#19760](https://github.com/grafana/loki/issues/19760)) ([4a19787](https://github.com/grafana/loki/commit/4a19787dff6fcca74a380b686dc58882f7195a3a))
* **deps:** Update module github.com/influxdata/telegraf to v1.37.0 (main) ([#20356](https://github.com/grafana/loki/issues/20356)) ([dc1e0ae](https://github.com/grafana/loki/commit/dc1e0aefcb004662b70d704ea8b11e7df26fa8e9))
* **deps:** Update module github.com/klauspost/compress to v1.18.2 (main) ([#20108](https://github.com/grafana/loki/issues/20108)) ([f4f2b2a](https://github.com/grafana/loki/commit/f4f2b2a718b2d591adb99b358430ad905c3fa4ee))
* **deps:** Update module github.com/leodido/go-syslog/v4 to v4.3.0 (main) ([#19416](https://github.com/grafana/loki/issues/19416)) ([036387b](https://github.com/grafana/loki/commit/036387b297cb99c28f1aaa1c6751b004b167edba))
* **deps:** Update module github.com/minio/minio-go/v7 to v7.0.98 (main) ([#20436](https://github.com/grafana/loki/issues/20436)) ([cf89342](https://github.com/grafana/loki/commit/cf893421f2fe51853331dc318843dde4b0969807))
* **deps:** Update module github.com/ncw/swift/v2 to v2.0.5 (main) ([#19764](https://github.com/grafana/loki/issues/19764)) ([fa5e144](https://github.com/grafana/loki/commit/fa5e144b42a5d0e4347a366213fd491ec808603e))
* **deps:** Update module github.com/oschwald/geoip2-golang to v2 (main) ([#19799](https://github.com/grafana/loki/issues/19799)) ([33eeab6](https://github.com/grafana/loki/commit/33eeab62c144a78cb0511c8d5321591ce5f0d03e))
* **deps:** Update module github.com/oschwald/geoip2-golang/v2 to v2.1.0 (main) ([#20357](https://github.com/grafana/loki/issues/20357)) ([8853d71](https://github.com/grafana/loki/commit/8853d710d4582b5591c0bc7fa192a3e228ee0a9a))
* **deps:** Update module github.com/parquet-go/parquet-go to v0.27.0 (main) ([#20426](https://github.com/grafana/loki/issues/20426)) ([a283eac](https://github.com/grafana/loki/commit/a283eac267662b5239430cfcb70893460ec84657))
* **deps:** Update module github.com/prometheus/alertmanager to v0.30.0 (main) ([#20358](https://github.com/grafana/loki/issues/20358)) ([f53a609](https://github.com/grafana/loki/commit/f53a609be4c9c32f4ad03a4a61daa5e0b525f989))
* **deps:** Update module github.com/prometheus/client_golang to v1.23.2 (main) ([#19763](https://github.com/grafana/loki/issues/19763)) ([8317f7e](https://github.com/grafana/loki/commit/8317f7e4629be6bc8f40579f00c2151fb126fb4c))
* **deps:** Update module github.com/prometheus/common to v0.67.5 (main) ([#20363](https://github.com/grafana/loki/issues/20363)) ([aaacbf4](https://github.com/grafana/loki/commit/aaacbf4edaa2fd657cd1376fcb3fb53a2d029757))
* **deps:** Update module github.com/prometheus/prometheus to v0.309.1 (main) ([#20388](https://github.com/grafana/loki/issues/20388)) ([bf79bcf](https://github.com/grafana/loki/commit/bf79bcff061caecd0a2d6828c1b8ec31990f2a9c))
* **deps:** Update module github.com/prometheus/sigv4 to v0.4.0 (main) ([#20386](https://github.com/grafana/loki/issues/20386)) ([2f80526](https://github.com/grafana/loki/commit/2f80526f19a036513d9849876717c17678bc9ff6))
* **deps:** Update module github.com/redis/go-redis/v9 to v9.17.2 (main) ([#20116](https://github.com/grafana/loki/issues/20116)) ([434a929](https://github.com/grafana/loki/commit/434a9295a57d0469826e9f8aa252a2bc1a1ebaaa))
* **deps:** Update module github.com/schollz/progressbar/v3 to v3.19.0 (main) ([#20365](https://github.com/grafana/loki/issues/20365)) ([0b238bc](https://github.com/grafana/loki/commit/0b238bccea4babd97b3393de9ac0380e7c65a66c))
* **deps:** Update module github.com/shirou/gopsutil/v4 to v4.25.12 (main) ([#20347](https://github.com/grafana/loki/issues/20347)) ([0740eb8](https://github.com/grafana/loki/commit/0740eb8a56b1e57000eb4e6862ee75d3c3f15b0f))
* **deps:** Update module github.com/sirupsen/logrus to v1.9.4 (main) ([#20447](https://github.com/grafana/loki/issues/20447)) ([35c8df7](https://github.com/grafana/loki/commit/35c8df7705afd31f8d735533a9d15175d273a616))
* **deps:** Update module github.com/sony/gobreaker/v2 to v2.4.0 (main) ([#20366](https://github.com/grafana/loki/issues/20366)) ([090ffd5](https://github.com/grafana/loki/commit/090ffd517252ea39a96fac037afe85119b9a9bd0))
* **deps:** Update module github.com/tjhop/slog-gokit to v0.1.5 (main) ([#19808](https://github.com/grafana/loki/issues/19808)) ([615413e](https://github.com/grafana/loki/commit/615413e269f7ed4b52fbd752e4cde29e773d454d))
* **deps:** Update module github.com/twmb/franz-go to v1.20.6 (main) ([#20348](https://github.com/grafana/loki/issues/20348)) ([5ee4fee](https://github.com/grafana/loki/commit/5ee4fee4bc66e179dd8717d09699353932d119e7))
* **deps:** Update module github.com/twmb/franz-go/pkg/kadm to v1.17.1 (main) ([#19790](https://github.com/grafana/loki/issues/19790)) ([1dad0be](https://github.com/grafana/loki/commit/1dad0be6b7ba439a2be9a64562b3762e495d6950))
* **deps:** Update module github.com/twmb/franz-go/pkg/kmsg to v1.12.0 (main) ([#19791](https://github.com/grafana/loki/issues/19791)) ([f28c247](https://github.com/grafana/loki/commit/f28c247cb47dca402b8465e9ff5373d288df17bc))
* **deps:** Update module github.com/workiva/go-datastructures to v1.1.7 (main) ([#19766](https://github.com/grafana/loki/issues/19766)) ([f5e0683](https://github.com/grafana/loki/commit/f5e0683986cec6d5edd643626c2dcbb4e894a65e))
* **deps:** Update module github.com/xdg-go/scram to v1.2.0 (main) ([#20046](https://github.com/grafana/loki/issues/20046)) ([9e52320](https://github.com/grafana/loki/commit/9e523207b8c3afcdb810ca4f4d47b43887235346))
* **deps:** Update module go.opentelemetry.io/collector/pdata to v1.49.0 (main) ([#20371](https://github.com/grafana/loki/issues/20371)) ([7d759f2](https://github.com/grafana/loki/commit/7d759f215d1167b46470fa40a9812d3d3a53126c))
* **deps:** Update module go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc to v0.64.0 (main) ([#20372](https://github.com/grafana/loki/issues/20372)) ([9da1b1b](https://github.com/grafana/loki/commit/9da1b1bcd5643148e0d749b4d780606712d0f6b2))
* **deps:** Update module go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace to v0.64.0 (main) ([#20373](https://github.com/grafana/loki/issues/20373)) ([719635a](https://github.com/grafana/loki/commit/719635ad80c31a005ab6809cf7cab947611e20c7))
* **deps:** Update module go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp to v0.64.0 (main) ([#20374](https://github.com/grafana/loki/issues/20374)) ([f1b3e1b](https://github.com/grafana/loki/commit/f1b3e1bf67ed296edf49ed8c30fed800be147d9b))
* **deps:** Update module go.opentelemetry.io/otel/sdk to v1.39.0 (main) ([#20376](https://github.com/grafana/loki/issues/20376)) ([95b82d6](https://github.com/grafana/loki/commit/95b82d66b701afc8c9473a68dccd27251a9b824c))
* **deps:** Update module golang.org/x/crypto to v0.44.0 (main) ([#19776](https://github.com/grafana/loki/issues/19776)) ([c85c67a](https://github.com/grafana/loki/commit/c85c67ac77f900655f2bbe41b6793eb4e05c7f7f))
* **deps:** Update module golang.org/x/net to v0.47.0 (main) ([#19850](https://github.com/grafana/loki/issues/19850)) ([5c422a6](https://github.com/grafana/loki/commit/5c422a6b9010fb2bf05c7f56e955cc0acf5a429b))
* **deps:** Update module golang.org/x/oauth2 to v0.34.0 (main) ([#20160](https://github.com/grafana/loki/issues/20160)) ([ebf7b93](https://github.com/grafana/loki/commit/ebf7b93c67139b5d329dd3c951aedc900824513b))
* **deps:** Update module golang.org/x/sync to v0.19.0 (main) ([#20161](https://github.com/grafana/loki/issues/20161)) ([817b9d8](https://github.com/grafana/loki/commit/817b9d8c9c43aae0d4f1f54a13889a1a1d18e7c6))
* **deps:** Update module golang.org/x/sys to v0.40.0 (main) ([#20378](https://github.com/grafana/loki/issues/20378)) ([9538b20](https://github.com/grafana/loki/commit/9538b2097a4604f49442e5845c2d95498a0110d0))
* **deps:** Update module golang.org/x/text to v0.33.0 (main) ([#20408](https://github.com/grafana/loki/issues/20408)) ([9fd6733](https://github.com/grafana/loki/commit/9fd6733ef4f9f82ee032728c27229ce00673ddfe))
* **deps:** Update module golang.org/x/time to v0.14.0 (main) ([#19782](https://github.com/grafana/loki/issues/19782)) ([74f68fa](https://github.com/grafana/loki/commit/74f68fac62757fc50b295100bb30c7bb4c3007aa))
* **deps:** Update module google.golang.org/api to v0.257.0 (main) ([#20143](https://github.com/grafana/loki/issues/20143)) ([5f1da75](https://github.com/grafana/loki/commit/5f1da751a4181af9bf11a675bb24f9e8c5cad39c))
* **deps:** Update module google.golang.org/grpc to v1.77.0 (main) ([#19945](https://github.com/grafana/loki/issues/19945)) ([f3213bb](https://github.com/grafana/loki/commit/f3213bb2f90fe1f8ff4421f400c22820ee41e69b))
* **deps:** Update module google.golang.org/protobuf to v1.36.11 (main) ([#20349](https://github.com/grafana/loki/issues/20349)) ([a80b52e](https://github.com/grafana/loki/commit/a80b52e01941693892313c440dd656e72aff4d2c))
* **deps:** Update module k8s.io/apimachinery to v0.35.0 (main) ([#20381](https://github.com/grafana/loki/issues/20381)) ([731e067](https://github.com/grafana/loki/commit/731e067b3abfb463fe59b4d9ebc4e3116d97fd87))
* **distributor:** Avoid recalculating the segmentation key hash twice ([#19961](https://github.com/grafana/loki/issues/19961)) ([8b78f79](https://github.com/grafana/loki/commit/8b78f7924d7414af116809912d76cfd4e71dcbda))
* **distributor:** Panic when no healthy instances found ([#19998](https://github.com/grafana/loki/issues/19998)) ([1c5dfed](https://github.com/grafana/loki/commit/1c5dfed23acc4931baa6bdcb1fe49b12e2f77072))
* **distributor:** Track discarded entries and bytes when hitting stream limits using the ingest limits service ([#20244](https://github.com/grafana/loki/issues/20244)) ([9b0af7c](https://github.com/grafana/loki/commit/9b0af7c002b8a8e87fe1e3ec2817d06641e099d3))
* **docker:** Missing permissions to start docker ([#19947](https://github.com/grafana/loki/issues/19947)) ([39d2bea](https://github.com/grafana/loki/commit/39d2beaae6f2867084cb856e353413fe58e066fc))
* **docker:** Set WORKDIR to root in loki Dockerfiles ([#19941](https://github.com/grafana/loki/issues/19941)) ([13f2b1a](https://github.com/grafana/loki/commit/13f2b1adaeb12e39d1019aa484488422feb499f1))
* **helm:** Bump Helm deps, publish loki-helm-test w/ release ([#19939](https://github.com/grafana/loki/issues/19939)) ([7e4e34e](https://github.com/grafana/loki/commit/7e4e34e4c8da860f08db3c3d89eeb8d8d77d82dd))
* **helm:** Add startup probe read ([#19708](https://github.com/grafana/loki/issues/19708)) ([bce87fb](https://github.com/grafana/loki/commit/bce87fbce5d518d2785620596ee78898718b1ace))
* **helm:** Apply fix from [#14126](https://github.com/grafana/loki/issues/14126) to example ([#20252](https://github.com/grafana/loki/issues/20252)) ([716563a](https://github.com/grafana/loki/commit/716563a660f079762c8ea8caf5ca78a12e81b314))
* **helm:** Correct GEL provisioner tenant creation instructions ([#20271](https://github.com/grafana/loki/issues/20271)) ([9639e2e](https://github.com/grafana/loki/commit/9639e2e1269067fb1e3d13ecfbd7f5350326d606))
* **helm:** Correct GEL version ([#19657](https://github.com/grafana/loki/issues/19657)) ([cd8b195](https://github.com/grafana/loki/commit/cd8b195fd8e278aa7ef44da401ac9c36209575a3))
* **helm:** Do not mark loki.storage.bucketNames.chunks as required, if an s3 url is provided ([#19873](https://github.com/grafana/loki/issues/19873)) ([e9951bb](https://github.com/grafana/loki/commit/e9951bb4ebfdd85c4567c67a339edf6d7fb78991))
* **helm:** Do not mark loki.storage.bucketNames.chunks as required, if minio is used. ([#19871](https://github.com/grafana/loki/issues/19871)) ([eddd4f8](https://github.com/grafana/loki/commit/eddd4f873de3cfd6a14e349356abc8fa9596a13d))
* **helm:** Do not mark loki.storage.bucketNames.ruler as required, if rulerConfig.storage.type is local ([#19882](https://github.com/grafana/loki/issues/19882)) ([f2f564a](https://github.com/grafana/loki/commit/f2f564a2672df170502760d9c8a0afca2855515b))
* **helm:** Don't fail for missing bucket name, if local disk is used. ([#19675](https://github.com/grafana/loki/issues/19675)) ([ad0a29e](https://github.com/grafana/loki/commit/ad0a29eaae24cbcc385ae5ed39614fbd2765fb27))
* **helm:** Don't fail for missing bucket name, if minio is enabled. ([#19745](https://github.com/grafana/loki/issues/19745)) ([cd0c578](https://github.com/grafana/loki/commit/cd0c5782105c998fe011b9e47f1563c1bf769ccd))
* **helm:** Enable volumeAttributesClassName attributes for volumeClaimTemplates ([#19719](https://github.com/grafana/loki/issues/19719)) ([06da42a](https://github.com/grafana/loki/commit/06da42a8ac203361960ffada560bc44fea257c96))
* **helm:** Fix ingester-b volumeAttributesClassName templating ([#20188](https://github.com/grafana/loki/issues/20188)) ([d696f18](https://github.com/grafana/loki/commit/d696f188165d9bf4c40b60f8baa0c8bc4ed9bf29))
* **helm:** Fix rendering of dnsConfig for backend, read, write, single-binary and table-manager ([#20013](https://github.com/grafana/loki/issues/20013)) ([1cdb3c7](https://github.com/grafana/loki/commit/1cdb3c731819eba5808de55a29b319e09aa00ec0))
* **helm:** Respect global registry in sidecar image ([#18246](https://github.com/grafana/loki/issues/18246)) ([#19347](https://github.com/grafana/loki/issues/19347)) ([79eae2c](https://github.com/grafana/loki/commit/79eae2ca25c8faa262c246bafcf913eb4e8fd2c3))
* **helm:** Update Chart version in README.md ([#19669](https://github.com/grafana/loki/issues/19669)) ([94096b7](https://github.com/grafana/loki/commit/94096b745229fbc653fe6037251e36816f2b76e5))
* **helm:** Update version ([#19670](https://github.com/grafana/loki/issues/19670)) ([b90ae22](https://github.com/grafana/loki/commit/b90ae22a2c3da177e334fd16e057fb5e260d1f69))
* **ingest-limits:** Reduce FetchMaxBytes to 10MB ([#19883](https://github.com/grafana/loki/issues/19883)) ([82cfafd](https://github.com/grafana/loki/commit/82cfafd3862906e41ab32a03e8cd7f10e8511940))
* **ingest-limits:** Revoke partitions if lost ([#20030](https://github.com/grafana/loki/issues/20030)) ([1ac5d1f](https://github.com/grafana/loki/commit/1ac5d1f0d5e80cbb5e2ea73fed007ea6f80ccf61))
* **kafka:** Deadlock on shutdown ([#20384](https://github.com/grafana/loki/issues/20384)) ([272a278](https://github.com/grafana/loki/commit/272a2783802c4476d55ba4b8e89ebc02bb3f345e))
* **logql:** Parsed labels should not override structured metadata ([#19991](https://github.com/grafana/loki/issues/19991)) ([61f9367](https://github.com/grafana/loki/commit/61f936751b185a9e6f7127321c83b1fc1816a067))
* **lokitool:** Update ruler path and enable alternative TLS env variables ([#19572](https://github.com/grafana/loki/issues/19572)) ([d1ce5cb](https://github.com/grafana/loki/commit/d1ce5cb6af8cf1cb8063d07fb0c5b841e23b5caf))
* **nomad:** Nomad simple example ([#19629](https://github.com/grafana/loki/issues/19629)) ([17aec11](https://github.com/grafana/loki/commit/17aec119a0af3e1749f38857f35dca18200d0c4f))
* **operator:** Change leader-election parameters ([#19707](https://github.com/grafana/loki/issues/19707)) ([86068cf](https://github.com/grafana/loki/commit/86068cfb6c9c221dbb3878bdd5e86a53b6ce2caa))
* **operator:** Do not deploy NetworkPolicies automatically on OCP 4.20 ([#19680](https://github.com/grafana/loki/issues/19680)) ([8df33ff](https://github.com/grafana/loki/commit/8df33ff659d53d17b68fb894879587b330e63607))
* **operator:** Return quickstart script to working condition and improve rootless usage ([#19960](https://github.com/grafana/loki/issues/19960)) ([397da27](https://github.com/grafana/loki/commit/397da277753d771d8c1492dd3f4db4b208b3532d))
* **parser:** Do not cache key conflicts results in intern set ([#19984](https://github.com/grafana/loki/issues/19984)) ([0a9b024](https://github.com/grafana/loki/commit/0a9b024106eda89844dbd68e6625b241ab0b1655))
* **querier:** Support multi-tenant queries in Patterns API ([#19809](https://github.com/grafana/loki/issues/19809)) ([f609e27](https://github.com/grafana/loki/commit/f609e27e2eebaadce3c629c8ad8be054f885a604))
* **querier:** Restrict start/end timestamp to requested range for scheduler ([#20086](https://github.com/grafana/loki/issues/20086)) ([448cc74](https://github.com/grafana/loki/commit/448cc745cab05e8967696e3c24231b4d23ee7715))
* **querier:** Set Content-Type header for JSON responses in serializeHTTPHandler ([#19878](https://github.com/grafana/loki/issues/19878)) ([019d6b4](https://github.com/grafana/loki/commit/019d6b40a84ff0de1e835ba111efe121c966cf2d))
* **querylimits:** Accept request limits over not initialized limits ([#19891](https://github.com/grafana/loki/issues/19891)) ([905eac8](https://github.com/grafana/loki/commit/905eac851f38da589c49a41478e2edd2b3bb3ffb))
* **retry:** Do not retry if any of multierrors is a client error ([#19887](https://github.com/grafana/loki/issues/19887)) ([9825137](https://github.com/grafana/loki/commit/9825137713f84ee26de4640084f6ee3d5cc4354d))
* **ruler:** Validate remote write config ([#19920](https://github.com/grafana/loki/issues/19920)) ([e916944](https://github.com/grafana/loki/commit/e9169443760f0e64868f52e8b746f31eadf68f6d))
* **server:** Return status bad request code for interval limit error ([#19895](https://github.com/grafana/loki/issues/19895)) ([f21f5d5](https://github.com/grafana/loki/commit/f21f5d545f5ce16a12bed556a63ac5c7764559cd))
* **storage:** Do not override S3 region if already specified in configuration chain ([#20127](https://github.com/grafana/loki/issues/20127)) ([0046bfb](https://github.com/grafana/loki/commit/0046bfb3a4c08321a0006d1aa70963e234162404))
* **storage:** Fix regression in S3 client configuration ([#20110](https://github.com/grafana/loki/issues/20110)) ([d3f9532](https://github.com/grafana/loki/commit/d3f9532b061c4406bbd5d95c77b5220c0bd80193)), closes [#19908](https://github.com/grafana/loki/issues/19908)

# Send Data

# Sending Logs to Loki via Kafka using Alloy

<!-- vale Grafana.We = NO -->
<!-- INTERACTIVE page intro.md START -->

# Sending Logs to Loki via Kafka using Alloy

Alloy natively supports receiving logs via Kafka. In this example, we will configure Alloy to receive logs via Kafka using two different methods:

- [loki.source.kafka](https://grafana.com/docs/alloy/latest/reference/components/loki.source.kafka): reads messages from Kafka using a consumer group and forwards them to other `loki.*` components.
- [otelcol.receiver.kafka](https://grafana.com/docs/alloy/latest/reference/components/otelcol.receiver.kafka/): accepts telemetry data from a Kafka broker and forwards it to other `otelcol.*` components.

<!-- INTERACTIVE ignore START -->

## Dependencies

Before you begin, ensure you have the following to run the demo:

- Docker
- Docker Compose

{{< admonition type="tip" >}}
Alternatively, you can try out this example in our interactive learning environment: [Sending Logs to Loki via Kafka using Alloy](https://killercoda.com/grafana-labs/course/loki/alloy-kafka-logs).

It's a fully configured environment with all the dependencies already installed.

![Interactive](/media/docs/loki/loki-ile.svg)

Provide feedback, report bugs, and raise issues in the [Grafana Killercoda repository](https://github.com/grafana/killercoda).
{{< /admonition >}}
<!-- INTERACTIVE ignore END -->

## Scenario

In this scenario, we have a microservices application called the Carnivorous Greenhouse. This application consists of the following services:

- **User Service:** Manages user data and authentication for the application. Such as creating users and logging in.
- **Plant Service:** Manages the creation of new plants and updates other services when a new plant is created.
- **Simulation Service:** Generates sensor data for each plant.
- **Websocket Service:** Manages the websocket connections for the application.
- **Bug Service:** A service that when enabled, randomly causes services to fail and generate additional logs.
- **Main App:** The main application that ties all the services together.
- **Database:** A database that stores user and plant data.

Each service generates logs that are sent to Alloy via Kafka. In this example, they are sent on two different topics:

- `loki`: This sends a structured log formatted message (json).
- `otlp`: This sends a serialized OpenTelemetry log message.

You would not typically do this within your own application, but for the purposes of this example we wanted to show how Alloy can handle different types of log messages over Kafka.

<!-- INTERACTIVE page intro.md END -->

<!-- INTERACTIVE page step1.md START -->

## Step 1: Environment setup

In this step, we will set up our environment by cloning the repository that contains our demo application and spinning up our observability stack using Docker Compose.

1. To get started, clone the repository that contains our demo application:
    <!-- INTERACTIVE exec START -->
    ```bash
    git clone -b microservice-kafka  https://github.com/grafana/loki-fundamentals.git
    ```
    <!-- INTERACTIVE exec END -->

1. Next we will spin up our observability stack using Docker Compose:

    <!-- INTERACTIVE ignore START -->
    ```bash
    docker compose -f loki-fundamentals/docker-compose.yml up -d
    ```
    <!-- INTERACTIVE ignore END -->

    {{< docs/ignore >}}

    <!-- INTERACTIVE exec START -->
    ```bash
    docker-compose -f loki-fundamentals/docker-compose.yml up -d 
    ```
    <!-- INTERACTIVE exec END -->

    {{< /docs/ignore >}}

    This will spin up the following services:

    ```console
    ✔ Container loki-fundamentals-grafana-1      Started                                                        
    ✔ Container loki-fundamentals-loki-1         Started                        
    ✔ Container loki-fundamentals-alloy-1        Started
    ✔ Container loki-fundamentals-zookeeper-1    Started
    ✔ Container loki-fundamentals-kafka-1        Started
    ```

We will access two UI interfaces:

- Alloy at [http://localhost:12345](http://localhost:12345)
- Grafana at [http://localhost:3000](http://localhost:3000)
<!-- INTERACTIVE page step1.md END -->

<!-- INTERACTIVE page step2.md START -->

## Step 2: Configure Alloy to ingest raw Kafka logs

In this first step, we will configure Alloy to ingest raw Kafka logs. To do this, we will update the `config.alloy` file to include the Kafka logs configuration.

### Open your code editor and locate the `config.alloy` file

Grafana Alloy requires a configuration file to define the components and their relationships. The configuration file is written using Alloy configuration syntax. We will build the entire observability pipeline within this configuration file. To start, we will open the `config.alloy` file in the code editor:

{{< docs/ignore >}}
**Note: Killercoda has an inbuilt Code editor which can be accessed via the `Editor` tab.**

1. Expand the `loki-fundamentals` directory in the file explorer of the `Editor` tab.
1. Locate the `config.alloy` file in the `loki-fundamentals` directory (Top level directory).
1. Click on the `config.alloy` file to open it in the code editor.
{{< /docs/ignore >}}

<!-- INTERACTIVE ignore START -->
1. Open the `loki-fundamentals` directory in a code editor of your choice.
1. Locate the `config.alloy` file in the top level directory, `loki-fundamentals'.
1. Click on the `config.alloy` file to open it in the code editor.
<!-- INTERACTIVE ignore END -->

You will copy all three of the following configuration snippets into the `config.alloy` file.  

### Source logs from Kafka

First, we will configure the Loki Kafka source. `loki.source.kafka` reads messages from Kafka using a consumer group and forwards them to other `loki.*` components.

The component starts a new Kafka consumer group for the given arguments and fans out incoming entries to the list of receivers in `forward_to`.

Add the following configuration to the `config.alloy` file:

```alloy
loki.source.kafka "raw" {
  brokers                = ["kafka:9092"]
  topics                 = ["loki"]
  forward_to             = [loki.write.http.receiver]
  relabel_rules          = loki.relabel.kafka.rules
  version                = "2.0.0"
  labels                = {service_name = "raw_kafka"}
}
```

In this configuration:

- `brokers`: The Kafka brokers to connect to.
- `topics`: The Kafka topics to consume. In this case, we are consuming the `loki` topic.
- `forward_to`: The list of receivers to forward the logs to. In this case, we are forwarding the logs to the `loki.write.http.receiver`.
- `relabel_rules`: The relabel rules to apply to the incoming logs. This can be used to generate labels from the temporary internal labels that are added by the Kafka source.
- `version`: The Kafka protocol version to use.
- `labels`: The labels to add to the incoming logs. In this case, we are adding a `service_name` label with the value `raw_kafka`. This will be used to identify the logs from the raw Kafka source in the Log Explorer App in Grafana.

For more information on the `loki.source.kafka` configuration, see the [Loki Kafka Source documentation](https://grafana.com/docs/alloy/latest/reference/components/loki.source.kafka/).

### Create a dynamic relabel based on Kafka topic

Next, we will configure the Loki relabel rules. The `loki.relabel` component rewrites the label set of each log entry passed to its receiver by applying one or more relabeling rules and forwards the results to the list of receivers in the component’s arguments. In our case we are directly calling the rule from the `loki.source.kafka` component.

Now add the following configuration to the `config.alloy` file:

```alloy
loki.relabel "kafka" {
  forward_to      = [loki.write.http.receiver]
  rule {
    source_labels = ["__meta_kafka_topic"]
    target_label  = "topic"
  }
}
```

In this configuration:

- `forward_to`: The list of receivers to forward the logs to. In this case, we are forwarding the logs to the `loki.write.http.receiver`. Though in this case, we are directly calling the rule from the `loki.source.kafka` component. So `forward_to` is being used as a placeholder as it is required by the `loki.relabel` component.
- `rule`: The relabeling rule to apply to the incoming logs. In this case, we are renaming the `__meta_kafka_topic` label to `topic`.

For more information on the `loki.relabel` configuration, see the [Loki Relabel documentation](https://grafana.com/docs/alloy/latest/reference/components/loki.relabel/).

### Write logs to Loki

Lastly, we will configure the Loki write component. `loki.write` receives log entries from other loki components and sends them over the network using the Loki logproto format.

And finally, add the following configuration to the `config.alloy` file:

```alloy
loki.write "http" {
  endpoint {
    url = "http://loki:3100/loki/api/v1/push"
  }
}
```

In this configuration:

- `endpoint`: The endpoint to send the logs to. In this case, we are sending the logs to the Loki HTTP endpoint.

For more information on the `loki.write` configuration, see the [Loki Write documentation](https://grafana.com/docs/alloy/latest/reference/components/loki.write/).

### Reload the Alloy configuration to check the changes

Once added, save the file. Then run the following command to request Alloy to reload the configuration:

<!-- INTERACTIVE exec START -->
```bash
curl -X POST http://localhost:12345/-/reload
```
<!-- INTERACTIVE exec END -->

The new configuration will be loaded.  You can verify this by checking the Alloy UI: [http://localhost:12345](http://localhost:12345).

## Stuck? Need help?

If you get stuck or need help creating the configuration, you can copy and replace the entire `config.alloy` using the completed configuration file:

<!-- INTERACTIVE exec START -->
```bash
cp loki-fundamentals/completed/config-raw.alloy loki-fundamentals/config.alloy
curl -X POST http://localhost:12345/-/reload
```
<!-- INTERACTIVE exec END -->

<!-- INTERACTIVE page step2.md END -->

<!-- INTERACTIVE page step3.md START -->

## Step 3: Configure Alloy to ingest OpenTelemetry logs via Kafka

Next we will configure Alloy to also ingest OpenTelemetry logs via Kafka, we need to update the Alloy configuration file once again. We will add the new components to the `config.alloy` file along with the existing components.

### Open your code editor and locate the `config.alloy` file

Like before, we generate our next pipeline configuration within the same `config.alloy` file. You will add the following configuration snippets to the file **in addition** to the existing configuration. Essentially, we are configuring two pipelines within the same Alloy configuration file.

### Source OpenTelemetry logs from Kafka

First, we will configure the OpenTelemetry Kafka receiver. `otelcol.receiver.kafka` accepts telemetry data from a Kafka broker and forwards it to other `otelcol.*` components.

Now add the following configuration to the `config.alloy` file:

```alloy
otelcol.receiver.kafka "default" {
  brokers          = ["kafka:9092"]
  protocol_version = "2.0.0"

  logs {
    topics   = ["otlp"]
    encoding = "otlp_proto"
  }

  output {
    logs    = [otelcol.processor.batch.default.input]
  }
}
```

In this configuration:

- `brokers`: The Kafka brokers to connect to.
- `protocol_version`: The Kafka protocol version to use.
- `logs`: Configures how to receive logs from Kafka. The `topics` argument sets the Kafka topics to consume, in this case the `otlp` topic. The `encoding` sets the encoding of the incoming logs, which decodes messages as OTLP protobuf.
- `output`: The list of receivers to forward the logs to. In this case, we are forwarding the logs to the `otelcol.processor.batch.default.input`.

For more information on the `otelcol.receiver.kafka` configuration, see the [OpenTelemetry Receiver Kafka documentation](https://grafana.com/docs/alloy/latest/reference/components/otelcol.receiver.kafka/).

### Batch OpenTelemetry logs before sending

Next, we will configure a OpenTelemetry processor. `otelcol.processor.batch` accepts telemetry data from other otelcol components and places them into batches. Batching improves the compression of data and reduces the number of outgoing network requests required to transmit data. This processor supports both size and time based batching.

Now add the following configuration to the `config.alloy` file:

```alloy
otelcol.processor.batch "default" {
    output {
        logs = [otelcol.exporter.otlphttp.default.input]
    }
}
```

In this configuration:

- `output`: The list of receivers to forward the logs to. In this case, we are forwarding the logs to the `otelcol.exporter.otlphttp.default.input`.

For more information on the `otelcol.processor.batch` configuration, see the [OpenTelemetry Processor Batch documentation](https://grafana.com/docs/alloy/latest/reference/components/otelcol.processor.batch/).

### Write OpenTelemetry logs to Loki

Lastly, we will configure the OpenTelemetry exporter. `otelcol.exporter.otlphttp` accepts telemetry data from other otelcol components and writes them over the network using the OTLP HTTP protocol. We will use this exporter to send the logs to the Loki native OTLP endpoint.

Finally, add the following configuration to the `config.alloy` file:

```alloy
otelcol.exporter.otlphttp "default" {
  client {
    endpoint = "http://loki:3100/otlp"
  }
}
```

In this configuration:

- `client`: The client configuration for the exporter. In this case, we are sending the logs to the Loki OTLP endpoint.

For more information on the `otelcol.exporter.otlphttp` configuration, see the [OpenTelemetry Exporter OTLP HTTP documentation](https://grafana.com/docs/alloy/latest/reference/components/otelcol.exporter.otlphttp/).

### Reload the Alloy configuration to check the changes

Once added, save the file. Then run the following command to request Alloy to reload the configuration:
<!-- INTERACTIVE exec START -->
```bash
curl -X POST http://localhost:12345/-/reload
```
<!-- INTERACTIVE exec END -->

The new configuration will be loaded. You can verify this by checking the Alloy UI: [http://localhost:12345](http://localhost:12345).

## Stuck? Need help (Full Configuration)?

If you get stuck or need help creating the configuration, you can copy and replace the entire `config.alloy`. This differs from the previous `Stuck? Need help` section as we are replacing the entire configuration file with the completed configuration file. Rather than just adding the first Loki Raw Pipeline configuration.

<!-- INTERACTIVE exec START -->
```bash
cp loki-fundamentals/completed/config.alloy loki-fundamentals/config.alloy
curl -X POST http://localhost:12345/-/reload
```
<!-- INTERACTIVE exec END -->

<!-- INTERACTIVE page step3.md END -->

<!-- INTERACTIVE page step4.md START -->

## Step 4: Start the Carnivorous Greenhouse

In this step, we will start the Carnivorous Greenhouse application. To start the application, run the following command:
<!-- INTERACTIVE ignore START -->
{{< admonition type="note" >}}
This docker-compose file relies on the `loki-fundamentals_loki` docker network. If you have not started the observability stack, you will need to start it first.
{{< /admonition >}}
<!-- INTERACTIVE ignore END -->

{{< docs/ignore >}}

**Note: This docker-compose file relies on the `loki-fundamentals_loki` docker network. If you have not started the observability stack, you will need to start it first.**

{{< /docs/ignore >}}

<!-- INTERACTIVE ignore START -->
```bash
docker compose -f loki-fundamentals/greenhouse/docker-compose-micro.yml up -d --build 
```
<!-- INTERACTIVE ignore END -->

{{< docs/ignore >}}

<!-- INTERACTIVE exec START -->
```bash
docker-compose -f loki-fundamentals/greenhouse/docker-compose-micro.yml up -d --build
```
<!-- INTERACTIVE exec END -->

{{< /docs/ignore >}}

This will start the following services:

```console
 ✔ Container greenhouse-db-1                 Started                                                         
 ✔ Container greenhouse-websocket_service-1  Started 
 ✔ Container greenhouse-bug_service-1        Started
 ✔ Container greenhouse-user_service-1       Started
 ✔ Container greenhouse-plant_service-1      Started
 ✔ Container greenhouse-simulation_service-1 Started
 ✔ Container greenhouse-main_app-1           Started
```

Once started, you can access the Carnivorous Greenhouse application at [http://localhost:5005](http://localhost:5005). Generate some logs by interacting with the application in the following ways:

- Create a user
- Log in
- Create a few plants to monitor
- Enable bug mode to activate the bug service. This will cause services to fail and generate additional logs.

Finally to view the logs in Loki, navigate to the Loki Logs Explore view in Grafana at [http://localhost:3000/a/grafana-lokiexplore-app/explore](http://localhost:3000/a/grafana-lokiexplore-app/explore).

<!-- INTERACTIVE page step4.md END -->

<!-- INTERACTIVE page finish.md START -->

## Summary

In this example, we configured Alloy to ingest logs via Kafka. We configured Alloy to ingest logs in two different formats: raw logs and OpenTelemetry logs. Where to go next?

{{< docs/ignore >}}

### Back to docs

Head back to where you started from to continue with the Loki documentation: [Loki documentation](https://grafana.com/docs/loki/latest/send-data/alloy)

{{< /docs/ignore >}}

## Further reading

For more information on Grafana Alloy, refer to the following resources:

- [Grafana Alloy getting started examples](https://grafana.com/docs/alloy/latest/tutorials/)
- [Grafana Alloy component reference](https://grafana.com/docs/alloy/latest/reference/components/)

## Complete metrics, logs, traces, and profiling example

If you would like to use a demo that includes Mimir, Loki, Tempo, and Grafana, you can use [Introduction to Metrics, Logs, Traces, and Profiling in Grafana](https://github.com/grafana/intro-to-mlt). `Intro-to-mltp` provides a self-contained environment for learning about Mimir, Loki, Tempo, and Grafana.

The project includes detailed explanations of each component and annotated configurations for a single-instance deployment. Data from `intro-to-mltp` can also be pushed to Grafana Cloud.

<!-- INTERACTIVE page finish.md END -->
<!-- vale Grafana.We = YES -->

# Sending OpenTelemetry logs to Loki using Alloy

<!-- vale Grafana.We = NO -->
<!-- INTERACTIVE page intro.md START -->

# Sending OpenTelemetry logs to Loki using Alloy

Alloy natively supports receiving logs in the OpenTelemetry format. This allows you to send logs from applications instrumented with OpenTelemetry to Alloy, which can then be sent to Loki for storage and visualization in Grafana. In this example, we will make use of 3 Alloy components to achieve this:

- **OpenTelemetry Receiver:** This component will receive logs in the OpenTelemetry format via HTTP and gRPC.
- **OpenTelemetry Processor:** This component will accept telemetry data from other `otelcol.*` components and place them into batches. Batching improves the compression of data and reduces the number of outgoing network requests required to transmit data.
- **OpenTelemetry Exporter:** This component will accept telemetry data from other `otelcol.*` components and write them over the network using the OTLP HTTP protocol. We will use this exporter to send the logs to the Loki native OTLP endpoint.

<!-- INTERACTIVE ignore START -->

## Dependencies

Before you begin, ensure you have the following to run the demo:

- Docker
- Docker Compose

{{< admonition type="tip" >}}
Alternatively, you can try out this example in our interactive learning environment: [Sending OpenTelemetry logs to Loki using Alloy](https://killercoda.com/grafana-labs/course/loki/alloy-otel-logs).

It's a fully configured environment with all the dependencies already installed.

![Interactive](/media/docs/loki/loki-ile.svg)

Provide feedback, report bugs, and raise issues in the [Grafana Killercoda repository](https://github.com/grafana/killercoda).
{{< /admonition >}}

<!-- INTERACTIVE ignore END -->

## Scenario

In this scenario, we have a microservices application called the Carnivorous Greenhouse. This application consists of the following services:

- **User Service:** Manages user data and authentication for the application. Such as creating users and logging in.
- **Plant Service:** Manages the creation of new plants and updates other services when a new plant is created.
- **Simulation Service:** Generates sensor data for each plant.
- **Websocket Service:** Manages the websocket connections for the application.
- **Bug Service:** A service that when enabled, randomly causes services to fail and generate additional logs.
- **Main App:** The main application that ties all the services together.
- **Database:** A database that stores user and plant data.

Each service generates logs using the OpenTelemetry SDK and exports to Alloy in the OpenTelemetry format. Alloy then ingests the logs and sends them to Loki. We will configure Alloy to ingest OpenTelemetry logs, send them to Loki, and view the logs in Grafana.

<!-- INTERACTIVE page intro.md END -->

<!-- INTERACTIVE page step1.md START -->

## Step 1: Environment setup

In this step, we will set up our environment by cloning the repository that contains our demo application and spinning up our observability stack using Docker Compose.

1. To get started, clone the repository that contains our demo application:
    <!-- INTERACTIVE exec START -->
    ```bash
    git clone -b microservice-otel  https://github.com/grafana/loki-fundamentals.git
    ```
    <!-- INTERACTIVE exec END -->
1. Next we will spin up our observability stack using Docker Compose:

    <!-- INTERACTIVE ignore START -->
    ```bash
    docker compose -f loki-fundamentals/docker-compose.yml up -d
    ```
    <!-- INTERACTIVE ignore END -->

    {{< docs/ignore >}}

    <!-- INTERACTIVE exec START -->
    ```bash
    docker-compose -f loki-fundamentals/docker-compose.yml up -d 
    ```
    <!-- INTERACTIVE exec END -->

    {{< /docs/ignore >}}

    This will spin up the following services:

    ```console
    ✔ Container loki-fundamentals-grafana-1  Started                                                        
    ✔ Container loki-fundamentals-loki-1     Started                        
    ✔ Container loki-fundamentals-alloy-1    Started
    ```

We will access two UI interfaces:

- Alloy at [http://localhost:12345](http://localhost:12345)
- Grafana at [http://localhost:3000](http://localhost:3000)
<!-- INTERACTIVE page step1.md END -->

<!-- INTERACTIVE page step2.md START -->

## Step 2: Configure Alloy to ingest OpenTelemetry logs

To configure Alloy to ingest OpenTelemetry logs, we need to update the Alloy configuration file. To start, we will update the `config.alloy` file to include the OpenTelemetry logs configuration.

### Open your code editor and locate the `config.alloy` file

Grafana Alloy requires a configuration file to define the components and their relationships. The configuration file is written using Alloy configuration syntax. We will build the entire observability pipeline within this configuration file. To start, we will open the `config.alloy` file in the code editor:

{{< docs/ignore >}}
**Note: Killercoda has an inbuilt Code editor which can be accessed via the `Editor` tab.**

1. Expand the `loki-fundamentals` directory in the file explorer of the `Editor` tab.
1. Locate the `config.alloy` file in the top level directory, `loki-fundamentals'.
1. Click on the `config.alloy` file to open it in the code editor.
{{< /docs/ignore >}}

<!-- INTERACTIVE ignore START -->
1. Open the `loki-fundamentals` directory in a code editor of your choice.
1. Locate the `config.alloy` file in the `loki-fundamentals` directory (Top level directory).
1. Click on the `config.alloy` file to open it in the code editor.
<!-- INTERACTIVE ignore END -->

You will copy all three of the following configuration snippets into the `config.alloy` file.

### Receive OpenTelemetry logs via gRPC and HTTP

First, we will configure the OpenTelemetry receiver. `otelcol.receiver.otlp` accepts logs in the OpenTelemetry format via HTTP and gRPC. We will use this receiver to receive logs from the Carnivorous Greenhouse application.

Now add the following configuration to the `config.alloy` file:

```alloy
 otelcol.receiver.otlp "default" {
   http {}
   grpc {}

   output {
     logs    = [otelcol.processor.batch.default.input]
   }
 }
```

In this configuration:

- `http`: The HTTP configuration for the receiver. This configuration is used to receive logs in the OpenTelemetry format via HTTP.
- `grpc`: The gRPC configuration for the receiver. This configuration is used to receive logs in the OpenTelemetry format via gRPC.
- `output`: The list of processors to forward the logs to. In this case, we are forwarding the logs to the `otelcol.processor.batch.default.input`.

For more information on the `otelcol.receiver.otlp` configuration, see the [OpenTelemetry Receiver OTLP documentation](https://grafana.com/docs/alloy/latest/reference/components/otelcol.receiver.otlp/).

### Create batches of logs using a OpenTelemetry processor

Next, we will configure a OpenTelemetry processor. `otelcol.processor.batch` accepts telemetry data from other `otelcol` components and places them into batches. Batching improves the compression of data and reduces the number of outgoing network requests required to transmit data. This processor supports both size and time based batching.

Now add the following configuration to the `config.alloy` file:

```alloy
otelcol.processor.batch "default" {
    output {
        logs = [otelcol.exporter.otlphttp.default.input]
    }
}
```

In this configuration:

- `output`: The list of receivers to forward the logs to. In this case, we are forwarding the logs to the `otelcol.exporter.otlphttp.default.input`.

For more information on the `otelcol.processor.batch` configuration, see the [OpenTelemetry Processor Batch documentation](https://grafana.com/docs/alloy/latest/reference/components/otelcol.processor.batch/).

### Export logs to Loki using a OpenTelemetry exporter

Lastly, we will configure the OpenTelemetry exporter. `otelcol.exporter.otlphttp` accepts telemetry data from other `otelcol` components and writes them over the network using the OTLP HTTP protocol. We will use this exporter to send the logs to the Loki native OTLP endpoint.

Now add the following configuration to the `config.alloy` file:

```alloy
otelcol.exporter.otlphttp "default" {
  client {
    endpoint = "http://loki:3100/otlp"
  }
}
```

For more information on the `otelcol.exporter.otlphttp` configuration, see the [OpenTelemetry Exporter OTLP HTTP documentation](https://grafana.com/docs/alloy/latest/reference/components/otelcol.exporter.otlphttp/).

### Reload the Alloy configuration

Once added, save the file. Then run the following command to request Alloy to reload the configuration:
<!-- INTERACTIVE exec START -->
```bash
curl -X POST http://localhost:12345/-/reload
```
<!-- INTERACTIVE exec END -->

The new configuration will be loaded. You can verify this by checking the Alloy UI: [http://localhost:12345](http://localhost:12345).

## Stuck? Need help?

If you get stuck or need help creating the configuration, you can copy and replace the entire `config.alloy` using the completed configuration file:

<!-- INTERACTIVE exec START -->
```bash
cp loki-fundamentals/completed/config.alloy loki-fundamentals/config.alloy
curl -X POST http://localhost:12345/-/reload
```
<!-- INTERACTIVE exec END -->

<!-- INTERACTIVE page step2.md END -->

<!-- INTERACTIVE page step3.md START -->

## Step 3: Start the Carnivorous Greenhouse

In this step, we will start the Carnivorous Greenhouse application. To start the application, run the following command:
<!-- INTERACTIVE ignore START -->
{{< admonition type="note" >}}
This docker-compose file relies on the `loki-fundamentals_loki` docker network. If you have not started the observability stack, you will need to start it first.
{{< /admonition >}}
<!-- INTERACTIVE ignore END -->

{{< docs/ignore >}}

**Note: This docker-compose file relies on the `loki-fundamentals_loki` docker network. If you have not started the observability stack, you will need to start it first.**

{{< /docs/ignore >}}

<!-- INTERACTIVE ignore START -->
```bash
docker compose -f loki-fundamentals/greenhouse/docker-compose-micro.yml up -d --build 
```
<!-- INTERACTIVE ignore END -->

{{< docs/ignore >}}

<!-- INTERACTIVE exec START -->
```bash
docker-compose -f loki-fundamentals/greenhouse/docker-compose-micro.yml up -d --build
```
<!-- INTERACTIVE exec END -->

{{< /docs/ignore >}}

This will start the following services:

```bash
 ✔ Container greenhouse-db-1                 Started                                                         
 ✔ Container greenhouse-websocket_service-1  Started 
 ✔ Container greenhouse-bug_service-1        Started
 ✔ Container greenhouse-user_service-1       Started
 ✔ Container greenhouse-plant_service-1      Started
 ✔ Container greenhouse-simulation_service-1 Started
 ✔ Container greenhouse-main_app-1           Started
```

Once started, you can access the Carnivorous Greenhouse application at [http://localhost:5005](http://localhost:5005). Generate some logs by interacting with the application in the following ways:

- Create a user
- Log in
- Create a few plants to monitor
- Enable bug mode to activate the bug service. This will cause services to fail and generate additional logs.

Finally to view the logs in Loki, navigate to the Loki Logs Explore view in Grafana at [http://localhost:3000/a/grafana-lokiexplore-app/explore](http://localhost:3000/a/grafana-lokiexplore-app/explore).

<!-- INTERACTIVE page step3.md END -->

<!-- INTERACTIVE page finish.md START -->

## Summary

In this example, we configured Alloy to ingest OpenTelemetry logs and send them to Loki. This was a simple example to demonstrate how to send logs from an application instrumented with OpenTelemetry to Loki using Alloy. Where to go next?

{{< docs/ignore >}}

### Back to docs

Head back to where you started from to continue with the Loki documentation: [Loki documentation](https://grafana.com/docs/loki/latest/send-data/alloy)

{{< /docs/ignore >}}

## Further reading

For more information on Grafana Alloy, refer to the following resources:

- [Grafana Alloy getting started examples](https://grafana.com/docs/alloy/latest/tutorials/)
- [Grafana Alloy component reference](https://grafana.com/docs/alloy/latest/reference/components/)

## Complete metrics, logs, traces, and profiling example

If you would like to use a demo that includes Mimir, Loki, Tempo, and Grafana, you can use [Introduction to Metrics, Logs, Traces, and Profiling in Grafana](https://github.com/grafana/intro-to-mlt). `Intro-to-mltp` provides a self-contained environment for learning about Mimir, Loki, Tempo, and Grafana.

The project includes detailed explanations of each component and annotated configurations for a single-instance deployment. Data from `intro-to-mltp` can also be pushed to Grafana Cloud.

<!-- INTERACTIVE page finish.md END -->
<!-- vale Grafana.We = YES -->

# Docker driver client configuration

The Docker daemon on each machine has a default logging driver and
each container will use the default driver unless configured otherwise.

## Installation

Before configuring the plugin, [install or upgrade the Grafana Loki Docker Driver Client](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/docker-driver/)

## Change the logging driver for a container

The `docker run` command can be configured to use a different logging driver
than the Docker daemon's default with the `--log-driver` flag. Any options that
the logging driver supports can be set using the `--log-opt <NAME>=<VALUE>` flag.
`--log-opt` can be passed multiple times for each option to be set.

The following command will start Grafana in a container and send logs to Grafana
Cloud, using a batch size of 400000 bytes and no more than 5 retries if a send
fails.

```bash
docker run --log-driver=loki \
    --log-opt loki-url="https://<user_id>:<password>@logs-us-west1.grafana.net/loki/api/v1/push" \
    --log-opt loki-retries=5 \
    --log-opt loki-batch-size=400000 \
    grafana/grafana
```

{{< admonition type="note" >}}
The Loki logging driver still uses the json-log driver in combination with sending logs to Loki, this is mainly useful
to keep the `docker logs` command working.
You can adjust file size and rotation using the respective log option `max-size` and `max-file`. Keep in mind that
default values for these options are not taken from json-log configuration.
You can disable JSON log file creation by setting the log option `no-file` to `true`, but this also disables `docker logs` for the container.
{{< /admonition >}}

## Change the default logging driver

If you want the Loki logging driver to be the default for all containers,
change Docker's `daemon.json` file (located in `/etc/docker` on Linux) and set
the value of `log-driver` to `loki`:

```json
{
  "debug": true,
  "log-driver": "loki"
}
```

Options for the logging driver can also be configured with `log-opts` in the
`daemon.json`:

```json
{
  "debug": true,
  "log-driver": "loki",
  "log-opts": {
    "loki-url": "https://<user_id>:<password>@logs-us-west1.grafana.net/loki/api/v1/push",
    "loki-batch-size": "400000"
  }
}
```

{{< admonition type="note" >}}
log-opt configuration options in daemon.json must be provided as strings. Boolean and numeric values (such as the value for loki-batch-size in the example above) must therefore be enclosed in quotes (`"`).
{{< /admonition >}}

After changing `daemon.json`, restart the Docker daemon for the changes to take
effect. All **newly created** containers from that host will then send logs to Loki via the driver.

## Configure the logging driver for a Swarm service or Compose

You can also configure the logging driver for
a [swarm service](https://docs.docker.com/engine/swarm/how-swarm-mode-works/services/)
directly in your compose file. This also applies for `docker-compose`:

```yaml
services:
  logger:
    image: grafana/grafana
    logging:
      driver: loki
      options:
        loki-url: "https://<user_id>:<password>@logs-prod-us-central1.grafana.net/loki/api/v1/push"
```

You can then deploy your stack using:

```bash
docker stack deploy my_stack_name --compose-file docker-compose.yaml
```

Or with `docker compose`:

```bash
docker compose -f docker-compose.yaml up
```

Once deployed, the Grafana service will send its logs to Loki.

{{< admonition type="note" >}}
Stack name and service name for each swarm service and project name and service name for each compose service are
automatically discovered and sent as Loki labels, this way you can filter by them in Grafana.
{{< /admonition >}}

## Labels

Loki can receive a set of labels along with log line. These labels are used to index log entries and query back logs
using [LogQL stream selector](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/#log-stream-selector).

By default, the Docker driver will add the following labels to each log line:

- `filename`: the path to the JSON log file on disk. This label is set even when `no-file=true`, in which case it reflects the path the file would have used, even though no file is actually created.
- `host`: the hostname where the log has been generated
- `source`: the stream the log line was read from, either `stdout` or `stderr`.
- `swarm_stack`, `swarm_service`: added when deploying from Docker Swarm.
- `compose_project`, `compose_service`: added when deploying with Docker Compose.

Custom labels can be added using the `loki-external-labels`, `loki-pipeline-stages`,
`loki-pipeline-stage-file`, `labels`, `env`, and `env-regex` options. See the
next section for all supported options.

`loki-external-labels` have the default value of `container_name={{.Name}}`. If you have custom value for
`loki-external-labels` then that will replace the default value, meaning you won't have `container_name` label unless
you explicitly add it (e.g: `loki-external-labels: "job=docker,container_name={{.Name}}"`.

## Pipeline stages

While you can provide `loki-pipeline-stage-file` it can be hard to mount the configuration file to the driver root
filesystem.
This is why another option `loki-pipeline-stages` is available allowing you to pass a list of stages inlined. Pipeline
stages are run at last on every lines.

The example [docker-compose](https://github.com/grafana/loki/blob/main/clients/cmd/docker-driver/docker-compose.yaml)
below configures 2 stages, one to extract level values and one to set it as a label:

```yaml
services:
  grafana:
    image: grafana/grafana
    logging:
      driver: loki
      options:
        loki-url: http://host.docker.internal:3100/loki/api/v1/push
        loki-pipeline-stages: |
          - regex:
              expression: '(level|lvl|severity)=(?P<level>\w+)'
          - labels:
              level:
    ports:
      - "3000:3000"
```

{{< admonition type="note" >}}
Note the `loki-pipeline-stages: |` letting you keep the indentation correct.
{{< /admonition >}}

When using docker run you can also pass the value via a string parameter like such:

```bash
read -d '' stages << EOF
- regex:
     expression: '(level|lvl|severity)=(?P<level>\\\w+)'
- labels:
    level:
EOF

docker run --log-driver=loki \
    --log-opt loki-url="http://host.docker.internal:3100/loki/api/v1/push" \
    --log-opt loki-pipeline-stages="$stages" \
    -p 3000:3000 grafana/grafana
```

This is a bit more difficult as you need to properly escape bash special characters. (note `\\\w+` for `\w+`)

Providing both `loki-pipeline-stage-file` and `loki-pipeline-stages` will cause an error.

## Relabeling

You can use [Prometheus relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config) configuration to modify labels discovered by the driver. The configuration must be passed as a YAML string like the [pipeline stages](#pipeline-stages).

Relabeling phase will happen only once per container and it is applied on the container metadata when it starts. So you can for example rename the labels that are only available during the starting of the container, not the labels available on log lines. Use [pipeline stages](#pipeline-stages) instead.

For example the configuration below will rename the label `swarm_stack` and `swarm_service` to respectively `namespace` and `service`.

```yaml
services:
  grafana:
    image: grafana/grafana
    logging:
      driver: loki
      options:
        loki-url: http://host.docker.internal:3100/loki/api/v1/push
        loki-relabel-config: |
          - action: labelmap
            regex: swarm_stack
            replacement: namespace
          - action: labelmap
            regex: swarm_(service)
    ports:
      - "3000:3000"
```

## Supported log-opt options

To specify additional logging driver options, you can use the --log-opt NAME=VALUE flag.

| Option                          | Required? |       Default Value        | Description                                                                                                                                                                                                                                                                                                     |
|---------------------------------|:---------:|:--------------------------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `loki-url`                      |    Yes    |                            | Loki HTTP push endpoint.                                                                                                                                                                                                                                                                                        |
| `loki-external-labels`          |    No     | `container_name={{.Name}}` | Additional label value pair separated by `,` to send with logs. The value is expanded with the [Docker tag template format](https://docs.docker.com/engine/logging/log_tags/). (eg: `container_name={{.ID}}.{{.Name}},cluster=prod`)                                                                 |
| `loki-timeout`                  |    No     |           `10s`            | The timeout to use when sending logs to the Loki instance. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".                                                                                                                                                                                      |
| `loki-batch-wait`               |    No     |            `1s`            | The amount of time to wait before sending a log batch complete or not. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".                                                                                                                                                                          |
| `loki-batch-size`               |    No     |         `1048576`          | The maximum size of a log batch to send.                                                                                                                                                                                                                                                                        |
| `loki-min-backoff`              |    No     |          `500ms`           | The minimum amount of time to wait before retrying a batch. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".                                                                                                                                                                                     |
| `loki-max-backoff`              |    No     |            `5m`            | The maximum amount of time to wait before retrying a batch. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".                                                                                                                                                                                     |
| `loki-retries`                  |    No     |            `10`            | The maximum amount of retries for a log batch. Setting it to `0` will retry indefinitely.                                                                                                                                                                                                                       |
| `loki-pipeline-stage-file`      |    No     |                            | The location of a pipeline stage configuration file ([example](https://github.com/grafana/loki/blob/main/clients/cmd/docker-driver/pipeline-example.yaml)). Pipeline stages allows to parse log lines to extract more labels, [see associated documentation](https://grafana.com/docs/alloy/latest/reference/components/loki/loki.process/). |
| `loki-pipeline-stages`          |    No     |                            | The pipeline stage configuration provided as a string [see pipeline stages](#pipeline-stages) and [associated documentation](https://grafana.com/docs/alloy/latest/reference/components/loki/loki.process/).                                                                                                                                 |
| `loki-relabel-config`           |    No     |                            | A [Prometheus relabeling configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config) allowing you to rename labels [see relabeling](#relabeling).                                                                                                                  |
| `loki-tenant-id`                |    No     |                            | Set the tenant id (http header`X-Scope-OrgID`) when sending logs to Loki. It can be overridden by a pipeline stage.                                                                                                                                                                                             |
| `loki-tls-ca-file`              |    No     |                            | Set the path to a custom certificate authority.                                                                                                                                                                                                                                                                 |
| `loki-tls-cert-file`            |    No     |                            | Set the path to a client certificate file.                                                                                                                                                                                                                                                                      |
| `loki-tls-key-file`             |    No     |                            | Set the path to a client key.                                                                                                                                                                                                                                                                                   |
| `loki-tls-server-name`          |    No     |                            | Name used to validate the server certificate.                                                                                                                                                                                                                                                                   |
| `loki-tls-insecure-skip-verify` |    No     |          `false`*          | Allow to skip tls verification.                                                                                                                                                                                                                                                                                 |
| `loki-proxy-url`                |    No     |                            | Proxy URL use to connect to Loki.                                                                                                                                                                                                                                                                               |
| `no-file`                       |    No     |          `false`*          | When `false` (default), the driver creates a local JSON log file for each container, which enables `docker logs`. Set to `true` to skip creating JSON log files entirely. No disk space is used for logs, but `docker logs` will not work for the container.                                |
| `keep-file`                     |    No     |          `false`*          | When `false` (default), the JSON log file for a container is deleted when the container stops. Set to `true` to retain the JSON log file after the container stops. Has no effect when `no-file=true`, because no JSON log file is created.                                                                      |
| `max-size`                      |    No     |             -1             | The maximum size of the log before it is rolled. A positive integer plus a modifier representing the unit of measure (k, m, or g). Defaults to -1 (unlimited). This is used by json-log required to keep the `docker log` command working.                                                                      |
| `max-file`                      |    No     |             1              | The maximum number of log files that can be present. If rolling the logs creates excess files, the oldest file is removed. Only effective when max-size is also set. A positive integer. Defaults to 1.                                                                                                         |
| `labels`                        |    No     |                            | Comma-separated list of keys of labels, which should be included in message, if these labels are specified for container.                                                                                                                                                                                       |
| `env`                           |    No     |                            | Comma-separated list of keys of environment variables to be included in message if they specified for a container.                                                                                                                                                                                              |
| `env-regex`                     |    No     |                            | A regular expression to match logging-related environment variables. Used for advanced log label options. If there is collision between the label and env keys, the value of the env takes precedence. Both options add additional fields to the labels of a logging message.                                   |
| `mode`                          |    No     |         `blocking`         | Standard Docker logging-driver option. Set to `non-blocking` to buffer log messages in memory and send them to Loki asynchronously instead of blocking the container's main process. See [Known Issue: Deadlocked Docker Daemon](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/docker-driver/#known-issue-deadlocked-docker-daemon).                                     |
| `max-buffer-size`               |    No     |            `1m`            | Standard Docker logging-driver option. The size of the in-memory buffer used when `mode=non-blocking`. Has no effect when `mode=blocking` (the default).                                                                                                                                                        |

### `no-file` and `keep-file` interactions

The `no-file` and `keep-file` options control whether JSON log files are created and how long they are kept:

| `no-file` | `keep-file` | JSON log file created? | Deleted on container stop? | `docker logs` available? |
|:---------:|:-----------:|:----------------------:|:--------------------------:|:------------------------:|
| `false`   | `false`     | Yes                    | Yes                        | Yes                      |
| `false`   | `true`      | Yes                    | No                         | Yes                      |
| `true`    | (any)       | No                     | N/A                        | No                       |

### *note:

Boolean values in the configuration file only accept the following string type:

| Value |              Accepted Values               |
|-------|:------------------------------------------:|
| true  |  `"1", "t", "T", "true", "TRUE", "True"`   |
| false | `"0", "f", "F", "false", "FALSE", "False"` |

## Troubleshooting

Plugin logs can be found as docker daemon log. To enable debug mode refer to the
[Docker daemon documentation](https://docs.docker.com/engine/daemon/).

The standard output (`stdout`) of a plugin is redirected to Docker logs. Such
entries are prefixed with `plugin=`.

To find out the plugin ID of the Loki logging driver, use `docker plugin ls` and
look for the `loki` entry.

Depending on your system, location of Docker daemon logging may vary. Refer to
[Docker documentation for Docker daemon](https://docs.docker.com/engine/daemon/)
log location for your specific platform.

# Fluent Bit community plugin

{{< admonition type="warning" >}}

We recommend using the official [Fluent Bit Loki plugin](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/fluentbit/fluent-bit-plugin/). The official plugin is more feature-rich and has better support for features such as structured metadata. The community plugin is still available for use, but it's no longer actively maintained.

{{< /admonition >}}

The Fluent Bit community plugin by Grafana Labs (`grafana-loki`) provided an alternative way to send logs to Loki. Although very similar to the [official plugin](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/fluentbit/fluent-bit-plugin/) there are some differences in the configuration options. This page provides instructions for how to install, configure, and use the Fluent Bit community plugin to send logs to Loki. Although the plugin is no longer actively maintained, this documentation is still available for reference.

{{< youtube id="s43IBSVyTpQ" >}}

## Usage

### Docker

You can run a Fluent Bit container with Loki output plugin pre-installed using our [Docker Hub](https://hub.docker.com/r/grafana/fluent-bit-plugin-loki) image:

```bash
docker run -v /var/log:/var/log \
    -e LOG_PATH="/var/log/*.log" -e LOKI_URL="http://localhost:3100/loki/api/v1/push" \
    grafana/fluent-bit-plugin-loki:latest
```

Or, you can run the standard [Fluent Bit image](https://hub.docker.com/r/fluent/fluent-bit) and provide the plugin binary separately, as described in the [Local](#local) section below.

### Docker container logs

To ship logs from Docker containers to Loki using Fluent Bit, you can use the Fluent Bit Docker image and configure it to forward logs directly to Loki. Below is a step-by-step guide on setting up Fluent Bit for this purpose.

#### Prerequisites

- Docker is installed on your machine.
- Running instance of Loki OSS.

#### Configuration

1. Create a Fluent Bit configuration file named `fluent-bit.conf` with the following content, which defines the input from Docker container logs and sets up the output to send logs to your Loki instance:

   ```ini
   [SERVICE]
       Flush        1
       Log_Level    info

   [INPUT]
       Name     tail
       Path     /var/lib/docker/containers/*/*.log
       Parser   docker
       Tag      docker.*

   [OUTPUT]
       Name         grafana-loki
       Match        *
       Url          http://localhost:3100/loki/api/v1/push
       Labels       {job="fluentbit"}
   ```

### Kubernetes

You can run Fluent Bit as a [daemonset](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) to collect all your Kubernetes workload logs.

To do so you can use the [Fluent Bit Helm chart](https://github.com/fluent/helm-charts) with the following `values.yaml` changing the value of `FLUENT_LOKI_URL`:

```yaml
image:
  # Here we use the Docker image which has the plugin installed
  repository: grafana/fluent-bit-plugin-loki
  tag: latest

args:
  - "-e"
  - "/fluent-bit/bin/out_grafana_loki.so"
  - --workdir=/fluent-bit/etc
  - --config=/fluent-bit/etc/conf/fluent-bit.conf

env:
  # Note that for security reasons you should fetch the credentials through a Kubernetes Secret https://kubernetes.io/docs/concepts/configuration/secret/ . You may use the envFrom for this.
  - name: FLUENT_LOKI_URL
    value: https://user:pass@your-loki.endpoint/loki/api/v1/push

config:
  inputs: |
    [INPUT]
        Name tail
        Tag kube.*
        Path /var/log/containers/*.log
        # Be aware that local clusters like docker-desktop or kind use the docker log format and not the cri. For more information, refer to the Fluent Bit Kubernetes installation documentation (https://docs.fluentbit.io/manual/installation/downloads/kubernetes).
        multiline.parser docker, cri
        Mem_Buf_Limit 5MB
        Skip_Long_Lines On

  outputs: |
    [Output]
        Name grafana-loki
        Match kube.*
        Url ${FLUENT_LOKI_URL}
        Labels {job="fluent-bit"}
        LabelKeys level,app # this sets the values for the actual Loki stream labels; all other keys remain in the log line
        BatchWait 1
        BatchSize 1048576
        LineFormat json
        LogLevel info
        AutoKubernetesLabels true
```

```bash
helm repo add fluent https://fluent.github.io/helm-charts
helm repo update
helm install fluent-bit fluent/fluent-bit -f values.yaml
```

By default it will collect all containers logs and extract labels from Kubernetes API (`container_name`, `namespace`, etc.).

If you also want to host your Loki instance inside the cluster install the [official Loki Helm chart](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/).

### AWS Elastic Container Service (ECS)

You can use the fluent-bit Loki Docker image as a Firelens log router in AWS ECS.
For more information about this see the [AWS ECS documentation](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/firelens-using-fluentbit.html).

### Local

First, you need to follow the [instructions](https://github.com/grafana/loki/blob/main/clients/cmd/fluent-bit/README.md) in order to build the plugin dynamic library.

Assuming you have Fluent Bit installed in your `$PATH` you can run the plugin using:

```bash
fluent-bit -e /path/to/built/out_grafana_loki.so -c fluent-bit.conf
```

You can also adapt your plugins.conf, removing the need to change the command line options:

```conf
[PLUGINS]
    Path /path/to/built/out_grafana_loki.so
```

## Configuration options

| Key                  | Description                                                                                                                                                                                                                                                                                                                                                                             | Default                                |
|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
| Url                  | Url of Loki server API endpoint.                                                                                                                                                                                                                                                                                                                                                        | http://localhost:3100/loki/api/v1/push |
| TenantID             | The tenant ID used by default to push logs to Loki. If omitted or empty it assumes Loki is running in single-tenant mode and no `X-Scope-OrgID` header is sent.                                                                                                                                                                                                                         | ""                                     |
| BatchWait            | Time to wait before send a log batch to Loki, full or not.                                                                                                                                                                                                                                                                                                                              | 1s                                     |
| BatchSize            | Log batch size to send a log batch to Loki (unit: Bytes).                                                                                                                                                                                                                                                                                                                               | 1 MiB (1024 * 1024 Bytes)               |
| Timeout              | Maximum time to wait for Loki server to respond to a request.                                                                                                                                                                                                                                                                                                                           | 10s                                    |
| MinBackoff           | Initial backoff time between retries.                                                                                                                                                                                                                                                                                                                                                   | 500ms                                  |
| MaxBackoff           | Maximum backoff time between retries.                                                                                                                                                                                                                                                                                                                                                   | 5m                                     |
| MaxRetries           | Maximum number of retries when sending batches. Setting it to `0` will retry indefinitely.                                                                                                                                                                                                                                                                                                                                        | 10                                     |
| Labels               | Labels for API requests.                                                                                                                                                                                                                                                                                                                                                                | {job="fluent-bit"}                     |
| LogLevel             | LogLevel for plugin logger.                                                                                                                                                                                                                                                                                                                                                             | `info`                                 |
| RemoveKeys           | Specify removing keys.                                                                                                                                                                                                                                                                                                                                                                  | none                                   |
| AutoKubernetesLabels | If set to `true`, it will add all Kubernetes labels to Loki labels.                                                                                                                                                                                                                                                                                                                        | false                                  |
| LabelKeys            | Comma separated list of keys to use as stream labels. All other keys will be placed into the log line. LabelKeys is deactivated when using `LabelMapPath` label mapping configuration.                                                                                                                                                                                                  | none                                   |
| LineFormat           | Format to use when flattening the record to a log line. Valid values are `json` or `key_value`. If set to `json` the log line sent to Loki will be the fluentd record (excluding any keys extracted out as labels) dumped as json. If set to `key_value`, the log line will be each item in the record concatenated together (separated by a single space) in the format &lt;key&gt;=&lt;value&gt;. | json                                   |
| DropSingleKey        | If set to true and after extracting label_keys a record only has a single key remaining, the log line sent to Loki will just be the value of the record key.                                                                                                                                                                                                                            | true                                   |
| LabelMapPath         | Path to a json file defining how to transform nested records.                                                                                                                                                                                                                                                                                                                           | none                                   |
| Buffer               | Enable buffering mechanism.                                                                                                                                                                                                                                                                                                                                                              | false                                  |
| BufferType           | Specify the buffering mechanism to use (currently only `dque` is implemented).                                                                                                                                                                                                                                                                                                            | dque                                   |
| DqueDir              | Path to the directory for queued logs.                                                                                                                                                                                                                                                                                                                                                   | /tmp/flb-storage/loki                  |
| DqueSegmentSize      | Segment size in terms of number of records per segment.                                                                                                                                                                                                                                                                                                                                  | 500                                    |
| DqueSync             | Whether to fsync each queue change. Specify no fsync with `normal`, and fsync with `full`.                                                                                                                                                                                                                                                                                                                                                      | `normal`                                  |
| DqueName             | Queue name, must be unique per output.                                                                                                                                                                                                                                                                                                                                                     | dque                                   |
| ca_file              | Path to a CA certificate file to use when verifying the Loki server's TLS certificate.                                                                                                                                                                                                                                                                                                    | none                                    |
| cert_file            | Path to a TLS client certificate file to use for mutual TLS authentication with the Loki server.                                                                                                                                                                                                                                                                                          | none                                    |
| key_file             | Path to the TLS private key file that corresponds to `cert_file`.                                                                                                                                                                                                                                                                                                                          | none                                    |
| insecure_skip_verify | If set to `true`, skip verification of the Loki server's TLS certificate. Not recommended for production use.                                                                                                                                                                                                                                                                             | false                                   |

### Labels

Labels, for example `{container_name="nginx", cluster="us-west1"}`, are used to [query logs](https://grafana.com/docs/loki/<LOKI_VERSION>/query/).  Labels are usually metadata about the workload producing the log stream (`instance`, `container_name`, `region`, `cluster`, `level`). In Loki labels are indexed, so you should be cautious when choosing them. High cardinality label values can have drastic impact on query performance.

You can use the config parameters `Labels`, `RemoveKeys` , `LabelKeys` and `LabelMapPath` to instruct the output plugin how to perform labels extraction from your log entries or to add static labels to all log entries.

### AutoKubernetesLabels

If set to `true`, `AutoKubernetesLabels` will add all Kubernetes labels to Loki labels automatically and ignore parameters `LabelKeys`, `LabelMapPath`.

### LabelMapPath

When using the `Parser` and `Filter` plugins Fluent Bit can extract and add data to the current record/log data. While Loki labels are key value pairs, record data can be nested structures.
You can pass a JSON file that defines how to extract labels from each record. Each JSON key from the file will be matched with the log record to find label values. Values from the configuration are used as label names.

Considering the record below :

```json
{
  "kubernetes": {
    "container_name": "myapp",
    "pod_name": "myapp-xxx",
    "namespace_name": "prod",
    "labels" : {
        "team": "x-men"
    }
  },
  "HOSTNAME": "docker-desktop",
  "log" : "a log line",
  "time": "20190926T152206Z"
}
```

and a LabelMap file as follows :

```json
{
  "kubernetes": {
    "container_name": "container",
    "pod_name": "pod",
    "namespace_name": "namespace",
    "labels" : {
        "team": "team"
    }
  }
}
```

The labels extracted will be `{team="x-men", container="myapp", pod="myapp-xxx", namespace="prod"}`.

If you don't want the `kubernetes` and `HOSTNAME` fields to appear in the log line you can use the `RemoveKeys` configuration field. For example, `RemoveKeys kubernetes,HOSTNAME`.

### Buffering

Buffering refers to the ability to store the records somewhere, and while they are processed and delivered, still be able to continue storing more records. The Loki output plugin can be blocked by the Loki client because of its design:

- If the BatchSize is over the limit, the output plugin pauses receiving new records until the pending batch is successfully sent to the server
- If the Loki server is unreachable (retry 429s, 500s and connection-level errors), the output plugin blocks new records until the Loki server is available again, and the pending batch is successfully sent to the server or as long as the maximum number of attempts has been reached within configured back-off mechanism

The blocking state with some of the input plugins is not acceptable, because it can have an undesirable side effect on the part that generates the logs. Fluent Bit implements a buffering mechanism that is based on parallel processing. Therefore, it cannot send logs in order. There are two ways of handling the out-of-order logs: 

- Configure Loki to [accept out-of-order writes](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#accept-out-of-order-writes).

- Configure the Loki output plugin to use the buffering mechanism based on [`dque`](https://github.com/joncrlsn/dque), which is compatible with the Loki server strict time ordering:

    ```properties
    [Output]
        Name grafana-loki
        Match *
        Url http://localhost:3100/loki/api/v1/push
        Buffer true
        DqueSegmentSize 8096
        DqueDir /tmp/flb-storage/buffer
        DqueName loki.0
    ```

### Configuration examples

To configure the Loki output plugin add this section to your fluent-bit.conf file.

```properties
[Output]
    Name grafana-loki
    Match *
    Url http://localhost:3100/loki/api/v1/push
    BatchWait 1s
    BatchSize 30720
    # (30KiB)
    Labels {test="fluent-bit-go", lang="Golang"}
    RemoveKeys key1,key2
    LabelKeys key3,key4
    LineFormat key_value
```

```properties
[Output]
    Name grafana-loki
    Match *
    Url http://localhost:3100/loki/api/v1/push
    BatchWait 1s
    BatchSize 30720 # (30KiB)
    AutoKubernetesLabels true
    RemoveKeys key1,key2
```

A full [example configuration file](https://github.com/grafana/loki/blob/main/clients/cmd/fluent-bit/fluent-bit.conf) is also available in the Loki repository.

### Running multiple plugin instances

You can run multiple plugin instances in the same fluent-bit process, for example if you want to push to different Loki servers or route logs into different Loki tenant IDs. To do so, add additional `[Output]` sections.

# Sending logs to Loki using Fluent Bit tutorial

<!-- INTERACTIVE page intro.md START -->

# Sending logs to Loki using Fluent Bit tutorial

In this tutorial, you will learn how to send logs to Loki using Fluent Bit. Fluent Bit is a lightweight and fast log processor and forwarder that can collect, process, and deliver logs to various destinations. We will use the official Fluent Bit Loki output plugin to send logs to Loki.

<!-- INTERACTIVE ignore START -->

## Dependencies

Before you begin, ensure you have the following to run the demo:

- Docker
- Docker Compose

{{< admonition type="tip" >}}
Alternatively, you can try out this example in our interactive learning environment: [Sending logs to Loki using Fluent Bit tutorial](https://killercoda.com/grafana-labs/course/loki/fluentbit-loki-tutorial).

It's a fully configured environment with all the dependencies already installed.

![Interactive](/media/docs/loki/loki-ile.svg)

Provide feedback, report bugs, and raise issues in the [Grafana Killercoda repository](https://github.com/grafana/killercoda).
{{< /admonition >}}

<!-- INTERACTIVE ignore END -->

## Scenario

In this scenario, we have a microservices application called the Carnivorous Greenhouse. This application consists of the following services:

- **User Service:** Manages user data and authentication for the application. Such as creating users and logging in.
- **Plant Service:** Manages the creation of new plants and updates other services when a new plant is created.
- **Simulation Service:** Generates sensor data for each plant.
- **Websocket Service:** Manages the websocket connections for the application.
- **Bug Service:** A service that when enabled, randomly causes services to fail and generate additional logs.
- **Main App:** The main application that ties all the services together.
- **Database:** A database that stores user and plant data.

Each service has been instrumented with the Fluent Bit logging framework to generate logs. If you would like to learn more about how the Carnivorous Greenhouse application was instrumented with Fluent Bit, refer to the [Carnivorous Greenhouse repository](https://github.com/grafana/loki-fundamentals/blob/fluentbit-official/greenhouse/loggingfw.py).

<!-- INTERACTIVE page intro.md END -->

<!-- INTERACTIVE page step1.md START -->

## Step 1: Environment setup

In this step, we will set up our environment by cloning the repository that contains our demo application and spinning up our observability stack using Docker Compose.

1. To get started, clone the repository that contains our demo application:

    ```bash
    git clone -b fluentbit-official  https://github.com/grafana/loki-fundamentals.git
    ```

1. Next we will spin up our observability stack using Docker Compose:

    ```bash
    docker compose -f loki-fundamentals/docker-compose.yml up -d
    ```

    This will spin up the following services:

    ```console
    ✔ Container loki-fundamentals-grafana-1       Started                                                        
    ✔ Container loki-fundamentals-loki-1          Started                        
    ✔ Container loki-fundamentals-fluent-bit-1    Started
    ```

Once we have finished configuring the Fluent Bit agent and sending logs to Loki, we will be able to view the logs in Grafana. To check if Grafana is up and running, navigate to the following URL: [http://localhost:3000](http://localhost:3000)
<!-- INTERACTIVE page step1.md END -->

<!-- INTERACTIVE page step2.md START -->

## Step 2: Configure Fluent Bit to send logs to Loki

To configure Fluent Bit to receive logs from our application, we need to provide a configuration file. This configuration file will define the components and their relationships. We will build the entire observability pipeline within this configuration file.

### Open your code editor and locate the `fluent-bit.conf` file

Fluent Bit requires a configuration file to define the components and their relationships. The configuration file is written using Fluent Bit configuration syntax. We will build the entire observability pipeline within this configuration file. To start, we will open the `fluent-bit.conf` file in the code editor:

{{< docs/ignore >}}
> Note: Killercoda has an inbuilt Code editor which can be accessed via the `Editor` tab.

1. Expand the `loki-fundamentals` directory in the file explorer of the `Editor` tab.
1. Locate the `fluent-bit.conf` file in the top level directory, `loki-fundamentals`.
1. Click on the `fluent-bit.conf` file to open it in the code editor.
{{< /docs/ignore >}}

<!-- INTERACTIVE ignore START -->
1. Open the `loki-fundamentals` directory in a code editor of your choice.
1. Locate the `fluent-bit.conf` file in the `loki-fundamentals` directory (Top level directory).
1. Click on the `fluent-bit.conf` file to open it in the code editor.
<!-- INTERACTIVE ignore END -->

You will copy all of the configuration snippets into the `fluent-bit.conf` file.

### Receiving Fluent Bit protocol logs

The first step is to configure Fluent Bit to receive logs from the Carnivorous Greenhouse application. Since the application is instrumented with Fluent Bit logging framework, it will send logs using the forward protocol (unique to Fluent Bit). We will use the `forward` input plugin to receive logs from the application.

Now add the following configuration to the `fluent-bit.conf` file:

```conf
[INPUT]
    Name              forward
    Listen            0.0.0.0
    Port              24224
```

In this configuration:

- `Name`: The name of the input plugin. In this case, we are using the `forward` input plugin.
- `Listen`: The IP address to listen on. In this case, we are listening on all IP addresses.
- `Port`: The port to listen on. In this case, we are listening on port `24224`.

For more information on the `forward` input plugin, see the [Fluent Bit Forward documentation](https://docs.fluentbit.io/manual/pipeline/inputs/forward).

### Export logs to Loki using the official Loki output plugin

Lastly, we will configure Fluent Bit to export logs to Loki using the official Loki output plugin. The Loki output plugin allows you to send logs or events to a Loki service. It supports data enrichment with Kubernetes labels, custom label keys, and structured metadata.

Add the following configuration to the `fluent-bit.conf` file:

```conf
[OUTPUT]
    name   loki
    match  service.**
    host   loki
    port   3100
    labels agent=fluent-bit
    label_map_path /fluent-bit/etc/conf/logmap.json
```

In this configuration:

- `name`: The name of the output plugin. In this case, we are using the `loki` output plugin.
- `match`: The tag to match. In this case, we are matching all logs with the tag `service.**`.
- `host`: The hostname of the Loki service. In this case, we are using the hostname `loki`.
- `port`: The port of the Loki service. In this case, we are using port `3100`.
- `labels`: Additional labels to add to the logs. In this case, we are adding the label `agent=fluent-bit`.
- `label_map_path`: The path to the label map file. In this case, we are using the file `logmap.json`.

For more information on the `loki` output plugin, see the [Fluent Bit Loki documentation](https://docs.fluentbit.io/manual/pipeline/outputs/loki).

#### `logmap.json` file

The `logmap.json` file is used to map the log fields to the Loki labels. In this tutorial we have pre-filled the `logmap.json` file with the following configuration:

```json
{
"service": "service_name",
"instance_id": "instance_id"
 }
```

This configuration maps the `service` field to the Loki label `service_name` and the `instance_id` field to the Loki label `instance_id`.

### Reload the Fluent Bit configuration

After adding the configuration to the `fluent-bit.conf` file, you will need to reload the Fluent Bit configuration. To reload the configuration, run the following command:

```bash
docker restart loki-fundamentals-fluent-bit-1
```

To verify that the configuration has been loaded successfully, you can check the Fluent Bit logs by running the following command:

```bash
docker logs loki-fundamentals-fluent-bit-1
```

## Stuck? Need help?

If you get stuck or need help creating the configuration, you can copy and replace the entire `config.alloy` using the completed configuration file:

```bash
cp loki-fundamentals/completed/fluent-bit.conf loki-fundamentals/fluent-bit.conf
docker restart loki-fundamentals-fluent-bit-1
```

<!-- INTERACTIVE page step2.md END -->

<!-- INTERACTIVE page step3.md START -->

## Step 3: Start the Carnivorous Greenhouse

In this step, we will start the Carnivorous Greenhouse application. To start the application, run the following command:
<!-- INTERACTIVE ignore START -->
{{< admonition type="note" >}}
This docker-compose file relies on the `loki-fundamentals_loki` Docker network. If you have not started the observability stack, you will need to start it first.
{{< /admonition >}}
<!-- INTERACTIVE ignore END -->

{{< docs/ignore >}}

> Note: This docker-compose file relies on the `loki-fundamentals_loki` docker network. If you have not started the observability stack, you will need to start it first.

{{< /docs/ignore >}}

```bash
docker compose -f loki-fundamentals/greenhouse/docker-compose-micro.yml up -d --build 
```

This will start the following services:

```bash
 ✔ Container greenhouse-db-1                 Started                                                         
 ✔ Container greenhouse-websocket_service-1  Started 
 ✔ Container greenhouse-bug_service-1        Started
 ✔ Container greenhouse-user_service-1       Started
 ✔ Container greenhouse-plant_service-1      Started
 ✔ Container greenhouse-simulation_service-1 Started
 ✔ Container greenhouse-main_app-1           Started
```

Once started, you can access the Carnivorous Greenhouse application at [http://localhost:5005](http://localhost:5005). Generate some logs by interacting with the application in the following ways:

1. Create a user.
1. Log in.
1. Create a few plants to monitor.
1. Enable bug mode to activate the bug service. This will cause services to fail and generate additional logs.

Finally to view the logs in Loki, navigate to the Loki Logs Explore view in Grafana at [http://localhost:3000/a/grafana-lokiexplore-app/explore](http://localhost:3000/a/grafana-lokiexplore-app/explore).

<!-- INTERACTIVE page step3.md END -->

<!-- INTERACTIVE page finish.md START -->

## Summary

In this tutorial, you learned how to send logs to Loki using Fluent Bit. You configured Fluent Bit to receive logs from the Carnivorous Greenhouse application and export logs to Loki using the official Loki output plugin. Where to next?

{{< docs/ignore >}}

### Back to Docs

Head back to where you started from to continue with the [Loki documentation](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/alloy).

{{< /docs/ignore >}}

## Further reading

For more information on Fluent Bit, refer to the following resources:

- [Fluent Bit documentation](https://docs.fluentbit.io/manual/)
- [Other examples of Fluent Bit configurations](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/fluentbit/)

## Complete metrics, logs, traces, and profiling example

If you would like to use a demo that includes Mimir, Loki, Tempo, and Grafana, you can use [Introduction to Metrics, Logs, Traces, and Profiling in Grafana](https://github.com/grafana/intro-to-mlt). `Intro-to-mltp` provides a self-contained environment for learning about Mimir, Loki, Tempo, and Grafana.

The project includes detailed explanations of each component and annotated configurations for a single-instance deployment. Data from `intro-to-mltp` can also be pushed to Grafana Cloud.

<!-- INTERACTIVE page finish.md END -->

# Fluent Bit Loki output plugin

[Fluent Bit](https://fluentbit.io/) is a fast and lightweight logs and metrics processor and forwarder that can be configured with the [Fluent-bit Loki output plugin](https://docs.fluentbit.io/manual/pipeline/outputs/loki) to ship logs to Loki. 

You can define which log files you want to collect using the [`Tail`](https://docs.fluentbit.io/manual/pipeline/inputs/tail) or [`Stdin`](https://docs.fluentbit.io/manual/pipeline/inputs/standard-input) data pipeline input. Additionally, Fluent Bit supports multiple `Filter` and `Parser` plugins (`Kubernetes`, `JSON`, etc.) to structure and alter log lines.

{{< admonition type="note" >}}
There are two Fluent Bit plugins for Loki: the officially maintained plugin `loki` and the `grafana-loki` plugin. We recommend using the `loki` plugin described within this page as it's officially maintained by the Fluent Bit project. 

For more information, see the [Fluent Bit Loki output plugin documentation](https://docs.fluentbit.io/manual/pipeline/outputs/loki).  Note that the `grafana-loki` plugin is no longer actively maintained.
{{< /admonition >}}

## Configuration

All configuration options for the Fluent Bit Loki output plugin are documented in the [Fluent Bit Loki output plugin documentation](https://docs.fluentbit.io/manual/pipeline/outputs/loki#configuration-parameters).

Here is a generic example for connecting Fluent Bit to Loki hosted on Grafana Cloud:

```conf
    [OUTPUT]
        Name        loki
        Match       *
        Host        YourHostname.company.com
        port        443
        tls         on
        tls.verify  on
        http_user   XXX
        http_passwd XXX
```

Replace `Host`, `http_user`, and `http_passwd` with your Grafana Cloud Loki endpoint and credentials.


## Usage examples

Here are some examples of how to use Fluent Bit to send logs to Loki.

### Tail Docker logs

Here is an example to run Fluent Bit in a Docker container, collect Docker logs, and send them to a local Loki instance. 

```bash
docker run -v /var/lib/docker/containers:/var/lib/docker/containers fluent/fluent-bit:latest /fluent-bit/bin/fluent-bit -i tail -p Path="/var/lib/docker/containers/*/*.log" -p Parser=docker -p Tag="docker.*"  -o loki -p host=loki -p port=3100 -p labels="agent=fluent-bit,env=docker"
```

In this example, we are using the `tail` input plugin to collect Docker logs and the `loki` output plugin to send logs to Loki. Note it is recommended to use a configuration file to define the input and output plugins. The `-p` flag is used to pass configuration parameters to the plugins.

#### Configuration file (Alternative to command line arguments)

Create a configuration file `fluent-bit.conf` with the following content:

```conf
[INPUT]
    Name   tail
    Path   /var/lib/docker/containers/*/*.log
    Parser docker
    Tag    docker.*

[OUTPUT]
    Name   loki
    Match  *
    Host   loki
    Port   3100
    Labels agent=fluent-bit,env=docker
```

Run Fluent Bit with the configuration file:

```bash
docker run -v /var/lib/docker/containers:/var/lib/docker/containers -v $(pwd)/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf fluent/fluent-bit:latest /fluent-bit/bin/fluent-bit -c /fluent-bit/etc/fluent-bit.conf
```

### Collect Docker events

Here is an example to run Fluent Bit in a Docker container, collect docker events, and send them to a local Loki instance. 

```bash
docker run -v /var/run/docker.sock:/var/run/docker.sock fluent/fluent-bit:latest /fluent-bit/bin/fluent-bit -i docker_events -o loki -p host=loki -p port=3100 -p labels="agent=fluent-bit,env=docker"
```

In this example, we are using the `docker_events` input plugin to collect Docker events and the `loki` output plugin to send logs to Loki. Note it is recommended to use a configuration file to define the input and output plugins. The `-p` flag is used to pass configuration parameters to the plugins.

#### Configuration file (Alternative to command line arguments)

Create a configuration file `fluent-bit.conf` with the following content:

```conf
[INPUT]
    Name   docker_events

[OUTPUT]
    Name   loki
    Match  *
    Host   loki
    Port   3100
    Labels agent=fluent-bit,env=docker
```

Run Fluent Bit with the configuration file:

```bash
docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd)/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf fluent/fluent-bit:latest /fluent-bit/bin/fluent-bit -c /fluent-bit/etc/fluent-bit.conf
```

### Collect Kubernetes logs

The recommended way to collect logs from Kubernetes with Fluent Bit is to use the Helm chart provided by the Fluent Bit project. The Helm chart is available at [https://github.com/fluent/helm-charts](https://github.com/fluent/helm-charts).

Here is an example of how to deploy the Fluent Bit Helm chart to collect logs from Kubernetes and send them to Loki:

1. Add the Fluent Bit Helm repository:

   ```bash
   helm repo add fluent https://fluent.github.io/helm-charts
   ```

1. Create a `values.yaml` file with the following content:

   ```yaml
   config:
       outputs: |
           [OUTPUT]
               Name        loki
               Match       *
               Host        YourHost.Company.net
               port        443
               tls         on
               tls.verify  on
               http_user   XXX
               http_passwd XXX
               Labels agent=fluent-bit
   ```

   Note we are only updating the `outputs` section of the Fluent Bit configuration. This is to replace the default output plugin with the Loki output plugin. If you need to update other parts of the Fluent Bit configuration refer to the [Fluent Bit values file reference](https://github.com/fluent/helm-charts/blob/main/charts/fluent-bit/values.yaml).

1. Deploy the Fluent Bit Helm chart:

   ```bash
   helm install fluent-bit fluent/fluent-bit -f values.yaml
   ```

## Next steps

- [Sending logs to Loki using Fluent Bit tutorial](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/fluentbit/fluent-bit-loki-tutorial/)

# Generating log data for testing

You can use k6 to generate log data for load testing.

## Using `pushParameterized`

Push logs to Loki with `pushParameterized`.
This method generates batches of streams in a random fashion.
This method requires three arguments:

| name | description |
| ---- | ----------- |
| `streams` | number of streams per batch |
| `minSize` | minimum batch size in bytes |
| `maxSize` | maximum batch size in bytes |

**JavaScript example code fragment:**

```javascript
import loki from 'k6/x/loki';

const KB = 1024;
const MB = KB * KB;

const conf = loki.Config("http://localhost:3100");
const client = loki.Client(conf);

export default () => {
   client.pushParameterized(2, 500 * KB, 1 * MB);
};
```

### Argument `streams`

The first argument of the method is the desired amount of streams per batch.
Instead of using a fixed amount of streams in each call, you can randomize the
value to simulate a more realistic scenario.

**JavaScript example code fragment:**

```javascript
function randomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
};

export default () => {
   let streams = randomInt(2, 8);
   client.pushParameterized(streams, 500 * KB, 1 * MB);
}
```

### Arguments `minSize` and `maxSize`

The second and third argument of the method take the lower and upper bound of
the batch size. The resulting batch size is a random value between the two
arguments. This mimics the behavior of a log client, such as Grafana Alloy,
where logs are buffered and pushed once a certain batch size
is reached or after a certain size when no logs have been received.

The batch size is not equal to the payload size, as the batch size only counts
bytes of the raw logs. The payload may be compressed when Protobuf encoding
is used.

## Log format

`xk6-loki` can emit log lines in seven distinct formats. The label `format` of
a stream defines the format of its log lines.

* Apache common (`apache_common`)
* Apache combined (`apache_combined`)
* Apache error (`apache_error`)
* [BSD syslog](https://datatracker.ietf.org/doc/html/rfc3164) (`rfc3164`)
* [Syslog](https://datatracker.ietf.org/doc/html/rfc5424) (`rfc5424`)
* JSON (`json`)
* [logfmt](https://pkg.go.dev/github.com/kr/logfmt) (`logfmt`)

Under the hood, the extension uses a fork the library
[flog](https://github.com/mingrammer/flog) for generating log lines.

## Labels

`xk6-loki` uses the following label names for generating streams:

| name      | type     | cardinality     |
| --------- | -------- | --------------- |
| instance  | fixed    | 1 per k6 worker |
| format    | fixed    | 7               |
| os        | fixed    | 3               |
| namespace | variable | >100            |
| app       | variable | >100            |
| pod       | variable | >100            |
| language  | variable | >100            |
| word      | variable | >100            |

By default, variable labels are not used.
However, you can specify the
cardinality (quantity of distinct label values) using the `cardinality` argument
in the `Config` constructor.

**JavaScript example code fragment:**
```javascript
import loki from 'k6/x/loki';

const cardinality = {
   "app": 1,
   "namespace": 2,
   "language": 2,
   "pod": 5,
};
const conf = loki.Config("http://localhost:3100", 5000, 1.0, cardinality);
const client = loki.Client(conf);
```

The total quantity of distinct streams is defined by the cartesian product of
all label values. Keep in mind that high cardinality negatively impacts the performance of
the Loki instance.

## Payload encoding

Loki accepts two kinds of push payload encodings: JSON and Protobuf.
While JSON is easier for humans to read,
Protobuf is optimized for performance
and should be preferred when possible.

To define the ratio of Protobuf to JSON requests, the client
configuration accepts values of 0.0 to 1.0.
0.0 means 100% JSON encoding, and 1.0 means 100% Protobuf encoding.

The default value is 0.9.

**JavaScript example code fragment:**
```javascript
import loki from 'k6/x/loki';

const ratio = 0.8; // 80% Protobuf, 20% JSON
const conf = loki.Config("http://localhost:3100", 5000, ratio);
const client = loki.Client(conf);
```

# Use k6 to load test log queries

When designing a test scenario for load testing the read path of a Loki
installation, it is important to know what types of queries you expect.

## Supported query types

Loki has 5 types of queries:

* instant query
* range query
* labels query
* label values query
* series query

In a real-world use-case, such as querying Loki using it as a Grafana
data source, all of these queries are used. Each of them has a different
[API](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/loki-http-api/) endpoint. The xk6-loki extension
provides a [JavaScript API](https://github.com/grafana/xk6-loki#javascript-api)
for all these query types.


### Instant query

Instant queries can be executed using the function `instantQuery(query, limit)`
on the `loki.Client` instance:

**JavaScript example code fragment:**

```javascript
export default () => {
  client.instantQuery(`rate({app="my-app-name"} | logfmt | level="error" [5m])`);
}
```

### Range query

Range queries can be executed using the function `rangeQuery(query, duration, limit)`
on the `loki.Client` instance:

**JavaScript example code fragment:**

```javascript
export default () => {
  client.rangeQuery(`{app="my-app-name"} | logfmt | level="error"`, "15m");
}
```

### Labels query

Labels queries can be executed using the function `labelsQuery(duration)`
on the `loki.Client` instance:

**JavaScript example code fragment:**

```javascript
export default () => {
  client.labelsQuery("10m");
}
```

### Label values query

Label values queries can be executed using the function `labelValuesQuery(label, duration)`
on the `loki.Client` instance:

**JavaScript example code fragment:**

```javascript
export default () => {
  client.labelValuesQuery("app", "10m");
}
```

### Series query

Series queries can be executed using the function `seriesQuery(matcher, range)`
on the `loki.Client` instance:

**JavaScript example code fragment:**

```javascript
export default () => {
  client.seriesQuery(`match[]={app=~"loki-.*"}`, "10m");
}
```

## Metrics

The extension collects metrics that are printed in the
[end-of-test summary](https://grafana.com/docs/k6/latest/results-output/end-of-test/) in addition to the built-in metrics.
These metrics are collected only for instant and range queries.

| name                              | description                                  |
|-----------------------------------|----------------------------------------------|
| `loki_bytes_processed_per_second` | amount of bytes processed by Loki per second |
| `loki_bytes_processed_total`      | total amount of bytes processed by Loki      |
| `loki_lines_processed_per_second` | amount of lines processed by Loki per second |
| `loki_lines_processed_total`      | total amount of lines processed by Loki      |

## Labels pool

With the xk6-loki extension, you can use the field `labels` on the `Config`
object. It contains label names and values that are generated in a reproducible
manner. Use the same labels cardinality configuration for both `write` and
`read` testing.

**JavaScript example code fragment:**

```javascript
const labelCardinality = {
  "app": 5,
  "namespace": 2,
};
const conf = new loki.Config(BASE_URL, 10000, 1.0, labelCardinality);
const client = new loki.Client(conf);

function randomChoice(items) {
  return items[Math.floor(Math.random() * items.length)];
}

export default() {
  let app = randomChoice(conf.labels.app);
  let namespace = randomChoice(conf.labels.namespace);
  client.rangeQuery(`{app="${app}", namespace="${namespace}"} | logfmt | level="error"`, "15m");
}
```

Alternatively, you can define your own pool of label names and values,
and then randomly select labels from your pool instead of a generated pool. 

## JavaScript example

A more complex example of a read scenario can be found in xk6-loki repository.
The test file
[read-scenario.js](https://github.com/grafana/xk6-loki/blob/main/examples/read-scenario.js)
can be reused and extended for your needs.

It allows you to configure ratios for each type of query and the ratios of time
ranges.

**JavaScript example:**

```javascript
const queryTypeRatioConfig = [
  {
    ratio: 0.1,
    item: readLabels
  },
  {
    ratio: 0.15,
    item: readLabelValues
  },
  {
    ratio: 0.05,
    item: readSeries
  },
  {
    ratio: 0.5,
    item: readRange
  },
  {
    ratio: 0.2,
    item: readInstant
  },
];
```

This configuration would execute approximately

- 10% labels requests
- 15% label values requests
- 5% requests for series
- 50% range queries
- 20% instant queries

during a test run.

# Use k6 to load test the write path

There are multiple considerations when
load testing a Loki cluster's write path.

The most important consideration is the setup of the target cluster.
Keep these items in mind when setting up your load test for the target cluster.

- Deployment mode. The cluster might be deployed as
a single-binary, as a simple scalable deployment, or as microservices

- Quantity of component instances. This aids in predicting the need
to horizontally scale the quantity of component instances.

- Resource allocation such as CPU, memory, disk, and network.
This aids in predicting the need to vertically scale the
underlying hardware.

These parameters can be adjusted in the load test:

* The quantity of distinct labels and their cardinality

    This will define how many active streams your load test will generate.
    Start with a small number of label values,
    to keep the quantity of streams small enough,
    such that it does not overwhelm your cluster.

* The batch size the client sends

    The batch size indirectly controls how many log lines per push request are
    sent. The smaller the batch size, and the larger the quantity
    of active streams you have,
    the longer it takes before chunks are flushed.
    Keeping lots of chunks
    in the ingester increases memory consumption.

* The number of virtual users (VUs)

    VUs can be used to control the amount of parallelism with which logs should
    be pushed. Every VU runs its own loop of iterations.
    Therefore, the number of VUs has the most impact on
    the generated log throughput.
    Since generating logs is CPU-intensive, there is a threshold above which
    increasing the number VUs does not result in a higher amount of log data.
    A rule of thumb is that the
    most data can be generated when the number of VUs are set to 1-1.5 times
    the quantity of CPU cores available on the k6 worker machine.
    For example,
    set the value in the range of 8 to 12 for an 8-core machine.

* The way to run k6

    k6 supports three [execution modes](https://grafana.com/docs/k6/latest/get-started/running-k6/#execution-modes) to run a test: local, distributed, and cloud.
    Whereas running your k6 load test from a single (local
    or remote) machine is easy to set up and fine for smaller Loki clusters,
    the single machine does not load test large Loki installations,
    because it cannot create the data to saturate the write path.
    For larger tests, consider [these optimizations](https://grafana.com/docs/k6/latest/testing-guides/running-large-tests/), or run them in [Grafana Cloud k6](/products/cloud/k6) or a Kubernetes cluster with the [k6 Operator](https://github.com/grafana/k6-operator).

## Metrics

The extension collects two metrics that are printed in the
[end-of-test summary](https://grafana.com/docs/k6/latest/results-output/end-of-test/) in addition to the built-in metrics.

| name | description |
| ---- | ----------- |
| `loki_client_uncompressed_bytes` | the quantity of uncompressed log data pushed to Loki, in bytes |
| `loki_client_lines` | the number of log lines pushed to Loki |

## k6 value checks

An HTTP request that successfully pushes logs to Loki
responds with status `204 No Content`.
The status code should be checked explicitly with a [k6 check](https://grafana.com/docs/k6/latest/javascript-api/k6/check/).


## JavaScript example

```javascript
import { check, fail } from 'k6';
import loki from 'k6/x/loki';

/*
 * Host name with port
 * @constant {string}
 */
const HOST = "localhost:3100";

/**
 * Name of the Loki tenant
 * passed as X-Scope-OrgID header to requests.
 * If tenant is omitted, xk6-loki runs in multi-tenant mode,
 * and every VU will use its own ID.
 * @constant {string}
 */
const TENANT_ID = "my_org_id"

/**
 * URL used for push and query requests
 * Path is automatically appended by the client
 * @constant {string}
 */
const BASE_URL = `${TENANT_ID}@${HOST}`;

/**
 * Minimum amount of virtual users (VUs)
 * @constant {number}
 */
const MIN_VUS = 1

/**
 * Maximum amount of virtual users (VUs)
 * @constant {number}
 */
const MAX_VUS = 10;

/**
 * Constants for byte values
 * @constant {number}
 */
const KB = 1024;
const MB = KB * KB;

/**
 * Definition of test scenario
 */
export const options = {
  thresholds: {
    'http_req_failed': [{ threshold: 'rate<=0.01', abortOnFail: true }],
  },
  scenarios: {
    write: {
      executor: 'ramping-vus',
      exec: 'write',
      startVUs: MIN_VUS,
      stages: [
        { duration: '5m', target: MAX_VUS },
        { duration: '30m', target: MAX_VUS },
      ],
      gracefulRampDown: '1m',
    },
  },
};

const labelCardinality = {
  "app": 5,
  "namespace": 1,
};
const timeout = 10000; // 10s
const ratio = 0.9; // 90% Protobuf
const conf = new loki.Config(BASE_URL, timeout, ratio, labelCardinality);
const client = new loki.Client(conf);

/**
 * Entrypoint for write scenario
 */
export function write() {
  let streams = randomInt(4, 8);
  let res = client.pushParameterized(streams, 800 * KB, 1 * MB);
  check(res,
    {
      'successful write': (res) => {
        let success = res.status === 204;
        if (!success) console.log(res.status, res.body);
        return success;
      },
    }
  );
}

/**
 * Return a random integer between min and max including min and max
 */
function randomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}
```

# How is native OTLP endpoint different from Loki Exporter

## Introduction

OpenTelemetry (OTel) is quickly becoming an industry standard with increasing adoption. Prior to the Loki 3.0 release, there was no native support for ingesting OTel logs to Loki, which led to creation of the [LokiExporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/lokiexporter/README.md). While the LokiExporter got the job done of ingesting OTel logs to Loki, it did not provide a native user experience, and the querying experience was not optimal. As part of our effort to improve user experience with OTel, we added native OTel log ingestion support to Loki with 3.0 release.

{{< admonition type="note" >}}
The native OTLP endpoint is the recommended way to send logs to Loki. Grafana Labs is working to migrate existing customers from Loki Exporter to the native OTLP endpoint.
For Cloud users, Adaptive Logs may not produce meaningful patterns if OTel logs are sent to the Cloud Logs endpoint. They must be sent to the OTLP endpoint.
{{< /admonition >}}

## What has changed?

### Formatting of logs

[LokiExporter's README](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/lokiexporter/README.md) explains in detail the formatting of OTel logs ingested via LokiExporter, while Loki’s native OTel log ingestion endpoint is described in detail in its [documentation](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/otel/). Here is a summary of how the logs are formatted between the two:

**LokiExporter**

- Index Labels:
  - It supports label control via hints set by the OTel client/collector.
  - Default labels:
    - job=service.namespace/service.name
    - instance=service.instance.id
    - exporter=OTLP
    - level=severity
- Log Body: Encodes log records and attributes in json(default) or logfmt format.

**Loki’s native OTel log ingestion endpoint**

- Index Labels:
  - It supports label control via per-tenant OTLP configuration in Loki.
  - By default, it picks some pre-configured resource attributes as index labels as explained [here](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/otel/#format-considerations).
- LogLine: Stringified LogRecord.Body.
- Structured Metadata: Anything not stored in Index labels and LogLine gets stored as [Structured Metadata](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/structured-metadata/).

### Sample Log

Let us take the following sample OTel log and see how it would look after ingestion in both formats with the default configuration:
- Resource Attributes:
  - service.name: "auth"
  - service.namespace: "dev"
  - service.kind: "app"
- Log Record:
  - Timestamp: 1715247552000000000
  - Body: "user logged in"
  - Severity Text: "INFO"
  - Attributes:
    - email: "foo@bar.com"

**Ingested with LokiExporter:**

- Index Labels:
  - job="dev/auth"
  - exporter="OTLP"
- Log Body: `{"body":"user logged in","severity":"INFO","attributes":{"email":"foo@bar.com"}, "resources":{"service.kind":"app","service.name":"auth","service.namespace":"dev"}}`

**Ingested with Loki’s native OTel endpoint:**

- Index Labels:
  - service_name="auth"
  - service_namespace="dev"
- Log Body: "user logged in"
- Structured Metadata:
  - service_kind: "app"
  - severity_text: "INFO"
  - email: "foo@bar.com"

### Querying experience

As seen earlier, LokiExporter encodes data to json or logfmt blob, which requires decoding it at query time to interact with OTel attributes.
However, the native OTel endpoint doesn't do any encoding of data before storing it. It leverages [Structured Metadata](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/structured-metadata/), which makes it easier to interact with OTel attributes without having to use any parsers in the queries.
Taking the above-ingested log line, let us look at how the querying experience would look between the two considering various scenarios:

**Query all logs without any filters**

- Ingested with LokiExporter: `{job="dev/auth"}`
- Ingested with Loki’s native OTel endpoint: `{service_name="auth", service_namespace="dev"}`

**Query logs with severity INFO**

- Ingested with LokiExporter: `{job="dev/auth"} | json | severity="INFO"`
- Ingested with Loki’s native OTel endpoint: `{service_name="auth", service_namespace="dev"} | severity_text="INFO"`

**Display log message as log line in logs query results**

- Ingested with LokiExporter: `{job="dev/auth"} | json | line_format "{{.body}}"`
- Ingested with Loki’s native OTel endpoint: `{service_name="auth", service_namespace="dev"}`

**Count login events in the last hour by email**

- Ingested with LokiExporter: `sum(count_over_time({job="dev/auth"} |= "user logged in" | json[1h])) by (email)`
- Ingested with Loki’s native OTel endpoint: `sum(count_over_time({service_name="auth", service_namespace="dev"} |= "user logged in"[1h])) by (email)`

## Benefits of switching from LokiExporter to native OTel endpoint

- **Future improvements:** There is an ongoing discussion on deprecating LokiExporter, which would stop receiving future enhancements. Loki’s native OTel endpoint represents the future of our product, and all future development and enhancements will be focused there. Upgrading to the native endpoint will ensure you benefit from the latest enhancements and the best possible user experience.
- **Simplified client config:** LokiExporter requires setting hints for managing stream labels, which defeats the purpose of choosing OTel in the first place since you can’t switch from one OTel-compatible storage to another without having to reconfigure your clients. With Loki’s native OTel endpoint, there is no added complexity in setting hints for your client to manage the labels.
- **Simplified querying:** Loki’s native OTel endpoint leverages Structured Metadata, which makes it easier to reference the attributes and other fields from the OTel LogRecord without having to use json or logfmt parsers for decoding the data at query time.
- **More modern high context data model:** Better aligned with modern observability practices than the older json based model.

## What do you need to do to switch from LokiExporter to native OTel ingestion format?

- Point your OpenTelemetry Collector to the Loki native OTel ingestion endpoint as explained [here](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/otel/#loki-configuration).
- Rewrite your LogQL queries in various places, including dashboards, alerts, starred queries in Grafana Explore, etc. to query OTel logs as per the new format.

# Getting started with the OpenTelemetry Collector and Loki tutorial

<!-- vale Grafana.We = NO -->
<!-- INTERACTIVE page intro.md START -->

# Getting started with the OpenTelemetry Collector and Loki tutorial

The OpenTelemetry Collector offers a vendor-agnostic implementation of how to receive, process, and export telemetry data. With the introduction of the OTLP endpoint in Loki, you can now send logs from applications instrumented with OpenTelemetry to Loki using the OpenTelemetry Collector in native OTLP format.
In this example, we will teach you how to configure the OpenTelemetry Collector to receive logs in the OpenTelemetry format and send them to Loki using the OTLP HTTP protocol. This will involve configuring the following components in the OpenTelemetry Collector:

- **OpenTelemetry Receiver:** This component will receive logs in the OpenTelemetry format via HTTP and gRPC.
- **OpenTelemetry Processor:** This component will accept telemetry data from other `otelcol.*` components and place them into batches. Batching improves the compression of data and reduces the number of outgoing network requests required to transmit data.
- **OpenTelemetry Exporter:** This component will accept telemetry data from other `otelcol.*` components and write them over the network using the OTLP HTTP protocol. We will use this exporter to send the logs to the Loki native OTLP endpoint.

<!-- INTERACTIVE ignore START -->

## Dependencies

Before you begin, ensure you have the following to run the demo:

- Docker
- Docker Compose

{{< admonition type="tip" >}}
Alternatively, you can try out this example in our interactive learning environment: [Getting started with the OpenTelemetry Collector and Loki tutorial](https://killercoda.com/grafana-labs/course/loki/otel-collector-getting-started).

It's a fully configured environment with all the dependencies already installed.

![Interactive](/media/docs/loki/loki-ile.svg)

Provide feedback, report bugs, and raise issues for the tutorial in the [Grafana Killercoda repository](https://github.com/grafana/killercoda).
{{< /admonition >}}

<!-- INTERACTIVE ignore END -->

## Scenario

In this scenario, we have a microservices application called the Carnivorous Greenhouse. This application consists of the following services:

- **User Service:** Manages user data and authentication for the application. Such as creating users and logging in.
- **Plant Service:** Manages the creation of new plants and updates other services when a new plant is created.
- **Simulation Service:** Generates sensor data for each plant.
- **Websocket Service:** Manages the websocket connections for the application.
- **Bug Service:** A service that when enabled, randomly causes services to fail and generate additional logs.
- **Main App:** The main application that ties all the services together.
- **Database:** A database that stores user and plant data.

Each service generates logs using the OpenTelemetry SDK and exports to the OpenTelemetry Collector in the OpenTelemetry format (OTLP). The Collector then ingests the logs and sends them to Loki.

<!-- INTERACTIVE page intro.md END -->

<!-- INTERACTIVE page step1.md START -->

## Step 1: Environment setup

In this step, we will set up our environment by cloning the repository that contains our demo application and spinning up our observability stack using Docker Compose.

1. To get started, clone the repository that contains our demo application:
    <!-- INTERACTIVE exec START -->
    ```bash
    git clone -b microservice-otel-collector  https://github.com/grafana/loki-fundamentals.git
    ```
    <!-- INTERACTIVE exec END -->
1. Next we will spin up our observability stack using Docker Compose:

    <!-- INTERACTIVE ignore START -->
    ```bash
    docker compose -f loki-fundamentals/docker-compose.yml up -d
    ```
    <!-- INTERACTIVE ignore END -->

    {{< docs/ignore >}}

    <!-- INTERACTIVE exec START -->
    ```bash
    docker-compose -f loki-fundamentals/docker-compose.yml up -d 
    ```
    <!-- INTERACTIVE exec END -->

    {{< /docs/ignore >}}

    To check the status of services we can run the following command:

    ```bash
    docker ps -a
    ```
    <!-- INTERACTIVE ignore START -->
    {{< admonition type="note" >}}
    The OpenTelemetry Collector container will show as `Stopped` or `Exited (1) About a minute ago`. This is expected as we have provided an empty configuration file. We will update this file in the next step.
    {{< /admonition >}}
    <!-- INTERACTIVE ignore END -->

After we've finished configuring the OpenTelemetry Collector and sending logs to Loki, we will be able to view the logs in Grafana. To check if Grafana is up and running, navigate to the following URL: [http://localhost:3000](http://localhost:3000)
<!-- INTERACTIVE page step1.md END -->

<!-- INTERACTIVE page step2.md START -->

## Step 2: Configuring the OpenTelemetry Collector

To configure the Collector to ingest OpenTelemetry logs from our application, we need to provide a configuration file. This configuration file will define the components and their relationships. We will build the entire observability pipeline within this configuration file.

### Open your code editor and locate the `otel-config.yaml` file

The configuration file is written using **YAML** configuration syntax. To start, we will open the `otel-config.yaml` file in the code editor:

{{< docs/ignore >}}
**Note: Killercoda has an inbuilt Code editor which can be accessed via the `Editor` tab.**

1. Expand the `loki-fundamentals` directory in the file explorer of the `Editor` tab.
2. Locate the `otel-config.yaml` file in the top level directory, `loki-fundamentals`.
3. Click on the `otel-config.yaml` file to open it in the code editor.
{{< /docs/ignore >}}

<!-- INTERACTIVE ignore START -->
1. Open the `loki-fundamentals` directory in a code editor of your choice.
1. Locate the `otel-config.yaml` file in the `loki-fundamentals` directory (Top level directory).
1. Click on the `otel-config.yaml` file to open it in the code editor.
<!-- INTERACTIVE ignore END -->

You will copy all three of the following configuration snippets into the `otel-config.yaml` file.

### Receive OpenTelemetry logs via gRPC and HTTP

First, we will configure the OpenTelemetry receiver. `otlp:` accepts logs in the OpenTelemetry format via HTTP and gRPC. We will use this receiver to receive logs from the Carnivorous Greenhouse application.

Now add the following configuration to the `otel-config.yaml` file:

```yaml
# Receivers
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
```

In this configuration:

- `receivers`: The list of receivers to receive telemetry data. In this case, we are using the `otlp` receiver.
- `otlp`: The OpenTelemetry receiver that accepts logs in the OpenTelemetry format.
- `protocols`: The list of protocols that the receiver supports. In this case, we are using `grpc` and `http`.
- `grpc`: The gRPC protocol configuration. The receiver will accept logs via gRPC on `4317`.
- `http`: The HTTP protocol configuration. The receiver will accept logs via HTTP on `4318`.
- `endpoint`: The IP address and port number to listen on. In this case, we are listening on all IP addresses on port `4317` for gRPC and port `4318` for HTTP.

For more information on the `otlp` receiver configuration, see the [OpenTelemetry Receiver OTLP documentation](https://github.com/open-telemetry/opentelemetry-collector/blob/main/receiver/otlpreceiver/README.md).

### Create batches of logs using a OpenTelemetry processor

Next add the following configuration to the `otel-config.yaml` file:

```yaml
# Processors
processors:
  batch:
```

In this configuration:

- `processors`: The list of processors to process telemetry data. In this case, we are using the `batch` processor.
- `batch`: The OpenTelemetry processor that accepts telemetry data from other `otelcol` components and places them into batches.

For more information on the `batch` processor configuration, see the [OpenTelemetry Processor Batch documentation](https://github.com/open-telemetry/opentelemetry-collector/blob/main/processor/batchprocessor/README.md).

### Export logs to Loki using a OpenTelemetry exporter

We will use the `otlphttp/logs` exporter to send the logs to the Loki native OTLP endpoint. Add the following configuration to the `otel-config.yaml` file:

```yaml
# Exporters
exporters:
  otlphttp/logs:
    endpoint: "http://loki:3100/otlp"
    tls:
      insecure: true
```

In this configuration:

- `exporters`: The list of exporters to export telemetry data. In this case, we are using the `otlphttp/logs` exporter.
- `otlphttp/logs`: The OpenTelemetry exporter that accepts telemetry data from other `otelcol` components and writes them over the network using the OTLP HTTP protocol.
- `endpoint`: The URL to send the telemetry data to. In this case, we are sending the logs to the Loki native OTLP endpoint at `http://loki:3100/otlp`.
- `tls`: The TLS configuration for the exporter. In this case, we are setting `insecure` to `true` to disable TLS verification.
- `insecure`: Disables TLS verification. This is set to `true` as we are using an insecure connection.
  
For more information on the `otlphttp/logs` exporter configuration, see the [OpenTelemetry Exporter OTLP HTTP documentation](https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/otlphttpexporter/README.md)

### Creating the pipeline

Now that we have configured the receiver, processor, and exporter, we need to create a pipeline to connect these components. Add the following configuration to the `otel-config.yaml` file:

```yaml
# Pipelines
service:
  pipelines:
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp/logs]
```

In this configuration:

- `pipelines`: The list of pipelines to connect the receiver, processor, and exporter. In this case, we are using the `logs` pipeline but there is also pipelines for metrics, traces, and continuous profiling.
- `receivers`: The list of receivers to receive telemetry data. In this case, we are using the `otlp` receiver component we created earlier.
- `processors`: The list of processors to process telemetry data. In this case, we are using the `batch` processor component we created earlier.
- `exporters`: The list of exporters to export telemetry data. In this case, we are using the `otlphttp/logs` component exporter we created earlier.

### Load the configuration

Before you load the configuration into the OpenTelemetry Collector, compare your configuration with the completed configuration below:

```yaml
# Receivers
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
        
# Processors
processors:
  batch:

# Exporters
exporters:
  otlphttp/logs:
    endpoint: "http://loki:3100/otlp"
    tls:
      insecure: true
      
# Pipelines
service:
  pipelines:
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp/logs]
```

Next, we need apply the configuration to the OpenTelemetry Collector. To do this, we will restart the OpenTelemetry Collector container:
<!-- INTERACTIVE exec START -->
```bash
docker restart loki-fundamentals-otel-collector-1
```
<!-- INTERACTIVE exec END -->

This will restart the OpenTelemetry Collector container with the new configuration. You can check the logs of the OpenTelemetry Collector container to see if the configuration was loaded successfully:
<!-- INTERACTIVE exec START -->
```bash
docker logs loki-fundamentals-otel-collector-1
```

Within the logs, you should see the following message:

```console
2024-08-02T13:10:25.136Z        info    service@v0.106.1/service.go:225 Everything is ready. Begin running and processing data.
```

## Stuck? Need help?

If you get stuck or need help creating the configuration, you can copy and replace the entire `otel-config.yaml` using the completed configuration file:

<!-- INTERACTIVE exec START -->
```bash
cp loki-fundamentals/completed/otel-config.yaml loki-fundamentals/otel-config.yaml
docker restart loki-fundamentals-otel-collector-1
```
<!-- INTERACTIVE exec END -->

<!-- INTERACTIVE page step2.md END -->

<!-- INTERACTIVE page step3.md START -->

## Step 3: Start the Carnivorous Greenhouse

In this step, we will start the Carnivorous Greenhouse application. To start the application, run the following command:
<!-- INTERACTIVE ignore START -->
{{< admonition type="note" >}}
This docker-compose file relies on the `loki-fundamentals_loki` docker network. If you have not started the observability stack, you will need to start it first.
{{< /admonition >}}
<!-- INTERACTIVE ignore END -->

{{< docs/ignore >}}

**Note: This docker-compose file relies on the `loki-fundamentals_loki` docker network. If you have not started the observability stack, you will need to start it first.**

{{< /docs/ignore >}}

<!-- INTERACTIVE ignore START -->
```bash
docker compose -f loki-fundamentals/greenhouse/docker-compose-micro.yml up -d --build 
```
<!-- INTERACTIVE ignore END -->

{{< docs/ignore >}}

<!-- INTERACTIVE exec START -->
```bash
docker-compose -f loki-fundamentals/greenhouse/docker-compose-micro.yml up -d --build
```
<!-- INTERACTIVE exec END -->

{{< /docs/ignore >}}

This will start the following services:

```console
 ✔ Container greenhouse-db-1                 Started                                                         
 ✔ Container greenhouse-websocket_service-1  Started 
 ✔ Container greenhouse-bug_service-1        Started
 ✔ Container greenhouse-user_service-1       Started
 ✔ Container greenhouse-plant_service-1      Started
 ✔ Container greenhouse-simulation_service-1 Started
 ✔ Container greenhouse-main_app-1           Started
```

Once started, you can access the Carnivorous Greenhouse application at [http://localhost:5005](http://localhost:5005). Generate some logs by interacting with the application in the following ways:

1. Create a user.
1. Log in.
1. Create a few plants to monitor.
1. Enable bug mode to activate the bug service. This will cause services to fail and generate additional logs.

Finally to view the logs in Loki, navigate to the Loki Logs Explore view in Grafana at [http://localhost:3000/a/grafana-lokiexplore-app/explore](http://localhost:3000/a/grafana-lokiexplore-app/explore).

<!-- INTERACTIVE page step3.md END -->

<!-- INTERACTIVE page finish.md START -->

## Summary

In this example, we configured the OpenTelemetry Collector to receive logs from an example application and send them to Loki using the native OTLP endpoint. Make sure to also consult the Loki configuration file `loki-config.yaml` to understand how we have configured Loki to receive logs from the OpenTelemetry Collector.

{{< docs/ignore >}}

### Back to docs

Head back to where you started from to continue with the [Loki documentation](https://grafana.com/docs/loki/latest/send-data/otel).

{{< /docs/ignore >}}

## Further reading

For more information on the OpenTelemetry Collector and the native OTLP endpoint of Loki, refer to the following resources:

- [Loki OTLP endpoint](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/otel/)
- [How is native OTLP endpoint different from Loki Exporter](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/otel/native_otlp_vs_loki_exporter)
- [OpenTelemetry Collector Configuration](https://opentelemetry.io/docs/collector/configuration/)

## Complete metrics, logs, traces, and profiling example

If you would like to use a demo that includes Mimir, Loki, Tempo, and Grafana, you can use [Introduction to Metrics, Logs, Traces, and Profiling in Grafana](https://github.com/grafana/intro-to-mlt). `Intro-to-mltp` provides a self-contained environment for learning about Mimir, Loki, Tempo, and Grafana.

The project includes detailed explanations of each component and annotated configurations for a single-instance deployment. Data from `intro-to-mltp` can also be pushed to Grafana Cloud.

<!-- INTERACTIVE page finish.md END -->
<!-- vale Grafana.We = YES -->

# Setup

# Install Loki with Docker or Docker Compose

You can install Loki and Alloy with Docker or Docker Compose if you are evaluating, testing, or developing Loki.
For production, Grafana recommends installing with Helm or Tanka.

The configuration files associated with these installation instructions run Loki as a single binary.

## Prerequisites

- [Docker](https://docs.docker.com/install)
- [Docker Compose](https://docs.docker.com/compose/install) (optional, only needed for the Docker Compose install method)

## Install with Docker on Linux

1. Create a directory called `loki`. Make `loki` your current working directory:

    ```bash
    mkdir loki
    cd loki
    ```

1. Copy and paste the following command into your command line to download `loki-local-config.yaml` to your `loki` directory. If necessary, update the Loki version numbers in the commands to match your version of Loki.

    ```bash
    wget https://raw.githubusercontent.com/grafana/loki/v3.6.0/cmd/loki/loki-local-config.yaml -O loki-config.yaml
    ```

1. Copy and paste the following command into your command line to start the Docker container using the configuration file you downloaded in the previous step. If necessary, update the Loki version numbers in the commands to match your version of Loki.

    ```bash
    docker run --name loki -d -v $(pwd):/mnt/config -p 3100:3100 grafana/loki:3.6.0 -config.file=/mnt/config/loki-config.yaml
    ```

    {{< admonition type="note" >}}
    The image is configured to run by default as user `loki` with UID `10001` and GID `10001`. You can use a different user, specially if you are using bind mounts, by specifying the UID with a `docker run` command and using `--user=UID` with a numeric UID suited to your needs.
    {{< /admonition >}}

    To send logs to Loki, consider using [Grafana Alloy](https://grafana.com/docs/alloy/latest/).

1. Verify that your containers are running:

    ```bash
    docker container ls
    ```

    You should see something similar to the following:

    ```bash

    CONTAINER ID   IMAGE                    COMMAND                  CREATED              STATUS              PORTS                                       NAMES
    cece1df84519   grafana/loki:3.6.0       "/usr/bin/loki -conf…"   About a minute ago   Up About a minute   0.0.0.0:3100->3100/tcp, :::3100->3100/tcp   loki
    ```

1. Verify that Loki is up and running.

    - To view readiness, navigate to http://localhost:3100/ready.
    - To view metrics, navigate to http://localhost:3100/metrics.

## Install with Docker on Windows

1. Copy and paste the following commands into your command line to download `loki-local-config.yaml` to your `loki` directory. Note that you will need to replace the `<local-path>` in the commands with your local path. If necessary, update the Loki version numbers in the commands to match your version of Loki.

```bash
cd "<local-path>"
wget https://raw.githubusercontent.com/grafana/loki/v3.6.0/cmd/loki/loki-local-config.yaml -O loki-config.yaml
```

1. Copy and paste the following command into your command line to start the Docker container using the configuration file you downloaded in the previous step. Note that you will need to replace the `<local-path>` in the commands with your local path. If necessary, update the Loki version numbers in the commands to match your version of Loki.

```bash
docker run --name loki -v <local-path>:/mnt/config -p 3100:3100 grafana/loki:3.6.0 --config.file=/mnt/config/loki-config.yaml
```

To send logs to Loki, consider using [Grafana Alloy](https://grafana.com/docs/alloy/latest/).

1. Verify that Loki is up and running.

    - To view readiness, navigate to http://localhost:3100/ready.
    - To view metrics, navigate to http://localhost:3100/metrics.

## Install with Docker Compose

Run the following commands in your command line. They work for Windows or Linux systems.

1. Create a directory called `loki`. Make `loki` your current working directory:

    ```bash
    mkdir loki
    cd loki
    ```

1. Copy and paste the following commands into your command line to download the `docker-compose.yaml` and `alloy-local-config.yaml` files. If necessary, update the Loki version numbers in the commands to match your version of Loki.

    ```bash
    wget https://raw.githubusercontent.com/grafana/loki/v3.6.0/examples/getting-started/docker-compose.yaml -O docker-compose.yaml
    wget https://raw.githubusercontent.com/grafana/loki/v3.6.0/examples/getting-started/alloy-local-config.yaml -O alloy-local-config.yaml
    wget https://raw.githubusercontent.com/grafana/loki/v3.6.0/examples/getting-started/loki-config.yaml -O loki-config.yaml
    ```

1. With `loki` as the current working directory, run the following 'docker-compose` command:

    ```bash
    docker-compose -f docker-compose.yaml up
    ```

    You should see something similar to the following:

    ```bash
    ✔ Container loki-loki-1      Started              0.0s
    ✔ Container loki-grafana-1   Started              0.0s
    ✔ Container loki-alloy-1     Started              0.0s
    ```

1. Verify that Loki is up and running.

    - To view read path readiness, navigate to http://localhost:3101/ready.
    - To view read path metrics, navigate to http://localhost:3101/metrics.
    - To view write path readiness, navigate to http://localhost:3102/ready.
    - To view write path metrics, navigate to http://localhost:3102/metrics.

# Build from source

Clone the Grafana Loki repository and use the provided `Makefile`
to build Loki from source.

{{< admonition type="note" >}}
Grafana Loki does not come with any included authentication layer. You must run an authenticating reverse proxy in front of your services to prevent unauthorized access to Loki (for example, nginx). Refer to [Manage authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/) for a list of open-source reverse proxies you can use.
{{< /admonition >}}

## Prerequisites

- [Go](https://golang.org/), version 1.26 or later (check the `go` directive in [go.mod](https://github.com/grafana/loki/blob/main/go.mod) for the exact minimum version);
set your `$GOPATH` environment variable
- `make`
- Docker (for updating protobuf and yacc files)

## Build locally

1. Clone Loki to `$GOPATH/src/github.com/grafana/loki`:

    ```bash
    git clone https://github.com/grafana/loki $GOPATH/src/github.com/grafana/loki
    ```

2. With a current working directory of `$GOPATH/src/github.com/grafana/loki`:

    ```bash
    make loki
    ```

The built executable will be in `$GOPATH/src/github.com/grafana/loki/cmd/loki/loki`.

# Install on Istio

When installing Loki on Istio service mesh you must complete some additional steps. Without these steps, the ingester, querier, etc. might start, but you will see logs like the following:

```
loki level=debug ts=2021-11-24T11:33:37.352544925Z caller=broadcast.go:48 msg="Invalidating forwarded broadcast" key=collectors/distributor version=123 oldVersion=122 content=[loki-distributor-59c4896444-t9t6g[] oldContent=[loki-distributor-59c4896444-t9t6g[]
```

This means that the pod is failing to join the ring.

If you try to add `loki` to `Grafana` data sources, you will see logs like (`empty ring`):

```
loki level=warn ts=2021-11-24T08:02:42.08262122Z caller=logging.go:72 traceID=3fc821042d8ada1a orgID=fake msg="GET /loki/api/v1/labels?end=1637740962079859431&start=1637740361925000000 (500) 97.4µs Response: \"empty ring\\n\" ws: false; X-Scope-Orgid: fake; uber-trace-id: 3fc821042d8ada1a:1feed8872deea75c:1180f95a8235bb6c:0; "
```

When you enable istio-injection on the namespace where Loki is running, you need to also modify the configuration for the Loki services. Given that Istio will not allow a pod to resolve another pod using an IP address, you must also modify the `memberlist` service.

## Required changes

### Query frontend service

Make the following modifications to the file for the Loki Query Frontend service.

1. Change the name of `grpc` port to `grpclb`. This is used by the grpc load balancing strategy which relies on SRV records. Otherwise the `querier` will not be able to reach the `query-frontend`. See https://github.com/grafana/loki/blob/0116aa61c86fa983ddcbbd5e30a2141d2e89081a/production/ksonnet/loki/common.libsonnet#L19
and
https://grpc.github.io/grpc/core/md_doc_load-balancing.html
1. Set the `appProtocol` of `grpclb` to `tcp`
1. Set `publishNotReadyAddresses` to `true`

```yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: loki-query-frontend
    app.kubernetes.io/instance: observability
    app.kubernetes.io/name: loki-query-frontend
spec:
  ports:
  - appProtocol: http
    name: http
    port: 3100
    protocol: TCP
    targetPort: http
  - appProtocol: tcp
    name: grpclb
    port: 9095
    protocol: TCP
    targetPort: grpc
  publishNotReadyAddresses: true
  selector:
    app: loki-query-frontend
    app.kubernetes.io/instance: observability
    app.kubernetes.io/name: loki-query-frontend
  type: ClusterIP
```

### Querier service

Make the following modifications to the file for the Loki Querier service.

Set the `appProtocol` of the `grpc` service to `tcp`

```yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: loki-querier
    app.kubernetes.io/instance: observability
    app.kubernetes.io/name: loki-querier
  name: loki-querier
  namespace: observability
spec:
  ports:
  - appProtocol: http
    name: http
    port: 3100
    protocol: TCP
    targetPort: http
  - appProtocol: tcp
    name: grpc
    port: 9095
    protocol: TCP
    targetPort: grpc
  selector:
    app: loki-querier
    app.kubernetes.io/instance: observability
    app.kubernetes.io/name: loki-querier
  type: ClusterIP

```

### Ingester service and Ingester headless service

Make the following modifications to the file for the Loki Query Ingester and Ingester Headless service.

Set the `appProtocol` of the `grpc` port to `tcp`.

```yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: loki-ingester-(headless)
    app.kubernetes.io/instance: observability
    app.kubernetes.io/name: loki-ingester
  name: loki-ingester-headless
spec:  
  clusterIP: None (if headless)
  ports:
  - name: http
    port: 3100
    protocol: TCP
    targetPort: http
  - appProtocol: tcp
    name: grpc
    port: 9095
    protocol: TCP
    targetPort: grpc
  selector:
    app: loki-ingester
    app.kubernetes.io/instance: observability
    app.kubernetes.io/name: loki-ingester
  type: ClusterIP
```

### Distributor service

Make the following modifications to the file for the Loki Distributor service.

Set the `appProtocol` of the `grpc` port to `tcp`.

```yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: loki-distributor
    app.kubernetes.io/instance: observability
    app.kubernetes.io/name: loki-distributor
spec:
  ports:
  - name: http
    port: 3100
    protocol: TCP
    targetPort: http
  - name: grpc
    port: 9095
    protocol: TCP
    targetPort: grpc
    appProtocol: tcp
  selector:
    app: loki-distributor
    app.kubernetes.io/instance: observability
    app.kubernetes.io/name: loki-distributor
  sessionAffinity: None
  type: ClusterIP

```

### Memberlist service

Make the following modifications to the file for the Memberlist service.

Set the `appProtocol` of the `http` port to `tcp`.

```yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/instance: observability
  name: loki-memberlist
  namespace: observability
spec:
  clusterIP: None
  ports:
    - name: http
      port: 7946
      protocol: TCP
      targetPort: 7946
      appProtocol: tcp
  selector:
    app.kubernetes.io/instance: observability
    app.kubernetes.io/part-of: memberlist
```

# Install Grafana Loki locally

To log events with Grafana Loki, download and install Loki.

- Loki is the logging engine.
- [Grafana Alloy](https://grafana.com/docs/alloy/latest/) is recommended for sending logs to Loki.

The configuration runs Loki as a single binary.

{{< admonition type="note" >}}
Grafana Loki does not come with any included authentication layer. You must run an authenticating reverse proxy in front of your services to prevent unauthorized access to Loki (for example, nginx). Refer to [Manage authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/) for a list of open-source reverse proxies you can use.
{{< /admonition >}}

## Install using APT or RPM package manager

1. Add the Grafana [Advanced Package Tool (APT)](https://apt.grafana.com/) or [RPM Package Manager (RPM)](https://rpm.grafana.com/) package repository following the linked instructions.
1. Install Loki
   1. Using `dnf`

      ```bash
      dnf update
      dnf install loki
      ```

   1. Using `apt-get`

      ```bash
      apt-get update
      apt-get install loki
      ```

## Install manually

1. Browse to the [release page](https://github.com/grafana/loki/releases/).
1. Find the **Assets** section for the version that you want to install.
1. Download the Loki archive file that corresponds to your system.

   Don't download LogCLI or Loki Canary at this time.
   LogCLI allows you to run Loki queries in a command line interface.
   [Loki Canary](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/loki-canary/) is a tool to audit Loki performance.

1. Extract the package contents into a directory. This is where Loki will run.
1. In the command line, change directory (`cd` on most systems) to the directory with Loki.

   Copy and paste the following command into your command line to download a generic configuration file.

   Use the Git references that match your downloaded Loki version to get the correct configuration file.
   For example, if you are using Loki version 3.6.0, you need to use the `https://raw.githubusercontent.com/grafana/loki/v3.6.0/cmd/loki/loki-local-config.yaml` URL to download the configuration file.

   ```bash
   wget https://raw.githubusercontent.com/grafana/loki/main/cmd/loki/loki-local-config.yaml
   ```

1. Run the following command to start Loki:

   **Windows**

   ```bash
   .\loki-windows-amd64.exe --config.file=loki-local-config.yaml
   ```

   **Linux**

   ```bash
   ./loki-linux-amd64 -config.file=loki-local-config.yaml
   ```

Loki runs and displays Loki logs in your command line and on http://localhost:3100/metrics.

The next step is running an agent to send logs to Loki.
Refer to [Grafana Alloy](https://grafana.com/docs/alloy/latest/) for the recommended log collection agent.

## Release binaries - openSUSE Linux only

Every release includes binaries for Loki.
You can find them on the [Releases page](https://github.com/grafana/loki/releases).

## Community openSUSE Linux packages

The community provides packages of Loki for openSUSE Linux.
To install them:

1. Add the repository `https://download.opensuse.org/repositories/security:/logging/` to your system.
   For example, if you are using Leap 15.1, run:

   ```bash
   sudo zypper ar https://download.opensuse.org/repositories/security:/logging/openSUSE_Leap_15.1/security:logging.repo
   sudo zypper ref
   ```

1. Install the Loki package:

   ```bash
   zypper in loki
   ```

1. Start and enable the Loki service:

   ```bash
   systemd start loki
   systemd enable loki
   ```

1. Modify the `/etc/loki/loki.yaml` configuration file as needed.

# Tanka

[Tanka](https://tanka.dev) is a reimplementation of
[Ksonnet](https://ksonnet.io) that Grafana Labs created after Ksonnet was
deprecated. Tanka is used by Grafana Labs to run Grafana Loki in production.

The Tanka installation runs the Loki cluster in microservices mode.

{{< admonition type="note" >}}
Grafana Loki does not come with any included authentication layer. You must run an authenticating reverse proxy in front of your services to prevent unauthorized access to Loki (for example, nginx). Refer to [Manage authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/) for a list of open-source reverse proxies you can use.
{{< /admonition >}}

## Prerequisites

Install the latest version of Tanka (version v0.31.0 or a more recent version) for the `tk env`
commands. Prebuilt binaries for Tanka can be found at the [Tanka releases
URL](https://github.com/grafana/tanka/releases).

In your config repo, if you don't have a Tanka application, create a folder and
call `tk init` inside of it. Then create an environment for Loki and provide the
URL for the Kubernetes API server to deploy to (e.g., `https://localhost:6443`):

```bash
mkdir <application name>
cd <application name>
tk init
tk env add environments/loki --namespace=loki --server=<Kubernetes API server>
```

Install `jsonnet-bundler` (`jb`), find instructions for your platform in Tanka's [installation docs](https://tanka.dev/install#jsonnet-bundler).

## Deploying

Download and install the Loki module using `jb` (version v0.6.0 or a more recent version):

```bash
jb init  # not required if you already ran `tk init`
jb install github.com/grafana/loki/production/ksonnet/loki@main
```

## Install a log-shipping agent

Use [Grafana Alloy](https://grafana.com/docs/alloy/latest/set-up/) to ship logs to Loki.

For installation guidance, refer to:

- [Install Grafana Alloy](https://grafana.com/docs/alloy/latest/set-up/install/)
- [Install Alloy on Kubernetes](https://grafana.com/docs/alloy/latest/set-up/install/kubernetes/)

Revise the YAML contents of `environments/loki/main.jsonnet`, updating these variables:

- Update the `username`, `password`, and the relevant `htpasswd` variable values.
- Update object storage variables for one backend (`s3`, `gcs`, or `azure`) and remove variables for backends that are not part of your setup. In this module, `storage_backend` must be a single value. Refer to [storage_config](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#storage_config) for configuration details.
- Update the `from` value in the Loki `schema_config` section to no more than 7 days prior to the current date. The `from` date represents the first day for which the `schema_config` section is valid. For example, if today is `2021-01-15`, set `from` to `2021-01-08`. This recommendation is based on the Loki default acceptance of log lines up to 1 week (`168h`) in the past. The `reject_old_samples_max_age` configuration variable controls the acceptance range. Use TSDB (`store: tsdb`, `schema: v13`) for new installs.

```jsonnet
local gateway = import 'loki/gateway.libsonnet';
local loki = import 'loki/loki.libsonnet';

loki + gateway {
  _config+:: {
    namespace: 'loki',
    htpasswd_contents: 'loki:$apr1$H4yGiGNg$ssl5/NymaGFRUvxIV1Nyr.',

    // Use TSDB shipper
    using_tsdb_shipper: true

    // Pick one object storage backend: 's3', 'gcs', or 'azure'
    storage_backend: 's3',

    // S3 variables (used when storage_backend = 's3')
    s3_access_key: 'key',
    s3_secret_access_key: 'secret access key',
    s3_address: 'url',
    s3_bucket_name: 'loki-chunks',
    s3_bucket_region: 'us-east-1',
    s3_path_style: false,

    // GCS variables (used when storage_backend = 'gcs')
    // gcs_bucket_name: 'loki-chunks',

    // Azure variables (used when storage_backend = 'azure')
    // azure_container_name: 'loki-chunks',
    // azure_account_name: 'account',
    // azure_account_key: 'key',

    // TSDB index store configuration
    loki+: {
      schema_config: {
        configs: [{
          from: 'YYYY-MM-DD',
          store: 'tsdb',
          object_store: $._config.storage_backend,
          schema: 'v13',
          index: {
            prefix: '%s_index_' % $._config.table_prefix,
            period: '%dh' % $._config.index_period_hours,
          },
        }],
      },
    },

    replication_factor: 3,
    memberlist_ring_enabled: true,
  },
}
```

Run `tk show environments/loki` to see the manifests that will be deployed to
the cluster. Run `tk apply environments/loki` to deploy the manifests.
To delete the environment from cluster, run `tk delete environments/loki`.

# Helm chart components

This section describes the components installed by the Helm Chart.
For the ports and protocols these components use, refer to [Loki network ports](https://grafana.com/docs/loki/<LOKI_VERSION>/reference/network-ports/).

## 3 methods of deployment

The Loki chart supports three methods of deployment:

- [Monolithic](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/install-monolithic/)
- [Simple Scalable](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/install-scalable/)
- [Microservice](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/install-microservices/)

By default, the chart installs in [Monolithic](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/install-monolithic/) mode (`deploymentMode: Monolithic`). For production at scale, we recommend deploying Loki in *microservices* (`deploymentMode: Distributed`) mode. To understand the differences between deployment methods, see the [Loki deployment modes](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/) documentation.

{{< admonition type="note" >}}
Simple Scalable Deployment (SSD) mode is being deprecated and removed in Loki 4.0.
{{< /admonition >}}

## Zone-aware replication

When deploying in [microservices](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/install-microservices/) mode, the chart enables **zone-aware replication** for ingesters by default (`ingester.zoneAwareReplication.enabled: true`). This creates three ingester StatefulSets (zone-a, zone-b, zone-c) and requires enabling the `rollout-operator` subchart (`rollout_operator.enabled: true`) for coordinated zone rollouts. Zone-aware replication allows multiple ingesters within a single zone to be shut down and restarted simultaneously during rollouts, while the remaining two zones guarantee at least one copy of the data.

To disable zone-aware replication (for example, in a development or test environment):

```yaml
ingester:
  zoneAwareReplication:
    enabled: false
```

If you deploy Loki using the jsonnet ([ksonnet](https://github.com/grafana/loki/tree/main/production/ksonnet)) method instead of the Helm chart, refer to [Speed up ingester rollout using zone awareness](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/zone-ingesters/), which describes the equivalent `multi_zone_ingester_*` jsonnet configuration.

## Pattern ingester

The chart includes an optional **pattern ingester** component (`patternIngester`) for detecting and extracting log patterns. It is disabled by default (`patternIngester.enabled: false`, `replicas: 0`). In Distributed mode (`deploymentMode: Distributed`), enable the standalone workload with:

```yaml
deploymentMode: Distributed
patternIngester:
  enabled: true
  replicas: 1
loki:
  pattern_ingester:
    enabled: true
```

In Monolithic or SimpleScalable mode, enable it via Loki configuration only:

```yaml
loki:
  pattern_ingester:
    enabled: true
```

## Overrides exporter

The **overrides exporter** (`overridesExporter`) is an optional component that exposes per-tenant configuration overrides as Prometheus metrics. It is disabled by default (`overridesExporter.enabled: false`). Enable it when you need visibility into tenancy-level limit settings.

## Bloom filters (deprecated)

Bloom filters are deprecated and will be removed in a future release.

The chart includes support for **bloom filters** through three components:

- `bloomGateway`: Serves bloom filter queries
- `bloomPlanner`: Plans bloom filter build jobs
- `bloomBuilder`: Builds bloom filters

All three are disabled by default (replicas set to 0). Enable bloom filters in the Loki configuration (`loki.bloom_build.enabled: true`, `loki.bloom_gateway.enabled: true`) and set non-zero replica counts for the corresponding components.

## Monitoring Loki

The Loki Helm chart includes built-in monitoring resources that can be enabled:

- **ServiceMonitor** (`monitoring.serviceMonitor.enabled`): Creates Prometheus Operator ServiceMonitor resources for scraping Loki metrics.
- **Recording rules** (`monitoring.rules.enabled`): Creates a PrometheusRule resource with loki-mixin recording rules.
- **Alert rules** (`monitoring.alerts.enabled`): Creates a PrometheusRule resource with alerts such as `LokiRequestErrors`, `LokiRequestPanics`, and `LokiRequestLatency`.
- **Dashboards** (`monitoring.dashboards.enabled`): Creates ConfigMaps containing Grafana dashboards for monitoring Loki.

These built-in monitoring resources are disabled by default. For comprehensive cluster-wide observability, use the [Kubernetes monitoring Helm chart](https://github.com/grafana/k8s-monitoring-helm). See [Monitoring](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/monitor-and-alert/) for details.

{{< admonition type="note" >}}
For comprehensive cluster-wide observability, Grafana Labs recommends the [Kubernetes monitoring Helm chart](https://github.com/grafana/k8s-monitoring-helm). The Loki chart still provides optional built-in Prometheus Operator resources under `monitoring.*`; only the former `monitoring.selfMonitoring` / Grafana Agent integration has been removed.
{{< /admonition >}}

## Canary

This chart installs the [Loki Canary app](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/loki-canary/) by default. This is another tool to verify the Loki deployment is in a healthy state. It can be disabled by setting `lokiCanary.enabled=false`.

## Gateway

By default and inspired by Grafana's [Tanka setup](https://github.com/grafana/loki/blob/main/production/ksonnet/loki), the chart
installs the gateway component which is an NGINX that exposes the Loki API and automatically proxies requests to the correct
Loki components (read or write, or single instance in the case of filesystem storage).
You can expose Loki either through `gateway.ingress` (with `gateway.enabled: true`) or through the top-level `ingress` key (with `gateway.enabled: false`), but not both.
If the gateway is enabled, Grafana and log shipping agents, such as Grafana Alloy, should be configured to use the gateway.
If NetworkPolicies are enabled, they are more restrictive if the gateway is enabled.

## Caching

By default, the chart deploys Memcached-based **chunks cache** (`chunksCache.enabled: true`) and **results cache** (`resultsCache.enabled: true`). To use an externally managed Memcached instead, disable the built-in caches and point `chunksCache.addresses` / `resultsCache.addresses` at your service. See [caching](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/caching/) for tuning guidance.

## StatefulSet persistence

Several components in this chart run as Kubernetes StatefulSets, including `ingester`, `write`, `backend`, and `singleBinary`, which all use `kind: StatefulSet` by default. Some Helm values map to StatefulSet fields that Kubernetes treats as immutable after the StatefulSet is created, such as storage class, persistent volume claim (PVC) size, access modes, and `podManagementPolicy`. If you change one of these values and run `helm upgrade`, the upgrade can fail.

For a few of these values, including PVC size and `podManagementPolicy`, you can set `*.statefulSetRecreateJob.enabled` on a Loki component. This runs an experimental job before the upgrade that deletes the StatefulSet but keeps its pods and PVCs, so no data is lost. The job does not handle other changes, such as storage class or access modes. Those still require a manual migration.

For the full list of affected values, which components render StatefulSets in each deployment mode, and how the recreate job works, see the "StatefulSet immutability" section of the [chart's README](https://github.com/grafana-community/helm-charts/tree/main/charts/loki#statefulset-immutability) on GitHub.

# Helm chart values

<!-- Autogenerated by the helm-doc-sync skill from the grafana-community/helm-charts repo. Do not edit by hand. -->

# Helm chart values

This is the generated reference for the Loki Helm chart values, sourced from the `values.yaml` of the chart in the [grafana-community/helm-charts](https://github.com/grafana-community/helm-charts/tree/main/charts/loki) repository.

Because the Loki Helm chart exposes a large number of configuration options, this reference is grouped into sections by top-level configuration key. Each section lists the configurable keys under that prefix along with their type, description, and default value.

To navigate it:

- Use the [Sections](#sections) list below to jump to a configuration group.
- Use your browser search (`Ctrl+F`) to locate a specific key.
- For installation examples and setup instructions, refer to the Helm installation guides rather than this reference.

{{< admonition type="note" >}}
This reference is for the Loki Helm chart version 3.0 or greater.
If you are using the `grafana/loki-stack` Helm chart from the grafana repo, please refer to the `values.yaml` of the respective GitHub repository [grafana/helm-charts](https://github.com/grafana/helm-charts/tree/main/charts/loki-stack).
{{< /admonition >}}

## Sections

- [Top-level values](#top-level-values)
- [backend](#backend) — Configuration for the backend pod(s)
- [bloomBuilder](#bloombuilder) — Configuration for the bloom-builder
- [bloomGateway](#bloomgateway) — Configuration for the bloom-gateway
- [bloomPlanner](#bloomplanner) — Configuration for the bloom-planner
- [chunksCache](#chunkscache) — Configuration for the Memcached-based chunks-cache, which caches the log chunks Loki fetches from object storage.
- [compactor](#compactor) — Configuration for the compactor
- [defaults](#defaults) — Default settings applied to the Loki components only.
- [distributor](#distributor) — Configuration for the distributor
- [gateway](#gateway) — Configuration for the gateway, an NGINX reverse proxy that routes incoming read and write requests to the appropriate Loki components.
- [global](#global) — Global values that apply to all components and subcharts, such as image registry, DNS configuration, and common environment variables.
- [indexGateway](#indexgateway) — Configuration for the index-gateway
- [ingester](#ingester) — Configuration for the ingester
- [ingress](#ingress) — Ingress configuration Use either this ingress or the gateway, but not both at once.
- [loki](#loki) — Configuration for running Loki
- [lokiCanary](#lokicanary) — Configuration for the Loki Canary, which continuously pushes logs to and queries them from this Loki installation to verify it is working correctly.
- [memberlist](#memberlist) — Configuration for the memberlist service, the gossip-based ring Loki components use for service discovery and hash-ring coordination.
- [memcached](#memcached) — Common configuration shared by the Memcached deployments backing Loki's caches.
- [memcachedExporter](#memcachedexporter) — Configuration for the Prometheus Memcached exporter sidecar that exposes cache metrics.
- [migrate](#migrate) — Options that may be necessary when performing a migration from another helm chart
- [monitoring](#monitoring) — Monitoring section determines which monitoring features to enable
- [networkPolicy](#networkpolicy) — Configuration for the Kubernetes NetworkPolicies that control inbound and outbound traffic for Loki components.
- [overridesExporter](#overridesexporter) — Configuration for the overrides-exporter
- [patternIngester](#patterningester) — Configuration for the pattern ingester
- [querier](#querier) — Configuration for the querier
- [queryFrontend](#queryfrontend) — Configuration for the query-frontend
- [queryScheduler](#queryscheduler) — Configuration for the query-scheduler
- [rbac](#rbac) — Configuration for the Kubernetes RBAC resources and Pod Security Policy settings created for Loki.
- [read](#read) — Configuration for the read pod(s)
- [resultsCache](#resultscache) — Configuration for the Memcached-based results-cache, which caches query results to speed up repeated queries.
- [rollout_operator](#rollout_operator) — Setting for the Grafana Rollout Operator https://github.com/grafana/helm-charts/tree/main/charts/rollout-operator
- [route](#route) — Gateway API routes for direct routing to Loki services, bypassing the nginx gateway.
- [ruler](#ruler) — Configuration for the ruler
- [serviceAccount](#serviceaccount) — Configuration for the Kubernetes ServiceAccount used by Loki components.
- [sidecar](#sidecar) — Configuration for the k8s-sidecar container that watches for ConfigMaps and Secrets (such as rules and dashboards) and loads them into Loki.
- [singleBinary](#singlebinary) — Configuration for the single binary node(s) used in Monolithic deployment mode, where all Loki components run in a single process.
- [tableManager](#tablemanager) — DEPRECATED Configuration for the table-manager.
- [test](#test) — Section for configuring optional Helm test
- [write](#write) — Configuration for the write pod(s) in SimpleScalable mode, which run the distributor and ingester components.

## Top-level values

Values that are not part of a specific component group.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `commonLabels` | object | Labels to be added to resources | `{}` |
| `deploymentMode` | string | Deployment mode lets you specify how to deploy Loki. There are 3 options: - Monolithic (deprecated: SingleBinary): Loki is deployed as a single binary, useful for small installs typically without HA, up to a few tens of GB/day. - SimpleScalable (deprecated, removed in Loki 4): Loki is deployed as 3 targets: read, write, and backend. Useful for medium installs easier to manage than distributed, up to a about 1TB/day. - Distributed: Loki is deployed as individual microservices. The most complicated but most capable, useful for large installs, typically over 1TB/day. There are also 2 additional modes used for migrating between deployment modes: - Monolithic&lt;-&gt;SimpleScalable (deprecated: SingleBinary&lt;-&gt;SimpleScalable): Migrate from SingleBinary to SimpleScalable (or vice versa) - SimpleScalable&lt;-&gt;Distributed: Migrate from SimpleScalable to Distributed (or vice versa) Note: SimpleScalable and Distributed REQUIRE the use of object storage. Ref: https://grafana.com/docs/loki/latest/get-started/deployment-modes/ | `"Monolithic"` |
| `extraObjects` | string |  | `nil` |
| `fullnameOverride` | string | Overrides the chart's computed fullname | `""` |
| `ignoreMinioDeprecation` | bool | Ignore MinIO deprecation validation when `minio.enabled=true`. This is a temporary compatibility escape hatch. | `false` |
| `imagePullSecrets` | list | Image pull secrets for Docker images | `[]` |
| `kubeVersionOverride` | string | Overrides the version used to determine compatibility of resources with the target Kubernetes cluster. This is useful when using `helm template`, because then helm will use the client version of kubectl as the Kubernetes version, which may or may not match your cluster's server version. Example: 'v1.24.4'. Set to null to use the version that helm devises. | `""` |
| `minio` | object | Configuration for the minio subchart (deprecated) | `{"address":null,"buckets":[{"name":"chunks","policy":"none","purge":false},{"name":"ruler","policy":"none","purge":false}],"drivesPerNode":2,"enabled":false,"persistence":{"annotations":{},"size":"5Gi"},"replicas":1,"resources":{"requests":{"cpu":"100m","memory":"128Mi"}},"rootPassword":"supersecretpassword","rootUser":"root-user","users":[{"accessKey":"logs-user","policy":"readwrite","secretKey":"supersecretpassword"}]}` |
| `nameOverride` | string | Overrides the chart's name | `""` |
| `namespaceOverride` | string | Overrides the chart's namespace | `""` |
| `podDisruptionBudget` | object | Pod Disruption Budget for single binary deployment. Deprecated in favor of `singleBinary.podDisruptionBudget` | `{"enabled":true}` |

## backend

Configuration for the backend pod(s)

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `backend.affinity` | object | Affinity for backend pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `backend.annotations` | object | Annotations for backend StatefulSet | `{}` |
| `backend.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `backend.autoscaling.behavior` | object | Behavior policies while scaling. | `{}` |
| `backend.autoscaling.enabled` | bool | Enable autoscaling for the backend. | `false` |
| `backend.autoscaling.maxReplicas` | int | Maximum autoscaling replicas for the backend. | `6` |
| `backend.autoscaling.minReplicas` | int | Minimum autoscaling replicas for the backend. | `3` |
| `backend.autoscaling.targetCPUUtilizationPercentage` | int | Target CPU utilization percentage for the backend. | `60` |
| `backend.autoscaling.targetMemoryUtilizationPercentage` | string | Target memory utilization percentage for the backend. | `nil` |
| `backend.dnsConfig` | object | DNS config for backend pods | `{}` |
| `backend.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `SimpleScalable`. | `true` |
| `backend.extraArgs` | list | Additional CLI args for the backend | `[]` |
| `backend.extraContainers` | list | Containers to add to the backend pods | `[]` |
| `backend.extraEnv` | list | Environment variables to add to the backend pods | `[]` |
| `backend.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the backend pods | `[]` |
| `backend.extraVolumeMounts` | list | Volume mounts to add to the backend pods | `[]` |
| `backend.extraVolumes` | list | Volumes to add to the backend pods | `[]` |
| `backend.fullnameOverride` | string | Override for the backend fullname. By default, this is generated using the fullname template with "-backend" | `"{{ include \"loki.name\" . }}-backend"` |
| `backend.hostUsers` | string | Use the host's user namespace in the backend pods. | `nil` |
| `backend.image.registry` | string | The Docker registry for the backend image. Overrides `loki.image.registry` | `nil` |
| `backend.image.repository` | string | Docker image repository for the backend image. Overrides `loki.image.repository` | `nil` |
| `backend.image.tag` | string | Docker image tag for the backend image. Overrides `loki.image.tag` | `nil` |
| `backend.initContainers` | list | Init containers to add to the backend pods | `[]` |
| `backend.kedaAutoscaling.behavior` | object | Behavior configuration for KEDA ScaledObject (horizontalPodAutoscalerConfig.behavior). Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#advanced | `{}` |
| `backend.kedaAutoscaling.cooldownPeriod` | string | Period to wait after the last trigger reported active before scaling the resource back to 0 (seconds). Defaults to defaults.kedaAutoscaling.cooldownPeriod if not set. | `nil` |
| `backend.kedaAutoscaling.enabled` | bool | Enable KEDA autoscaling for the backend. Mutually exclusive with autoscaling.enabled | `false` |
| `backend.kedaAutoscaling.fallback` | object | Fallback configuration for KEDA ScaledObject. Applied when the trigger metric cannot be retrieved. Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#fallback | `{}` |
| `backend.kedaAutoscaling.maxReplicas` | int | Maximum replicas for KEDA autoscaling | `6` |
| `backend.kedaAutoscaling.minReplicas` | int | Minimum replicas for KEDA autoscaling | `3` |
| `backend.kedaAutoscaling.pollingInterval` | string | Interval (seconds) to poll each trigger. Defaults to defaults.kedaAutoscaling.pollingInterval if not set. | `nil` |
| `backend.kedaAutoscaling.triggers` | list | KEDA triggers for the backend. Ref: https://keda.sh/docs/latest/scalers/ | `[]` |
| `backend.kind` | string | Kind of backend workload. StatefulSet and Deployment are supported. | `"StatefulSet"` |
| `backend.labels` | object | Additional labels for backend StatefulSet | `{}` |
| `backend.maxUnavailable` | int | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `1` |
| `backend.nodeSelector` | object | Node selector for backend pods | `{}` |
| `backend.persistence.accessModes` | list | Set access modes on the PersistentVolumeClaim | `["ReadWriteOnce"]` |
| `backend.persistence.annotations` | object | Annotations for volume claim | `{}` |
| `backend.persistence.dataVolumeParameters` | object | Parameters used for the `data` volume when volumeClaimEnabled if false | `{"emptyDir":{}}` |
| `backend.persistence.enableStatefulSetAutoDeletePVC` | bool | Enable StatefulSetAutoDeletePVC feature | `true` |
| `backend.persistence.enabled` | bool | Enable volume claims in pod spec | `false` |
| `backend.persistence.labels` | object | Labels for volume claim | `{}` |
| `backend.persistence.selector` | string | Selector for persistent disk | `nil` |
| `backend.persistence.size` | string | Size of persistent disk | `"10Gi"` |
| `backend.persistence.storageClass` | string | Storage class to be used. If defined, storageClassName: &lt;storageClass&gt;. If set to "-", storageClassName: "", which disables dynamic provisioning. If empty or set to null, no storageClassName spec is set, choosing the default provisioner (gp2 on AWS, standard on GKE, AWS, and OpenStack). | `nil` |
| `backend.persistence.volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `backend.persistence.volumeClaimsEnabled` | bool | Enable volume claims in pod spec. Deprecated in favor of `persistence.enabled`. | `true` |
| `backend.persistence.whenDeleted` | string | What to do with the volumes when the StatefulSet is deleted. | `"Delete"` |
| `backend.persistence.whenScaled` | string | What to do with the volume when the StatefulSet is scaled down. | `"Delete"` |
| `backend.podAnnotations` | object | Annotations for backend pods | `{}` |
| `backend.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `backend.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `backend.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `backend.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `backend.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `backend.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `backend.podLabels` | object | Additional labels for each `backend` pod | `{}` |
| `backend.podManagementPolicy` | string | The default is to deploy all pods in parallel. | `"Parallel"` |
| `backend.priorityClassName` | string | The name of the PriorityClass for backend pods | `nil` |
| `backend.replicas` | int | Number of replicas for the backend. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `3` |
| `backend.resizePolicy` | list | Container resize policy for the backend Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `backend.resources` | object | Resource requests and limits for the backend | `{}` |
| `backend.selectorLabels` | object | Additional selector labels for each `backend` pod | `{}` |
| `backend.service.annotations` | object | Annotations for backend Service | `{}` |
| `backend.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `backend.service.ipFamilies` | list | ipFamilies for backend Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `backend.service.ipFamilyPolicy` | string | ipFamilyPolicy for backend Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `backend.service.labels` | object | Additional labels for backend Service | `{}` |
| `backend.service.sessionAffinity` | string | Session affinity for backend Service | `""` |
| `backend.service.sessionAffinityConfig` | object | Session affinity config for backend Service | `{}` |
| `backend.service.trafficDistribution` | string | trafficDistribution for backend Service | `""` |
| `backend.service.type` | string | Service type for backend Service | `"ClusterIP"` |
| `backend.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `backend.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `backend.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `backend.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `backend.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `backend.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `backend.sidecar` | bool | Whether to enable the rules sidecar | `true` |
| `backend.statefulSetRecreateJob` | object | EXPERIMENTAL: Enable creating a Job to recreate the backend StatefulSet when the compactor configuration is changed. # Only applicable if `compactor.kind` is StatefulSet. Useful for PVC size changes, which require recreating the StatefulSet. | `{"enabled":false}` |
| `backend.strategy` | object | UpdateStrategy or Strategy for the backend. | `{"rollingUpdate":{"partition":0}}` |
| `backend.terminationGracePeriodSeconds` | int | Grace period to allow the backend to shutdown before it is killed. Especially for the ingester, this must be increased. It must be long enough so backends can be gracefully shutdown flushing/transferring all data and to successfully leave the member ring on shutdown. | `300` |
| `backend.topologySpreadConstraints` | list | Topology Spread Constraints for backend pods The value will be passed through tpl. | `[]` |

## bloomBuilder

Configuration for the bloom-builder

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `bloomBuilder.affinity` | object | Affinity for bloom-builder pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `bloomBuilder.annotations` | object | Annotations for bloom-builder | `{}` |
| `bloomBuilder.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `bloomBuilder.autoscaling.behavior.enabled` | bool | Enable autoscaling behaviours | `false` |
| `bloomBuilder.autoscaling.behavior.scaleDown` | object | define scale down policies, must conform to HPAScalingRules | `{}` |
| `bloomBuilder.autoscaling.behavior.scaleUp` | object | define scale up policies, must conform to HPAScalingRules | `{}` |
| `bloomBuilder.autoscaling.customMetrics` | list | Allows one to define custom metrics using the HPA/v2 schema (for example, Pods, Object or External metrics) | `[]` |
| `bloomBuilder.autoscaling.enabled` | bool | Enable autoscaling for the bloom-builder | `false` |
| `bloomBuilder.autoscaling.maxReplicas` | int | Maximum autoscaling replicas for the bloom-builder | `3` |
| `bloomBuilder.autoscaling.minReplicas` | int | Minimum autoscaling replicas for the bloom-builder | `1` |
| `bloomBuilder.autoscaling.targetCPUUtilizationPercentage` | int | Target CPU utilisation percentage for the bloom-builder | `60` |
| `bloomBuilder.autoscaling.targetMemoryUtilizationPercentage` | string | Target memory utilisation percentage for the bloom-builder | `nil` |
| `bloomBuilder.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `bloomBuilder.dnsConfig` | object | DNSConfig for bloom-builder pods | `{}` |
| `bloomBuilder.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `Distributed`. | `false` |
| `bloomBuilder.extraArgs` | list | Additional CLI args for the bloom-builder | `[]` |
| `bloomBuilder.extraContainers` | list | Containers to add to the bloom-builder pods | `[]` |
| `bloomBuilder.extraEnv` | list | Environment variables to add to the bloom-builder pods | `[]` |
| `bloomBuilder.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the bloom-builder pods | `[]` |
| `bloomBuilder.extraVolumeMounts` | list | Volume mounts to add to the bloom-builder pods | `[]` |
| `bloomBuilder.extraVolumes` | list | Volumes to add to the bloom-builder pods | `[]` |
| `bloomBuilder.hostAliases` | list | hostAliases to add | `[]` |
| `bloomBuilder.hostUsers` | string | Use the host's user namespace in the boom-builder | `nil` |
| `bloomBuilder.image.registry` | string | The Docker registry for the bloom-builder image. Overrides `loki.image.registry` | `nil` |
| `bloomBuilder.image.repository` | string | Docker image repository for the bloom-builder image. Overrides `loki.image.repository` | `nil` |
| `bloomBuilder.image.tag` | string | Docker image tag for the bloom-builder image. Overrides `loki.image.tag` | `nil` |
| `bloomBuilder.initContainers` | list | Init containers to add to the bloom-builder pods | `[]` |
| `bloomBuilder.kedaAutoscaling.behavior` | object | Behavior configuration for KEDA ScaledObject (horizontalPodAutoscalerConfig.behavior). Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#advanced | `{}` |
| `bloomBuilder.kedaAutoscaling.cooldownPeriod` | string | Period to wait after the last trigger reported active before scaling the resource back to 0 (seconds). Defaults to defaults.kedaAutoscaling.cooldownPeriod if not set. | `nil` |
| `bloomBuilder.kedaAutoscaling.enabled` | bool | Enable KEDA autoscaling for the bloom-builder. Mutually exclusive with autoscaling.enabled | `false` |
| `bloomBuilder.kedaAutoscaling.fallback` | object | Fallback configuration for KEDA ScaledObject. Applied when the trigger metric cannot be retrieved. Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#fallback | `{}` |
| `bloomBuilder.kedaAutoscaling.maxReplicas` | int | Maximum replicas for KEDA autoscaling | `3` |
| `bloomBuilder.kedaAutoscaling.minReplicas` | int | Minimum replicas for KEDA autoscaling | `1` |
| `bloomBuilder.kedaAutoscaling.pollingInterval` | string | Interval (seconds) to poll each trigger. Defaults to defaults.kedaAutoscaling.pollingInterval if not set. | `nil` |
| `bloomBuilder.kedaAutoscaling.triggers` | list | KEDA triggers for the bloom-builder. Ref: https://keda.sh/docs/latest/scalers/ | `[]` |
| `bloomBuilder.kind` | string | Kind of compactor deployment. StatefulSet and Deployment are supported. | `"Deployment"` |
| `bloomBuilder.labels` | object | Labels for bloom-builder | `{}` |
| `bloomBuilder.maxUnavailable` | string | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `nil` |
| `bloomBuilder.nodeSelector` | object | Node selector for bloom-builder pods | `{}` |
| `bloomBuilder.persistence.accessModes` | list | Set access modes on the generated volumeClaimTemplate. | `["ReadWriteOnce"]` |
| `bloomBuilder.persistence.annotations` | object | Annotations for the generated volumeClaimTemplate. | `{}` |
| `bloomBuilder.persistence.enabled` | bool | Enable generic ephemeral volume-backed storage for the bloom-builder `data` volume. | `false` |
| `bloomBuilder.persistence.labels` | object | Labels for the generated volumeClaimTemplate. | `{}` |
| `bloomBuilder.persistence.selector` | string | Selector for the generated volumeClaimTemplate. | `nil` |
| `bloomBuilder.persistence.size` | string | Size of the generated volumeClaimTemplate. | `"10Gi"` |
| `bloomBuilder.persistence.storageClass` | string | Storage class to be used for the generated volumeClaimTemplate. If defined, storageClassName: &lt;storageClass&gt;. If set to "-", storageClassName: "", which disables dynamic provisioning. If empty or set to null, no storageClassName spec is set, choosing the default provisioner. | `nil` |
| `bloomBuilder.persistence.type` | string | Storage type for the bloom-builder. pvc and ephemeral are supported. | `"ephemeral"` |
| `bloomBuilder.persistence.volumeAttributesClassName` | string | Volume attributes class name to be used for the generated volumeClaimTemplate. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `bloomBuilder.podAnnotations` | object | Annotations for bloom-builder pods | `{}` |
| `bloomBuilder.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `bloomBuilder.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `bloomBuilder.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `bloomBuilder.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `bloomBuilder.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `bloomBuilder.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `bloomBuilder.podLabels` | object | Labels for bloom-builder pods | `{}` |
| `bloomBuilder.priorityClassName` | string | The name of the PriorityClass for bloom-builder pods | `nil` |
| `bloomBuilder.replicas` | int | Number of replicas for the bloom-builder. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `0` |
| `bloomBuilder.resizePolicy` | list | Container resize policy for the bloom-builder Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `bloomBuilder.resources` | object | Resource requests and limits for the bloom-builder | `{}` |
| `bloomBuilder.service.annotations` | object | Annotations for bloom-builder Service | `{}` |
| `bloomBuilder.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `bloomBuilder.service.ipFamilies` | list | ipFamilies for bloom-builder Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `bloomBuilder.service.ipFamilyPolicy` | string | ipFamilyPolicy for bloom-builder Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `bloomBuilder.service.labels` | object | Additional labels for bloom-builder Service | `{}` |
| `bloomBuilder.service.sessionAffinity` | string | Session affinity for bloom-builder Service | `""` |
| `bloomBuilder.service.sessionAffinityConfig` | object | Session affinity config for bloom-builder Service | `{}` |
| `bloomBuilder.service.trafficDistribution` | string | trafficDistribution for bloom-builder Service | `""` |
| `bloomBuilder.service.type` | string | Service Type for bloom-builder Service | `"ClusterIP"` |
| `bloomBuilder.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `bloomBuilder.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `bloomBuilder.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `bloomBuilder.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `bloomBuilder.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `bloomBuilder.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `bloomBuilder.serviceAnnotations` | object | Annotations for bloom-builder service @deprecated -- This option is deprecated in favor of `bloomBuilder.service.annotations` | `{}` |
| `bloomBuilder.serviceLabels` | object | Labels for bloom-builder service @deprecated -- This option is deprecated in favor of `bloomBuilder.service.labels` | `{}` |
| `bloomBuilder.statefulSetRecreateJob` | object | Enable creating a Job to recreate the compactor StatefulSet when the compactor configuration is changed. # Only applicable if `compactor.kind` is StatefulSet. Useful for PVC size changes, which require recreating the StatefulSet. | `{"enabled":false}` |
| `bloomBuilder.strategy.rollingUpdate.maxUnavailable` | int | Pod Disruption Budget maxUnavailable | `1` |
| `bloomBuilder.terminationGracePeriodSeconds` | int | Grace period to allow the bloom-builder to shutdown before it is killed | `30` |
| `bloomBuilder.tolerations` | list | Tolerations for bloom-builder pods | `[]` |

## bloomGateway

Configuration for the bloom-gateway

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `bloomGateway.affinity` | object | Affinity for bloom-gateway pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `bloomGateway.annotations` | object | Annotations for bloom-builder | `{}` |
| `bloomGateway.appProtocol` | object | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" @deprecated -- This option is deprecated in favor of `bloomGateway.service.appProtocol` | `{"grpc":""}` |
| `bloomGateway.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `bloomGateway.dnsConfig` | object | DNSConfig for bloom-gateway pods | `{}` |
| `bloomGateway.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `Distributed`. | `false` |
| `bloomGateway.extraArgs` | list | Additional CLI args for the bloom-gateway | `[]` |
| `bloomGateway.extraContainers` | list | Containers to add to the bloom-gateway pods | `[]` |
| `bloomGateway.extraEnv` | list | Environment variables to add to the bloom-gateway pods | `[]` |
| `bloomGateway.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the bloom-gateway pods | `[]` |
| `bloomGateway.extraVolumeMounts` | list | Volume mounts to add to the bloom-gateway pods | `[]` |
| `bloomGateway.extraVolumes` | list | Volumes to add to the bloom-gateway pods | `[]` |
| `bloomGateway.hostAliases` | list | hostAliases to add | `[]` |
| `bloomGateway.hostUsers` | string | Use the host's user namespace in the bloom-gateway | `nil` |
| `bloomGateway.image.registry` | string | The Docker registry for the bloom-gateway image. Overrides `loki.image.registry` | `nil` |
| `bloomGateway.image.repository` | string | Docker image repository for the bloom-gateway image. Overrides `loki.image.repository` | `nil` |
| `bloomGateway.image.tag` | string | Docker image tag for the bloom-gateway image. Overrides `loki.image.tag` | `nil` |
| `bloomGateway.initContainers` | list | Init containers to add to the bloom-gateway pods | `[]` |
| `bloomGateway.kind` | string | Kind of compactor deployment. StatefulSet and Deployment are supported. | `"StatefulSet"` |
| `bloomGateway.labels` | object | Labels for bloom-builder | `{}` |
| `bloomGateway.livenessProbe` | object | liveness probe settings for bloom-gateway pods. If empty use `loki.livenessProbe` | `{}` |
| `bloomGateway.nodeSelector` | object | Node selector for bloom-gateway pods | `{}` |
| `bloomGateway.persistence.annotations` | object | Annotations for bloom-gateway PVCs | `{}` |
| `bloomGateway.persistence.claims` | list | List of the bloom-gateway PVCs |  |
| `bloomGateway.persistence.claims[0].accessModes` | list | Set access modes on the PersistentVolumeClaim | `["ReadWriteOnce"]` |
| `bloomGateway.persistence.claims[0].size` | string | Size of persistent disk | `"10Gi"` |
| `bloomGateway.persistence.claims[0].volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `bloomGateway.persistence.enableStatefulSetAutoDeletePVC` | bool | Enable StatefulSetAutoDeletePVC feature | `false` |
| `bloomGateway.persistence.enabled` | bool | Enable creating PVCs for the bloom-gateway | `false` |
| `bloomGateway.persistence.labels` | object | Labels for bloom gateway PVCs | `{}` |
| `bloomGateway.podAnnotations` | object | Annotations for bloom-gateway pods | `{}` |
| `bloomGateway.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `bloomGateway.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `bloomGateway.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `bloomGateway.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `bloomGateway.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `bloomGateway.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `bloomGateway.podLabels` | object | Labels for bloom-gateway pods | `{}` |
| `bloomGateway.podManagementPolicy` | string | The default is to deploy all pods in parallel. | `"Parallel"` |
| `bloomGateway.priorityClassName` | string | The name of the PriorityClass for bloom-gateway pods | `nil` |
| `bloomGateway.readinessProbe` | object | readiness probe settings for bloom-gateway pods. If empty, use `loki.readinessProbe` | `{}` |
| `bloomGateway.replicas` | int | Number of replicas for the bloom-gateway | `0` |
| `bloomGateway.resizePolicy` | list | Container resize policy for the bloom-gateway Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `bloomGateway.resources` | object | Resource requests and limits for the bloom-gateway | `{}` |
| `bloomGateway.service.annotations` | object | Annotations for bloom-gateway Service | `{}` |
| `bloomGateway.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `bloomGateway.service.ipFamilies` | list | ipFamilies for bloom-gateway Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `bloomGateway.service.ipFamilyPolicy` | string | ipFamilyPolicy for bloom-gateway Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `bloomGateway.service.labels` | object | Additional labels for bloom-gateway Service | `{}` |
| `bloomGateway.service.sessionAffinity` | string | Session affinity for bloom-gateway Service | `""` |
| `bloomGateway.service.sessionAffinityConfig` | object | Session affinity config for bloom-gateway Service | `{}` |
| `bloomGateway.service.trafficDistribution` | string | trafficDistribution for bloom-gateway Service | `""` |
| `bloomGateway.service.type` | string | Service Type for bloom-gateway Service | `"ClusterIP"` |
| `bloomGateway.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `bloomGateway.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `bloomGateway.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `bloomGateway.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `bloomGateway.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `bloomGateway.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `bloomGateway.serviceAnnotations` | object | Annotations for bloom-gateway service @deprecated -- This option is deprecated in favor of `bloomGateway.service.annotations` | `{}` |
| `bloomGateway.serviceLabels` | object | Labels for bloom-gateway service @deprecated -- This option is deprecated in favor of `bloomGateway.service.labels` | `{}` |
| `bloomGateway.startupProbe` | object | startup probe settings for bloom-gateway pods. If empty, use `loki.startupProbe` | `{}` |
| `bloomGateway.statefulSetRecreateJob` | object | Enable creating a Job to recreate the StatefulSet when the StatefulSet configuration is changed. # Only applicable if `compactor.kind` is StatefulSet. Useful for PVC size changes, which require recreating the StatefulSet. | `{"enabled":false}` |
| `bloomGateway.strategy` | object | UpdateStrategy or Strategy for the pattern ingester. | `{"rollingUpdate":{"partition":0}}` |
| `bloomGateway.terminationGracePeriodSeconds` | int | Grace period to allow the bloom-gateway to shutdown before it is killed | `30` |
| `bloomGateway.tolerations` | list | Tolerations for bloom-gateway pods | `[]` |

## bloomPlanner

Configuration for the bloom-planner

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `bloomPlanner.affinity` | object | Affinity for bloom-planner pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `bloomPlanner.annotations` | object | Annotations for bloom-builder | `{}` |
| `bloomPlanner.appProtocol` | object | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" @deprecated -- This option is deprecated in favor of `bloomPlanner.service.appProtocol` | `{"grpc":""}` |
| `bloomPlanner.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `bloomPlanner.dnsConfig` | object | DNSConfig for bloom-planner pods | `{}` |
| `bloomPlanner.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `Distributed`. | `false` |
| `bloomPlanner.extraArgs` | list | Additional CLI args for the bloom-planner | `[]` |
| `bloomPlanner.extraContainers` | list | Containers to add to the bloom-planner pods | `[]` |
| `bloomPlanner.extraEnv` | list | Environment variables to add to the bloom-planner pods | `[]` |
| `bloomPlanner.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the bloom-planner pods | `[]` |
| `bloomPlanner.extraVolumeMounts` | list | Volume mounts to add to the bloom-planner pods | `[]` |
| `bloomPlanner.extraVolumes` | list | Volumes to add to the bloom-planner pods | `[]` |
| `bloomPlanner.hostAliases` | list | hostAliases to add | `[]` |
| `bloomPlanner.hostUsers` | string | Use the host's user namespace in the bloom-planner | `nil` |
| `bloomPlanner.image.registry` | string | The Docker registry for the bloom-planner image. Overrides `loki.image.registry` | `nil` |
| `bloomPlanner.image.repository` | string | Docker image repository for the bloom-planner image. Overrides `loki.image.repository` | `nil` |
| `bloomPlanner.image.tag` | string | Docker image tag for the bloom-planner image. Overrides `loki.image.tag` | `nil` |
| `bloomPlanner.initContainers` | list | Init containers to add to the bloom-planner pods | `[]` |
| `bloomPlanner.kind` | string | Kind of compactor deployment. StatefulSet and Deployment are supported. | `"StatefulSet"` |
| `bloomPlanner.labels` | object | Labels for bloom-builder | `{}` |
| `bloomPlanner.livenessProbe` | object | liveness probe settings for bloom-planner pods. If empty use `loki.livenessProbe` | `{}` |
| `bloomPlanner.nodeSelector` | object | Node selector for bloom-planner pods | `{}` |
| `bloomPlanner.persistence.claims` | list | List of the bloom-planner PVCs |  |
| `bloomPlanner.persistence.claims[0].accessModes` | list | Set access modes on the PersistentVolumeClaim | `["ReadWriteOnce"]` |
| `bloomPlanner.persistence.claims[0].annotations` | object | Annotations for bloom-planner PVCs | `{}` |
| `bloomPlanner.persistence.claims[0].labels` | object | Labels for bloom planner PVCs | `{}` |
| `bloomPlanner.persistence.claims[0].size` | string | Size of persistent disk | `"10Gi"` |
| `bloomPlanner.persistence.claims[0].volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `bloomPlanner.persistence.enableStatefulSetAutoDeletePVC` | bool | Enable StatefulSetAutoDeletePVC feature | `false` |
| `bloomPlanner.persistence.enabled` | bool | Enable creating PVCs for the bloom-planner | `false` |
| `bloomPlanner.podAnnotations` | object | Annotations for bloom-planner pods | `{}` |
| `bloomPlanner.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `bloomPlanner.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `bloomPlanner.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `bloomPlanner.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `bloomPlanner.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `bloomPlanner.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `bloomPlanner.podLabels` | object | Labels for bloom-planner pods | `{}` |
| `bloomPlanner.podManagementPolicy` | string | The default is to deploy all pods in parallel. | `"Parallel"` |
| `bloomPlanner.priorityClassName` | string | The name of the PriorityClass for bloom-planner pods | `nil` |
| `bloomPlanner.readinessProbe` | object | readiness probe settings for bloom-planner pods. If empty, use `loki.readinessProbe` | `{}` |
| `bloomPlanner.replicas` | int | Number of replicas for the bloom-planner | `0` |
| `bloomPlanner.resizePolicy` | list | Container resize policy for the bloom-planner Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `bloomPlanner.resources` | object | Resource requests and limits for the bloom-planner | `{}` |
| `bloomPlanner.service.annotations` | object | Annotations for bloom-planner Service | `{}` |
| `bloomPlanner.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `bloomPlanner.service.ipFamilies` | list | ipFamilies for bloom-planner Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `bloomPlanner.service.ipFamilyPolicy` | string | ipFamilyPolicy for bloom-planner Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `bloomPlanner.service.labels` | object | Additional labels for bloom-planner Service | `{}` |
| `bloomPlanner.service.sessionAffinity` | string | Session affinity for bloom-planner Service | `""` |
| `bloomPlanner.service.sessionAffinityConfig` | object | Session affinity config for bloom-planner Service | `{}` |
| `bloomPlanner.service.trafficDistribution` | string | trafficDistribution for bloom-planner Service | `""` |
| `bloomPlanner.service.type` | string | Service Type for bloom-planner Service | `"ClusterIP"` |
| `bloomPlanner.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `bloomPlanner.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `bloomPlanner.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `bloomPlanner.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `bloomPlanner.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `bloomPlanner.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `bloomPlanner.serviceAnnotations` | object | Annotations for bloom-planner service @deprecated -- This option is deprecated in favor of `bloomPlanner.service.annotations` | `{}` |
| `bloomPlanner.serviceLabels` | object | Labels for bloom-planner service @deprecated -- This option is deprecated in favor of `bloomPlanner.service.labels` | `{}` |
| `bloomPlanner.startupProbe` | object | startup probe settings for bloom-planner pods. If empty use `loki.startupProbe` | `{}` |
| `bloomPlanner.statefulSetRecreateJob` | object | Enable creating a Job to recreate the StatefulSet when the StatefulSet configuration is changed. # Only applicable if `compactor.kind` is StatefulSet. Useful for PVC size changes, which require recreating the StatefulSet. | `{"enabled":false}` |
| `bloomPlanner.strategy` | object | UpdateStrategy or Strategy for the pattern ingester. | `{"rollingUpdate":{"partition":0}}` |
| `bloomPlanner.terminationGracePeriodSeconds` | int | Grace period to allow the bloom-planner to shutdown before it is killed | `30` |
| `bloomPlanner.tolerations` | list | Tolerations for bloom-planner pods | `[]` |

## chunksCache

Configuration for the Memcached-based chunks-cache, which caches the log chunks Loki fetches from object storage.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `chunksCache.addresses` | string | Comma separated addresses list in DNS Service Discovery format | `"dnssrvnoa+_memcached-client._tcp.{{ include \"loki.resourceName\" (dict \"ctx\" $ \"component\" \"chunks-cache\" \"suffix\" $.Values.chunksCache.suffix ) }}.{{ include \"loki.namespace\" $ }}.svc.{{ .Values.global.clusterDomain }}"` |
| `chunksCache.affinity` | object | Affinity for chunks-cache pods | `{}` |
| `chunksCache.allocatedCPU` | string | Amount of cpu allocated to chunks-cache for object storage (in integer or millicores). | `"500m"` |
| `chunksCache.allocatedMemory` | int | Amount of memory allocated to chunks-cache for object storage (in MB). | `8192` |
| `chunksCache.annotations` | object | Annotations for the chunks-cache pods | `{}` |
| `chunksCache.batchSize` | int | Batchsize for sending and receiving chunks from chunks cache | `4` |
| `chunksCache.connectionLimit` | int | Maximum number of connections allowed | `16384` |
| `chunksCache.defaultValidity` | string | Specify how long cached chunks should be stored in the chunks-cache before being expired | `"0s"` |
| `chunksCache.dnsConfig` | object | DNSConfig for chunks-cache | `{}` |
| `chunksCache.enabled` | bool | Specifies whether memcached based chunks-cache should be enabled | `true` |
| `chunksCache.extraArgs` | object | Additional CLI args for chunks-cache | `{}` |
| `chunksCache.extraContainers` | list | Additional containers to be added to the chunks-cache pod. | `[]` |
| `chunksCache.extraExtendedOptions` | string | Add extended options for chunks-cache memcached container. The format is the same as for the memcached -o/--extend flag. Example: extraExtendedOptions: 'tls,no_hashexpand' | `""` |
| `chunksCache.extraVolumeMounts` | list | Additional volume mounts to be added to the chunks-cache pod (applies to both memcached and exporter containers). Example: extraVolumeMounts: - name: extra-volume mountPath: /etc/extra-volume readOnly: true | `[]` |
| `chunksCache.extraVolumes` | list | Additional volumes to be added to the chunks-cache pod (applies to both memcached and exporter containers). Example: extraVolumes: - name: extra-volume secret: secretName: extra-volume-secret | `[]` |
| `chunksCache.hostUsers` | string | Use the host's user namespace in chunks-cache pods | `nil` |
| `chunksCache.initContainers` | list | Extra init containers for chunks-cache pods | `[]` |
| `chunksCache.l2.addresses` | string | Comma separated addresses list in DNS Service Discovery format | `"dnssrvnoa+_memcached-client._tcp.{{ include \"loki.resourceName\" (dict \"ctx\" $ \"component\" \"chunks-cache\" \"suffix\" $.Values.chunksCache.l2.suffix ) }}.{{ include \"loki.namespace\" $ }}.svc.{{ .Values.global.clusterDomain }}"` |
| `chunksCache.l2.affinity` | object | Affinity for chunks-cache-l2 pods | `{}` |
| `chunksCache.l2.allocatedCPU` | string | Amount of cpu allocated to chunks-cache-l2 for object storage (in integer or millicores). | `"500m"` |
| `chunksCache.l2.allocatedMemory` | int | Amount of memory allocated to chunks-cache-l2 for object storage (in MB). | `8192` |
| `chunksCache.l2.annotations` | object | Annotations for the chunks-cache-l2 pods | `{}` |
| `chunksCache.l2.batchSize` | int | Batchsize for sending and receiving chunks from chunks cache | `4` |
| `chunksCache.l2.connectionLimit` | int | Maximum number of connections allowed | `16384` |
| `chunksCache.l2.defaultValidity` | string | Specify how long cached chunks should be stored in the chunks-cache-l2 before being expired | `"0s"` |
| `chunksCache.l2.dnsConfig` | object | DNSConfig for chunks-cache-l2 | `{}` |
| `chunksCache.l2.enabled` | bool | Specifies whether memcached based chunks-cache-l2 should be enabled | `false` |
| `chunksCache.l2.extraArgs` | object | Additional CLI args for chunks-cache-l2 | `{}` |
| `chunksCache.l2.extraContainers` | list | Additional containers to be added to the chunks-cache-l2 pod. | `[]` |
| `chunksCache.l2.extraExtendedOptions` | string | Add extended options for chunks-cache-l2 memcached container. The format is the same as for the memcached -o/--extend flag. Example: extraExtendedOptions: 'tls,no_hashexpand' | `""` |
| `chunksCache.l2.extraVolumeMounts` | list | Additional volume mounts to be added to the chunks-cache-l2 pod (applies to both memcached and exporter containers). Example: extraVolumeMounts: - name: extra-volume mountPath: /etc/extra-volume readOnly: true | `[]` |
| `chunksCache.l2.extraVolumes` | list | Additional volumes to be added to the chunks-cache-l2 pod (applies to both memcached and exporter containers). Example: extraVolumes: - name: extra-volume secret: secretName: extra-volume-secret | `[]` |
| `chunksCache.l2.hostUsers` | string | Use the host's user namespace in chunks-cache-l2 pods | `nil` |
| `chunksCache.l2.initContainers` | list | Extra init containers for chunks-cache-l2 pods | `[]` |
| `chunksCache.l2.l2ChunkCacheHandoff` | string | The age of chunks should be transferred from l1 cache to l2 4 days | `"345600s"` |
| `chunksCache.l2.maxItemMemory` | int | Maximum item memory for chunks-cache-l2 (in MB). | `5` |
| `chunksCache.l2.maxUnavailable` | int | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `1` |
| `chunksCache.l2.nodeSelector` | object | Node selector for chunks-cach-l2 pods | `{}` |
| `chunksCache.l2.parallelism` | int | Parallel threads for sending and receiving chunks from chunks cache | `5` |
| `chunksCache.l2.persistence.enabled` | bool | Enable creating PVCs for the chunks-cache-l2 | `false` |
| `chunksCache.l2.persistence.mountPath` | string | Volume mount path | `"/data"` |
| `chunksCache.l2.persistence.storageClass` | string | Storage class to be used. If defined, storageClassName: &lt;storageClass&gt;. If set to "-", storageClassName: "", which disables dynamic provisioning. If empty or set to null, no storageClassName spec is set, choosing the default provisioner (gp2 on AWS, standard on GKE, AWS, and OpenStack). | `nil` |
| `chunksCache.l2.persistence.storageSize` | string | Size of persistent disk, must be in G or Gi | `"10G"` |
| `chunksCache.l2.persistence.volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `chunksCache.l2.podAnnotations` | object | Annotations for chunks-cache-l2 pods | `{}` |
| `chunksCache.l2.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `chunksCache.l2.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `chunksCache.l2.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `chunksCache.l2.podLabels` | object | Labels for chunks-cache-l2 pods | `{}` |
| `chunksCache.l2.podManagementPolicy` | string | Management policy for chunks-cache-l2 pods | `"Parallel"` |
| `chunksCache.l2.port` | int | Port of the chunks-cache-l2 service | `11211` |
| `chunksCache.l2.priorityClassName` | string | The name of the PriorityClass for chunks-cache-l2 pods | `nil` |
| `chunksCache.l2.replicas` | int | Total number of chunks-cache-l2 replicas | `1` |
| `chunksCache.l2.resizePolicy` | list | Container resize policy for the chunks-cache-l2 Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `chunksCache.l2.resources` | object | Resource requests and limits for the chunks-cache-l2 By default a safe memory limit will be requested based on allocatedMemory value (floor (* 1.2 allocatedMemory)). | `{}` |
| `chunksCache.l2.service` | object | Service annotations and labels | `{"annotations":{},"labels":{}}` |
| `chunksCache.l2.statefulStrategy` | object | Stateful chunks-cache strategy | `{"type":"RollingUpdate"}` |
| `chunksCache.l2.suffix` | string | Append to the name of the resources to make names different for l1 and l2 | `"l2"` |
| `chunksCache.l2.terminationGracePeriodSeconds` | int | Grace period to allow the chunks-cache-l2 to shutdown before it is killed | `60` |
| `chunksCache.l2.timeout` | string | Memcached operation timeout | `"2000ms"` |
| `chunksCache.l2.tolerations` | list | Tolerations for chunks-cache-l2 pods | `[]` |
| `chunksCache.l2.topologySpreadConstraints` | list | topologySpreadConstraints allows to customize the default topologySpreadConstraints. This can be either a single dict as shown below or a slice of topologySpreadConstraints. labelSelector is taken from the constraint itself (if it exists) or is generated by the chart using the same selectors as for services. | `[]` |
| `chunksCache.l2.writebackBuffer` | int | Max number of objects to use for cache write back | `500000` |
| `chunksCache.l2.writebackParallelism` | int | Number of parallel threads for cache write back | `1` |
| `chunksCache.l2.writebackSizeLimit` | string | Max memory to use for cache write back | `"500MB"` |
| `chunksCache.maxItemMemory` | int | Maximum item memory for chunks-cache (in MB). | `5` |
| `chunksCache.maxUnavailable` | int | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `1` |
| `chunksCache.nodeSelector` | object | Node selector for chunks-cache pods | `{}` |
| `chunksCache.parallelism` | int | Parallel threads for sending and receiving chunks from chunks cache | `5` |
| `chunksCache.persistence.enabled` | bool | Enable creating PVCs for the chunks-cache | `false` |
| `chunksCache.persistence.mountPath` | string | Volume mount path | `"/data"` |
| `chunksCache.persistence.storageClass` | string | Storage class to be used. If defined, storageClassName: &lt;storageClass&gt;. If set to "-", storageClassName: "", which disables dynamic provisioning. If empty or set to null, no storageClassName spec is set, choosing the default provisioner (gp2 on AWS, standard on GKE, AWS, and OpenStack). | `nil` |
| `chunksCache.persistence.storageSize` | string | Size of persistent disk, must be in G or Gi | `"10G"` |
| `chunksCache.persistence.volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `chunksCache.podAnnotations` | object | Annotations for chunks-cache pods | `{}` |
| `chunksCache.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `chunksCache.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `chunksCache.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `chunksCache.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `chunksCache.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `chunksCache.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `chunksCache.podLabels` | object | Labels for chunks-cache pods | `{}` |
| `chunksCache.podManagementPolicy` | string | Management policy for chunks-cache pods | `"Parallel"` |
| `chunksCache.port` | int | Port of the chunks-cache service | `11211` |
| `chunksCache.priorityClassName` | string | The name of the PriorityClass for chunks-cache pods | `nil` |
| `chunksCache.replicas` | int | Total number of chunks-cache replicas | `1` |
| `chunksCache.resizePolicy` | list | Container resize policy for the chunks-cache Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `chunksCache.resources` | object | Resource requests and limits for the chunks-cache By default a safe memory limit will be requested based on allocatedMemory value (floor (* 1.2 allocatedMemory)). | `{}` |
| `chunksCache.service` | object | Service annotations and labels | `{"annotations":{},"labels":{}}` |
| `chunksCache.statefulStrategy` | object | Stateful chunks-cache strategy | `{"type":"RollingUpdate"}` |
| `chunksCache.suffix` | string | Append to the name of the resources to make names different for l1 and l2 | `""` |
| `chunksCache.terminationGracePeriodSeconds` | int | Grace period to allow the chunks-cache to shutdown before it is killed | `60` |
| `chunksCache.timeout` | string | Memcached operation timeout | `"2000ms"` |
| `chunksCache.tolerations` | list | Tolerations for chunks-cache pods | `[]` |
| `chunksCache.topologySpreadConstraints` | list | topologySpreadConstraints allows to customize the default topologySpreadConstraints. This can be either a single dict as shown below or a slice of topologySpreadConstraints. labelSelector is taken from the constraint itself (if it exists) or is generated by the chart using the same selectors as for services. | `[]` |
| `chunksCache.writebackBuffer` | int | Max number of objects to use for cache write back | `500000` |
| `chunksCache.writebackParallelism` | int | Number of parallel threads for cache write back | `1` |
| `chunksCache.writebackSizeLimit` | string | Max memory to use for cache write back | `"500MB"` |

## compactor

Configuration for the compactor

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `compactor.affinity` | object | Affinity for compactor pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `compactor.annotations` | object | Annotations for compactor | `{}` |
| `compactor.appProtocol` | object | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" @deprecated -- This option is deprecated in favor of `compactor.service.appProtocol` | `{"grpc":""}` |
| `compactor.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `compactor.dnsConfig` | object | DNSConfig for compactor pods | `{}` |
| `compactor.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `Distributed`. | `true` |
| `compactor.extraArgs` | list | Additional CLI args for the compactor | `[]` |
| `compactor.extraContainers` | list | Containers to add to the compactor pods | `[]` |
| `compactor.extraEnv` | list | Environment variables to add to the compactor pods | `[]` |
| `compactor.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the compactor pods | `[]` |
| `compactor.extraVolumeMounts` | list | Volume mounts to add to the compactor pods | `[]` |
| `compactor.extraVolumes` | list | Volumes to add to the compactor pods | `[]` |
| `compactor.hostAliases` | list | hostAliases to add | `[]` |
| `compactor.hostUsers` | string | Use the host's user namespace in the compactor | `nil` |
| `compactor.image.registry` | string | The Docker registry for the compactor image. Overrides `loki.image.registry` | `nil` |
| `compactor.image.repository` | string | Docker image repository for the compactor image. Overrides `loki.image.repository` | `nil` |
| `compactor.image.tag` | string | Docker image tag for the compactor image. Overrides `loki.image.tag` | `nil` |
| `compactor.initContainers` | list | Init containers to add to the compactor pods | `[]` |
| `compactor.kind` | string | Kind of compactor deployment. StatefulSet and Deployment are supported. | `"StatefulSet"` |
| `compactor.labels` | object | Labels for compactor | `{}` |
| `compactor.livenessProbe` | object | liveness probe settings for compactor pods. If empty use `loki.livenessProbe` | `{}` |
| `compactor.nodeSelector` | object | Node selector for compactor pods | `{}` |
| `compactor.persistence.accessModes` | list | Set access modes on the PersistentVolumeClaim | `["ReadWriteOnce"]` |
| `compactor.persistence.annotations` | object | Annotations for the generated volumeClaimTemplate. | `{}` |
| `compactor.persistence.claims` | list | List of the compactor PVCs. Deprecated in favor of `compactor.persistence.dataVolumeParameters` and `compactor.persistence.size`. |  |
| `compactor.persistence.claims[0].accessModes` | list | Set access modes on the PersistentVolumeClaim | `["ReadWriteOnce"]` |
| `compactor.persistence.claims[0].annotations` | object | Annotations for compactor PVCs | `{}` |
| `compactor.persistence.claims[0].labels` | object | Labels for compactor PVCs | `{}` |
| `compactor.persistence.claims[0].volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `compactor.persistence.dataVolumeParameters` | object | Parameters used for the `data` volume when volumeClaimEnabled if false | `{"emptyDir":{}}` |
| `compactor.persistence.enableStatefulSetAutoDeletePVC` | bool | Enable StatefulSetAutoDeletePVC feature | `false` |
| `compactor.persistence.enabled` | bool | Enable creating PVCs for the compactor | `false` |
| `compactor.persistence.labels` | object | Labels for the generated volumeClaimTemplate. | `{}` |
| `compactor.persistence.selector` | string | Selector for the generated volumeClaimTemplate. | `nil` |
| `compactor.persistence.size` | string | Size of persistent disk | `"10Gi"` |
| `compactor.persistence.storageClass` | string | Storage class to be used. If defined, storageClassName: &lt;storageClass&gt;. If set to "-", storageClassName: "", which disables dynamic provisioning. If empty or set to null, no storageClassName spec is set, choosing the default provisioner (gp2 on AWS, standard on GKE, AWS, and OpenStack). | `nil` |
| `compactor.persistence.type` | string | Storage type for the compactor. pvc and ephemeral are supported. | `"pvc"` |
| `compactor.persistence.volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `compactor.podAnnotations` | object | Annotations for compactor pods | `{}` |
| `compactor.podLabels` | object | Labels for compactor pods | `{}` |
| `compactor.podManagementPolicy` | string | PodManagementPolicy for the compactor StatefulSet. Only applicable if `compactor.kind` is StatefulSet. # OrderedReady and Parallel are supported. | `"Parallel"` |
| `compactor.priorityClassName` | string | The name of the PriorityClass for compactor pods | `nil` |
| `compactor.readinessProbe` | object | readiness probe settings for compactor pods. If empty, use `loki.readinessProbe` | `{}` |
| `compactor.replicas` | int | Number of replicas for the compactor. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `0` |
| `compactor.resizePolicy` | list | Container resize policy for the compactor Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `compactor.resources` | object | Resource requests and limits for the compactor | `{}` |
| `compactor.service.annotations` | object | Annotations for compactor Service | `{}` |
| `compactor.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `compactor.service.ipFamilies` | list | ipFamilies for compactor Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `compactor.service.ipFamilyPolicy` | string | ipFamilyPolicy for compactor Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `compactor.service.labels` | object | Additional labels for compactor Service | `{}` |
| `compactor.service.sessionAffinity` | string | Session affinity for compactor Service | `""` |
| `compactor.service.sessionAffinityConfig` | object | Session affinity config for compactor Service | `{}` |
| `compactor.service.trafficDistribution` | string | trafficDistribution for compactor Service | `""` |
| `compactor.service.type` | string | Service Type for compactor Service | `"ClusterIP"` |
| `compactor.serviceAccount.annotations` | object | Annotations for the compactor service account | `{}` |
| `compactor.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `compactor.serviceAccount.imagePullSecrets` | list | Image pull secrets for the compactor service account | `[]` |
| `compactor.serviceAccount.name` | string | The name of the ServiceAccount to use for the compactor. If not set and create is true, a name is generated by appending "-compactor" to the common ServiceAccount. | `nil` |
| `compactor.serviceAnnotations` | object | Annotations for compactor service @deprecated -- This option is deprecated in favor of `compactor.service.annotations` | `{}` |
| `compactor.serviceLabels` | object | Labels for compactor service @deprecated -- This option is deprecated in favor of `compactor.service.labels` | `{}` |
| `compactor.serviceType` | string | Service type for compactor service @deprecated -- This option is deprecated in favor of `compactor.service.type` | `"ClusterIP"` |
| `compactor.startupProbe` | object | liveness probe settings for ingester pods. If empty use `loki.livenessProbe` | `{}` |
| `compactor.statefulSetRecreateJob` | object | EXPERIMENTAL: Enable creating a Job to recreate the compactor StatefulSet when the compactor configuration is changed. # Only applicable if `compactor.kind` is StatefulSet. Useful for PVC size changes, which require recreating the StatefulSet. | `{"enabled":false}` |
| `compactor.strategy` | object | UpdateStrategy or Strategy for the compactor. | `{"rollingUpdate":{"partition":0}}` |
| `compactor.terminationGracePeriodSeconds` | int | Grace period to allow the compactor to shutdown before it is killed | `30` |
| `compactor.tolerations` | list | Tolerations for compactor pods | `[]` |

## defaults

Default settings applied to the Loki components only. Not applied to loki-canary, memcached, the gateway/nginx, or other components.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `defaults.annotations` | object | Annotations for loki Deployments/StatefulSets | `{}` |
| `defaults.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `defaults.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `defaults.containerSecurityContext` | object | The SecurityContext for Loki containers for loki pods | `{}` |
| `defaults.dnsConfig` | object | DNSConfig for loki pods | `{}` |
| `defaults.enableServiceLinks` | bool | Should enableServiceLinks be enabled. Default to enable | `true` |
| `defaults.extraArgs` | list | Common additional CLI arguments for loki pods | `[]` |
| `defaults.extraEnv` | list | Common environment variables to add to all pods directly managed by this chart. | `[]` |
| `defaults.extraEnvFrom` | list | Common source of environment injections to add to all pods directly managed by this chart. For example to inject values from a Secret, use: extraEnvFrom: - secretRef: name: mysecret | `[]` |
| `defaults.extraVolumeMounts` | list | Common mount points to add to all pods directly managed by this chart. | `[]` |
| `defaults.extraVolumes` | list | Common volumes to add to all pods directly managed by this chart. | `[]` |
| `defaults.goSettings.goMemLimitFactor` | float | Fraction of the container memory limit used to compute GOMEMLIMIT (e.g. 0.85 = 85%). Set to 0 to disable automatic GOMEMLIMIT injection. | `0.85` |
| `defaults.goSettings.gogc` | int | Value to set for GOGC alongside GOMEMLIMIT. Lowering below the default of 100 reduces heap growth between GC cycles, working in tandem with GOMEMLIMIT to smooth memory usage. Set to 0 to disable automatic GOGC injection. | `80` |
| `defaults.hostAliases` | list | hostAliases for loki pods | `[]` |
| `defaults.hostUsers` | string | Use the host's user namespace for loki pods | `nil` |
| `defaults.kedaAutoscaling.authentication.authModes` | string | Authentication modes for the trigger (e.g. "bearer", "basic") | `""` |
| `defaults.kedaAutoscaling.authentication.enabled` | bool | Enable TriggerAuthentication for KEDA triggers | `false` |
| `defaults.kedaAutoscaling.authentication.secretTargetRef` | list | Secret key references for TriggerAuthentication | `[]` |
| `defaults.kedaAutoscaling.cooldownPeriod` | int | Period (seconds) to wait after the last trigger reported active before scaling back to 0. Overridable per component. | `300` |
| `defaults.kedaAutoscaling.customHeaders` | string | Custom HTTP headers to include in Prometheus requests. Useful for multi-tenant setups (e.g. "X-Scope-OrgID=tenant1") | `""` |
| `defaults.kedaAutoscaling.ignoreNullValues` | bool | Whether to ignore null values from Prometheus queries | `true` |
| `defaults.kedaAutoscaling.pollingInterval` | int | Interval (seconds) to poll each trigger. Overridable per component. | `30` |
| `defaults.kedaAutoscaling.prometheusAddress` | string | Prometheus server address for KEDA triggers | `""` |
| `defaults.kedaAutoscaling.unsafeSsl` | bool | Whether to skip SSL verification when connecting to Prometheus | `false` |
| `defaults.labels` | object | Additional labels for loki Deployments/StatefulSets | `{}` |
| `defaults.livenessProbe` | object | Configures the liveness probe for loki pods | `{}` |
| `defaults.nodeSelector` | object | Node selector for loki pods | `{}` |
| `defaults.podAnnotations` | object | Annotations for loki pods | `{}` |
| `defaults.podLabels` | object | Additional labels for loki pod | `{}` |
| `defaults.podSecurityContext` | object | The SecurityContext for loki pods | `{}` |
| `defaults.priorityClassName` | string | The name of the PriorityClass for loki pods | `""` |
| `defaults.readinessProbe` | object | Configures the readiness probe for loki pods | `{}` |
| `defaults.service.annotations` | object | Common annotations for all services | `{}` |
| `defaults.service.ipFamilies` | list | ipFamilies for all services Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `defaults.service.ipFamilyPolicy` | string | ipFamilyPolicy for all services Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `defaults.service.labels` | object | Common labels for all services | `{}` |
| `defaults.service.publishNotReadyAddresses` | bool | publishNotReadyAddresses for all services. Set to false so a pod's Service endpoint is withdrawn as soon as it becomes unready, instead of staying routable for the full termination window. Overridable per component via &lt;component&gt;.service.publishNotReadyAddresses. | `true` |
| `defaults.service.trafficDistribution` | string | trafficDistribution for services Ref: https://kubernetes.io/docs/concepts/services-networking/service/#traffic-distribution | `""` |
| `defaults.startupProbe` | object | Configures the startup probe for loki pods | `{}` |
| `defaults.statefulSetRecreateJob.image.registry` | string |  | `"registry.k8s.io"` |
| `defaults.statefulSetRecreateJob.image.repository` | string |  | `"kubectl"` |
| `defaults.statefulSetRecreateJob.image.tag` | string | Overrides the image tag. Defaults to .Capabilities.KubeVersion.Version | `""` |
| `defaults.statefulSetRecreateJob.patchPVC` | bool | When a component sets `*.statefulSetRecreateJob.enabled`, the pre-upgrade Job can patch PVC storage requests and recreate the StatefulSet for podManagementPolicy, serviceName, or volumeClaimTemplates count/name/size changes. It does not migrate storageClass, accessModes, selectors, or VCT labels. See the chart README section StatefulSet immutability. | `true` |
| `defaults.terminationGracePeriodSeconds` | int | Grace period to allow the gateway to shutdown before it is killed | `30` |
| `defaults.tolerations` | list | Tolerations for loki pods | `[]` |

## distributor

Configuration for the distributor

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `distributor.affinity` | object | Affinity for distributor pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `distributor.annotations` | object | Annotations for distributor | `{}` |
| `distributor.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" @deprecated -- This option is deprecated in favor of `distributor.service.appProtocol.grpc` | `""` |
| `distributor.autoscaling.behavior.enabled` | bool | Enable autoscaling behaviours | `false` |
| `distributor.autoscaling.behavior.scaleDown` | object | define scale down policies, must conform to HPAScalingRules | `{}` |
| `distributor.autoscaling.behavior.scaleUp` | object | define scale up policies, must conform to HPAScalingRules | `{}` |
| `distributor.autoscaling.customMetrics` | list | Allows one to define custom metrics using the HPA/v2 schema (for example, Pods, Object or External metrics) | `[]` |
| `distributor.autoscaling.enabled` | bool | Enable autoscaling for the distributor | `false` |
| `distributor.autoscaling.maxReplicas` | int | Maximum autoscaling replicas for the distributor | `3` |
| `distributor.autoscaling.minReplicas` | int | Minimum autoscaling replicas for the distributor | `1` |
| `distributor.autoscaling.targetCPUUtilizationPercentage` | int | Target CPU utilisation percentage for the distributor | `60` |
| `distributor.autoscaling.targetMemoryUtilizationPercentage` | string | Target memory utilisation percentage for the distributor | `nil` |
| `distributor.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `distributor.dnsConfig` | object | DNSConfig for distributor pods | `{}` |
| `distributor.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `Distributed`. | `true` |
| `distributor.extraArgs` | list | Additional CLI args for the distributor | `[]` |
| `distributor.extraContainers` | list | Containers to add to the distributor pods | `[]` |
| `distributor.extraEnv` | list | Environment variables to add to the distributor pods | `[]` |
| `distributor.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the distributor pods | `[]` |
| `distributor.extraVolumeMounts` | list | Volume mounts to add to the distributor pods | `[]` |
| `distributor.extraVolumes` | list | Volumes to add to the distributor pods | `[]` |
| `distributor.hostAliases` | list | hostAliases to add | `[]` |
| `distributor.hostUsers` | string | Use the host's user namespace in the distributor | `nil` |
| `distributor.image.registry` | string | The Docker registry for the distributor image. Overrides `loki.image.registry` | `nil` |
| `distributor.image.repository` | string | Docker image repository for the distributor image. Overrides `loki.image.repository` | `nil` |
| `distributor.image.tag` | string | Docker image tag for the distributor image. Overrides `loki.image.tag` | `nil` |
| `distributor.initContainers` | list | Init containers to add to the distributor pods | `[]` |
| `distributor.kedaAutoscaling.behavior` | object | Behavior configuration for KEDA ScaledObject (horizontalPodAutoscalerConfig.behavior). Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#advanced | `{}` |
| `distributor.kedaAutoscaling.cooldownPeriod` | string | Period to wait after the last trigger reported active before scaling the resource back to 0 (seconds). Defaults to defaults.kedaAutoscaling.cooldownPeriod if not set. | `nil` |
| `distributor.kedaAutoscaling.enabled` | bool | Enable KEDA autoscaling for the distributor. Mutually exclusive with autoscaling.enabled | `false` |
| `distributor.kedaAutoscaling.fallback` | object | Fallback configuration for KEDA ScaledObject. Applied when the trigger metric cannot be retrieved. Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#fallback | `{}` |
| `distributor.kedaAutoscaling.maxReplicas` | int | Maximum replicas for KEDA autoscaling | `3` |
| `distributor.kedaAutoscaling.minReplicas` | int | Minimum replicas for KEDA autoscaling | `1` |
| `distributor.kedaAutoscaling.pollingInterval` | string | Interval (seconds) to poll each trigger. Defaults to defaults.kedaAutoscaling.pollingInterval if not set. | `nil` |
| `distributor.kedaAutoscaling.triggers` | list | KEDA triggers for the distributor. Ref: https://keda.sh/docs/latest/scalers/ | `[]` |
| `distributor.kind` | string | Kind of compactor deployment. StatefulSet and Deployment are supported. | `"Deployment"` |
| `distributor.labels` | object | Labels for distributor | `{}` |
| `distributor.maxSurge` | int | Max Surge for distributor pods | `0` |
| `distributor.maxUnavailable` | string | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `nil` |
| `distributor.nodeSelector` | object | Node selector for distributor pods | `{}` |
| `distributor.podAnnotations` | object | Annotations for distributor pods | `{}` |
| `distributor.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `distributor.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `distributor.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `distributor.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `distributor.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `distributor.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `distributor.podLabels` | object | Labels for distributor pods | `{}` |
| `distributor.priorityClassName` | string | The name of the PriorityClass for distributor pods | `nil` |
| `distributor.replicas` | int | Number of replicas for the distributor. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `0` |
| `distributor.resizePolicy` | list | Container resize policy for the distributor Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `distributor.resources` | object | Resource requests and limits for the distributor | `{}` |
| `distributor.service.annotations` | object | Annotations for distributor Service | `{}` |
| `distributor.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `distributor.service.ipFamilies` | list | ipFamilies for distributor Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `distributor.service.ipFamilyPolicy` | string | ipFamilyPolicy for distributor Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `distributor.service.labels` | object | Additional labels for distributor Service | `{}` |
| `distributor.service.sessionAffinity` | string | Session affinity for distributor Service | `""` |
| `distributor.service.sessionAffinityConfig` | object | Session affinity config for distributor Service | `{}` |
| `distributor.service.trafficDistribution` | string | trafficDistribution for distributor Service | `""` |
| `distributor.service.type` | string | Service Type for distributor Service | `"ClusterIP"` |
| `distributor.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `distributor.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `distributor.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `distributor.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `distributor.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `distributor.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `distributor.serviceAnnotations` | object | Annotations for distributor service @deprecated -- This option is deprecated in favor of `ingester.service.annotations` | `{}` |
| `distributor.serviceLabels` | object | Labels for distributor service @deprecated -- This option is deprecated in favor of `ingester.service.labels` | `{}` |
| `distributor.serviceType` | string | Service type for distributor service @deprecated -- This option is deprecated in favor of `ingester.service.type` | `"ClusterIP"` |
| `distributor.shutdownDelay` | string | On SIGTERM, report 503 on /ready and wait this long before shutdown so the LB drains the pod before connections are cut. "0s" (default) disables. When enabling, keep terminationGracePeriodSeconds above shutdownDelay + server.graceful_shutdown_timeout. | `"0s"` |
| `distributor.strategy.rollingUpdate.maxUnavailable` | int | Pod Disruption Budget maxUnavailable | `1` |
| `distributor.terminationGracePeriodSeconds` | int | Grace period to allow the distributor to shutdown before it is killed | `30` |
| `distributor.topologySpreadConstraints` | list | Topology Spread Constraints for distributor pods The value will be passed through tpl. | `[]` |
| `distributor.trafficDistribution` | string | trafficDistribution for distributor service | `""` |

## gateway

Configuration for the gateway, an NGINX reverse proxy that routes incoming read and write requests to the appropriate Loki components.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `gateway.affinity` | object | Affinity for gateway pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `gateway.annotations` | object | Annotations for gateway deployment | `{}` |
| `gateway.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `false` |
| `gateway.autoscaling.behavior` | object | Behavior policies while scaling. | `{}` |
| `gateway.autoscaling.enabled` | bool | Enable autoscaling for the gateway | `false` |
| `gateway.autoscaling.maxReplicas` | int | Maximum autoscaling replicas for the gateway | `3` |
| `gateway.autoscaling.minReplicas` | int | Minimum autoscaling replicas for the gateway | `1` |
| `gateway.autoscaling.targetCPUUtilizationPercentage` | int | Target CPU utilisation percentage for the gateway | `60` |
| `gateway.autoscaling.targetMemoryUtilizationPercentage` | string | Target memory utilisation percentage for the gateway | `nil` |
| `gateway.basicAuth.enabled` | bool | Enables basic authentication for the gateway | `false` |
| `gateway.basicAuth.existingSecret` | string | Existing basic auth secret to use. Must contain '.htpasswd' | `nil` |
| `gateway.basicAuth.htpasswd` | string | Uses the specified users from the `loki.tenants` list to create the htpasswd file. if `loki.tenants` is not set, the `gateway.basicAuth.username` and `gateway.basicAuth.password` are used. The value is templated using `tpl`. Override this to use a custom htpasswd, e.g. in case the default causes high CPU load. | `` Either `loki.tenants` or `gateway.basicAuth.username` and `gateway.basicAuth.password`. `` |
| `gateway.basicAuth.password` | string | The basic auth password for the gateway | `nil` |
| `gateway.basicAuth.username` | string | The basic auth username for the gateway | `nil` |
| `gateway.containerPort` | int | Default container port | `8080` |
| `gateway.containerSecurityContext` | object | The SecurityContext for gateway containers | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `gateway.deploymentStrategy.type` | string |  | `"RollingUpdate"` |
| `gateway.dnsConfig` | object | DNS config for gateway pods | `{}` |
| `gateway.enabled` | bool | Specifies whether the gateway should be enabled | `true` |
| `gateway.extraArgs` | list | Additional CLI args for the gateway | `[]` |
| `gateway.extraContainers` | list | Containers to add to the gateway pods | `[]` |
| `gateway.extraEnv` | list | Environment variables to add to the gateway pods | `[]` |
| `gateway.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the gateway pods | `[]` |
| `gateway.extraVolumeMounts` | list | Volume mounts to add to the gateway pods | `[]` |
| `gateway.extraVolumes` | list | Volumes to add to the gateway pods | `[]` |
| `gateway.hostUsers` | string | Use the host's user namespace in the gateway | `nil` |
| `gateway.image.digest` | string | Overrides the gateway image tag with an image digest | `nil` |
| `gateway.image.pullPolicy` | string | The gateway image pull policy | `"IfNotPresent"` |
| `gateway.image.registry` | string | The Docker registry for the gateway image | `"docker.io"` |
| `gateway.image.repository` | string | The gateway image repository | `"nginxinc/nginx-unprivileged"` |
| `gateway.image.tag` | string | The gateway image tag | `"1.31-alpine"` |
| `gateway.ingress.annotations` | object | Annotations for the gateway ingress | `{}` |
| `gateway.ingress.enabled` | bool | Specifies whether an ingress for the gateway should be created | `false` |
| `gateway.ingress.hosts` | list | Hosts configuration for the gateway ingress, passed through the `tpl` function to allow templating | `[{"host":"gateway.loki.example.com","paths":[{"path":"/"}]}]` |
| `gateway.ingress.ingressClassName` | string | Ingress Class Name. MAY be required for Kubernetes versions &gt;= 1.18 | `""` |
| `gateway.ingress.labels` | object | Labels for the gateway ingress | `{}` |
| `gateway.ingress.tls` | list | TLS configuration for the gateway ingress. Hosts passed through the `tpl` function to allow templating | `[{"hosts":["gateway.loki.example.com"],"secretName":"loki-gateway-tls"}]` |
| `gateway.kedaAutoscaling.behavior` | object | Behavior configuration for KEDA ScaledObject (horizontalPodAutoscalerConfig.behavior). Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#advanced | `{}` |
| `gateway.kedaAutoscaling.cooldownPeriod` | string | Period to wait after the last trigger reported active before scaling the resource back to 0 (seconds). Defaults to defaults.kedaAutoscaling.cooldownPeriod if not set. | `nil` |
| `gateway.kedaAutoscaling.enabled` | bool | Enable KEDA autoscaling for the gateway. Mutually exclusive with autoscaling.enabled | `false` |
| `gateway.kedaAutoscaling.fallback` | object | Fallback configuration for KEDA ScaledObject. Applied when the trigger metric cannot be retrieved. Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#fallback | `{}` |
| `gateway.kedaAutoscaling.maxReplicas` | int | Maximum replicas for KEDA autoscaling | `3` |
| `gateway.kedaAutoscaling.minReplicas` | int | Minimum replicas for KEDA autoscaling | `1` |
| `gateway.kedaAutoscaling.pollingInterval` | string | Interval (seconds) to poll each trigger. Defaults to defaults.kedaAutoscaling.pollingInterval if not set. | `nil` |
| `gateway.kedaAutoscaling.triggers` | list | KEDA triggers for the gateway. Ref: https://keda.sh/docs/latest/scalers/ | `[]` |
| `gateway.lifecycle` | object | Lifecycle for the gateway container | `{}` |
| `gateway.livenessProbe` | object | liveness probe for the nginx container in the gateway pods. | `{}` |
| `gateway.metrics.containerSecurityContext` | object | The SecurityContext for memcached exporter containers | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"privileged":false,"readOnlyRootFilesystem":true,"runAsGroup":65532,"runAsNonRoot":true,"runAsUser":65532,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `gateway.metrics.enabled` | bool | Whether gateway metrics should be exported | `true` |
| `gateway.metrics.extraArgs` | list | Extra args to add to the exporter container. | `[]` |
| `gateway.metrics.image.pullPolicy` | string |  | `"IfNotPresent"` |
| `gateway.metrics.image.registry` | string |  | `"ghcr.io"` |
| `gateway.metrics.image.repository` | string |  | `"jkroepke/access-log-exporter"` |
| `gateway.metrics.image.tag` | string |  | `"0.4.21"` |
| `gateway.metrics.livenessProbe` | object | Liveness probe for memcached exporter | `{"failureThreshold":3,"httpGet":{"path":"/health","port":"http-metrics"},"initialDelaySeconds":30,"periodSeconds":10,"timeoutSeconds":5}` |
| `gateway.metrics.readinessProbe` | object | Readiness probe for memcached exporter | `{"failureThreshold":3,"httpGet":{"path":"/health","port":"http-metrics"},"initialDelaySeconds":5,"periodSeconds":5,"timeoutSeconds":3}` |
| `gateway.metrics.resizePolicy` | list | Container resize policy for the gateway metrics exporter Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `gateway.metrics.resources.limits` | object |  | `{}` |
| `gateway.metrics.resources.requests` | object |  | `{}` |
| `gateway.metrics.service.annotations` | object | Annotations for the gateway metrics exporter service. Use this instead of gateway.service.annotations to avoid propagating annotations (e.g. external-dns) to the exporter ClusterIP service. | `{}` |
| `gateway.metrics.service.labels` | object | Labels for the gateway metrics exporter service. | `{}` |
| `gateway.metrics.startupProbe` | object | Startup probe for memcached exporter | `{}` |
| `gateway.nginxConfig.clientMaxBodySize` | string | Allows customizing the `client_max_body_size` directive | `"4M"` |
| `gateway.nginxConfig.customBackendUrl` | string | Override Backend URL | `nil` |
| `gateway.nginxConfig.customReadUrl` | string | Override Read URL | `nil` |
| `gateway.nginxConfig.customWriteUrl` | string | Override Write URL | `nil` |
| `gateway.nginxConfig.enableIPv6` | bool | Enable listener for IPv6, disable on IPv4-only systems | `true` |
| `gateway.nginxConfig.file` | string | Config file contents for Nginx. Passed through the `tpl` function to allow templating | `See values.yaml` |
| `gateway.nginxConfig.httpSnippet` | string | Allows appending custom configuration to the http block, passed through the `tpl` function to allow templating | `""` |
| `gateway.nginxConfig.locationSnippet` | string | Allows appending custom configuration inside every location block, useful for authentication or setting headers that are not inherited from the server block, passed through the `tpl` function to allow templating. | `"{{ if .Values.loki.tenants }}proxy_set_header X-Scope-OrgID $remote_user;{{ end }}"` |
| `gateway.nginxConfig.logFormat` | string | NGINX log format | `"main '$remote_addr - $remote_user [$time_local]  $status '\n        '\"$request\" $body_bytes_sent \"$http_referer\" '\n        '\"$http_user_agent\" \"$http_x_forwarded_for\"';"` |
| `gateway.nginxConfig.resolver` | string | Allows overriding the DNS resolver address nginx will use. | `""` |
| `gateway.nginxConfig.schema` | string | Which schema to be used when building URLs. Can be 'http' or 'https'. | `"http"` |
| `gateway.nginxConfig.serverSnippet` | string | Allows appending custom configuration to the server block | `""` |
| `gateway.nginxConfig.ssl` | bool | Whether ssl should be appended to the listen directive of the server block or not. | `false` |
| `gateway.nodeSelector` | object | Node selector for gateway pods | `{}` |
| `gateway.podAnnotations` | object | Annotations for gateway pods | `{}` |
| `gateway.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `gateway.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `gateway.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `gateway.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `gateway.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `gateway.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `gateway.podLabels` | object | Additional labels for gateway pods | `{}` |
| `gateway.podSecurityContext` | object | The SecurityContext for gateway containers | `{"fsGroup":101,"runAsGroup":101,"runAsNonRoot":true,"runAsUser":101,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `gateway.priorityClassName` | string | The name of the PriorityClass for gateway pods | `nil` |
| `gateway.readinessProbe.httpGet.path` | string |  | `"/"` |
| `gateway.readinessProbe.httpGet.port` | string |  | `"http"` |
| `gateway.readinessProbe.initialDelaySeconds` | int |  | `15` |
| `gateway.readinessProbe.timeoutSeconds` | int |  | `1` |
| `gateway.replicas` | int | Number of replicas for the gateway | `1` |
| `gateway.resizePolicy` | list | Container resize policy for the gateway Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `gateway.resources` | object | Resource requests and limits for the gateway | `{}` |
| `gateway.route.main.additionalRules` | list | Additional rules to prepend before the default catch-all rule. Each entry is rendered into spec.rules as written (templating is supported), so any HTTPRoute/GRPCRoute rule shape is accepted — including filter-only rules with no backendRefs (e.g. RequestRedirect, URLRewrite). Useful for advanced routing logic (e.g. header-based routing, auth policies, redirects). Note: the chart-generated rule that points at the gateway service is always rendered for this route — additionalRules are prepended, not a replacement. Example HTTP-to-HTTPS redirect: additionalRules: - filters: - type: RequestRedirect requestRedirect: scheme: https statusCode: 301 matches: - path: { type: PathPrefix, value: / } | `[]` |
| `gateway.route.main.annotations` | object | Additional annotations for the route | `{}` |
| `gateway.route.main.apiVersion` | string | Set the route apiVersion, e.g. gateway.networking.k8s.io/v1 or gateway.networking.k8s.io/v1beta1. If not set, the latest available version will be auto-detected. | `""` |
| `gateway.route.main.enabled` | bool | Specifies whether this Gateway API route should be created | `false` |
| `gateway.route.main.filters` | list | Filters for the default rule | `[]` |
| `gateway.route.main.hostnames` | list | Hostnames for the route | `[]` |
| `gateway.route.main.kind` | string | Set the route kind. Valid options are GRPCRoute, HTTPRoute, TCPRoute, TLSRoute, UDPRoute | `"HTTPRoute"` |
| `gateway.route.main.labels` | object | Additional labels for the route | `{}` |
| `gateway.route.main.matches` | list | Matches for the default rule. Defaults to match all paths. | `[{"path":{"type":"PathPrefix","value":"/"}}]` |
| `gateway.route.main.parentRefs` | list | Parent gateway references to attach the route to | `[]` |
| `gateway.route.main.timeouts` | object | Timeouts for the default rule | `{}` |
| `gateway.service.annotations` | object | Annotations for the gateway service | `{}` |
| `gateway.service.clusterIP` | string | ClusterIP of the gateway service | `nil` |
| `gateway.service.ipFamilies` | list | ipFamilies for gateway Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `gateway.service.ipFamilyPolicy` | string | ipFamilyPolicy for gateway Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `gateway.service.labels` | object | Labels for gateway service | `{}` |
| `gateway.service.loadBalancerIP` | string | Load balancer IPO address if service type is LoadBalancer | `nil` |
| `gateway.service.nodePort` | int | Node port if service type is NodePort | `nil` |
| `gateway.service.port` | int | Port of the gateway service | `80` |
| `gateway.service.sessionAffinity` | string | Session affinity for gateway Service | `""` |
| `gateway.service.sessionAffinityConfig` | object | Session affinity config for gateway Service | `{}` |
| `gateway.service.trafficDistribution` | string | trafficDistribution for gateway Service | `""` |
| `gateway.service.type` | string | Type of the gateway service | `"ClusterIP"` |
| `gateway.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `gateway.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `false` |
| `gateway.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `true` |
| `gateway.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `gateway.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `gateway.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `gateway.startupProbe` | object | startup probe for the nginx container in the gateway pods. | `{}` |
| `gateway.terminationGracePeriodSeconds` | int | Grace period to allow the gateway to shutdown before it is killed | `30` |
| `gateway.tolerations` | list |  | `[]` |
| `gateway.topologySpreadConstraints` | list | Topology Spread Constraints for gateway pods The value will be passed through tpl. | `[]` |
| `gateway.verboseLogging` | bool | Enable logging of 2xx and 3xx HTTP requests | `true` |

## global

Global values that apply to all components and subcharts, such as image registry, DNS configuration, and common environment variables.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `global.clusterDomain` | string | configures cluster domain ("cluster.local" by default) | `"cluster.local"` |
| `global.dnsNamespace` | string | configures DNS service namespace | `"kube-system"` |
| `global.dnsService` | string | configures DNS service name | `"kube-dns"` |
| `global.extraArgs` | list | Common additional CLI arguments for all jobs (that is, -log.level debug, -config.expand-env=true or -log-config-reverse-order) (deprecated, use defaults.extraArgs) scope: backend, bloom-builder, bloom-gateway, bloom-planner, compactor, distributor, index-gateway, ingester, overrides-exporter, pattern-ingester, querier, query-frontend, query-scheduler, read, ruler, write. | `[]` |
| `global.extraEnv` | list | Common environment variables to add to all pods directly managed by this chart. (deprecated, use defaults.extraEnv) scope: backend, bloom-builder, bloom-gateway, bloom-planner, compactor, distributor, index-gateway, ingester, overrides-exporter, pattern-ingester, querier, query-frontend, query-scheduler, read, ruler, write. | `[]` |
| `global.extraEnvFrom` | list | Common source of environment injections to add to all pods directly managed by this chart. (deprecated, use defaults.extraEnvFrom) scope: backend, bloom-builder, bloom-gateway, bloom-planner, compactor, distributor, index-gateway, ingester, overrides-exporter, pattern-ingester, querier, query-frontend, query-scheduler, read, ruler, write. For example to inject values from a Secret, use: extraEnvFrom: - secretRef: name: mysecret | `[]` |
| `global.extraVolumeMounts` | list | Common mount points to add to all pods directly managed by this chart. (deprecated, use defaults.extraVolumeMounts) scope: backend, bloom-builder, bloom-gateway, bloom-planner, compactor, distributor, index-gateway, ingester, overrides-exporter, pattern-ingester, querier, query-frontend, query-scheduler, read, ruler, write. | `[]` |
| `global.extraVolumes` | list | Common volumes to add to all pods directly managed by this chart. (deprecated, use defaults.extraVolumes) scope: backend, bloom-builder, bloom-gateway, bloom-planner, compactor, distributor, index-gateway, ingester, overrides-exporter, pattern-ingester, querier, query-frontend, query-scheduler, read, ruler, write. | `[]` |
| `global.image.registry` | string | Overrides the Docker registry globally for all images (deprecated, use global.imageRegistry). This has lower precedence than global.imageRegistry, but higher precedence than component-level image.registry values. | `nil` |
| `global.imageRegistry` | string | Overrides the Docker registry globally for all images (standard format). This has the highest precedence and overrides every component-level image.registry value. | `nil` |
| `global.priorityClassName` | string | Overrides the priorityClassName for all pods | `nil` |

## indexGateway

Configuration for the index-gateway

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `indexGateway.affinity` | object | Affinity for index-gateway pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `indexGateway.annotations` | object | Annotations for index-gateway | `{}` |
| `indexGateway.appProtocol` | object | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" @deprecated -- This option is deprecated in favor of `indexGateway.service.appProtocol` | `{"grpc":""}` |
| `indexGateway.dnsConfig` | object | DNSConfig for index-gateway pods | `{}` |
| `indexGateway.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `Distributed`. | `true` |
| `indexGateway.extraArgs` | list | Additional CLI args for the index-gateway | `[]` |
| `indexGateway.extraContainers` | list | Containers to add to the index-gateway pods | `[]` |
| `indexGateway.extraEnv` | list | Environment variables to add to the index-gateway pods | `[]` |
| `indexGateway.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the index-gateway pods | `[]` |
| `indexGateway.extraVolumeMounts` | list | Volume mounts to add to the index-gateway pods | `[]` |
| `indexGateway.extraVolumes` | list | Volumes to add to the index-gateway pods | `[]` |
| `indexGateway.hostAliases` | list | hostAliases to add | `[]` |
| `indexGateway.hostUsers` | string | Use the host's user namespace in the index-gateway | `nil` |
| `indexGateway.image.registry` | string | The Docker registry for the index-gateway image. Overrides `loki.image.registry` | `nil` |
| `indexGateway.image.repository` | string | Docker image repository for the index-gateway image. Overrides `loki.image.repository` | `nil` |
| `indexGateway.image.tag` | string | Docker image tag for the index-gateway image. Overrides `loki.image.tag` | `nil` |
| `indexGateway.initContainers` | list | Init containers to add to the index-gateway pods | `[]` |
| `indexGateway.joinMemberlist` | bool | Whether the index gateway should join the memberlist hashring | `true` |
| `indexGateway.kind` | string | Kind of index-gateway deployment. StatefulSet and Deployment are supported. | `"StatefulSet"` |
| `indexGateway.labels` | object | Labels for index-gateway | `{}` |
| `indexGateway.lifecycle` | object | Lifecycle for the index-gateway container | `{}` |
| `indexGateway.maxUnavailable` | string | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `nil` |
| `indexGateway.nodeSelector` | object | Node selector for index-gateway pods | `{}` |
| `indexGateway.persistence.accessModes` | list | Set access modes on the PersistentVolumeClaim | `["ReadWriteOnce"]` |
| `indexGateway.persistence.annotations` | object | Annotations for index gateway PVCs | `{}` |
| `indexGateway.persistence.dataVolumeParameters` | object | Parameters used for the `data` volume when volumeClaimEnabled if false | `{"emptyDir":{}}` |
| `indexGateway.persistence.enableStatefulSetAutoDeletePVC` | bool | Enable StatefulSetAutoDeletePVC feature | `false` |
| `indexGateway.persistence.enabled` | bool | Enable creating PVCs which is required when using boltdb-shipper | `false` |
| `indexGateway.persistence.labels` | object | Labels for index gateway PVCs | `{}` |
| `indexGateway.persistence.size` | string | Size of persistent disk | `"10Gi"` |
| `indexGateway.persistence.storageClass` | string | Storage class to be used. If defined, storageClassName: &lt;storageClass&gt;. If set to "-", storageClassName: "", which disables dynamic provisioning. If empty or set to null, no storageClassName spec is set, choosing the default provisioner (gp2 on AWS, standard on GKE, AWS, and OpenStack). | `nil` |
| `indexGateway.persistence.volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `indexGateway.podAnnotations` | object | Annotations for index-gateway pods | `{}` |
| `indexGateway.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `indexGateway.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `indexGateway.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `indexGateway.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `indexGateway.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `indexGateway.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `indexGateway.podLabels` | object | Labels for index-gateway pods | `{}` |
| `indexGateway.priorityClassName` | string | The name of the PriorityClass for index-gateway pods | `nil` |
| `indexGateway.replicas` | int | Number of replicas for the index-gateway. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `0` |
| `indexGateway.resizePolicy` | list | Container resize policy for the index-gateway Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `indexGateway.resources` | object | Resource requests and limits for the index-gateway | `{}` |
| `indexGateway.service.annotations` | object | Annotations for index-gateway Service | `{}` |
| `indexGateway.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `indexGateway.service.ipFamilies` | list | ipFamilies for index-gateway Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `indexGateway.service.ipFamilyPolicy` | string | ipFamilyPolicy for index-gateway Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `indexGateway.service.labels` | object | Additional labels for index-gateway Service | `{}` |
| `indexGateway.service.sessionAffinity` | string | Session affinity for index-gateway Service | `""` |
| `indexGateway.service.sessionAffinityConfig` | object | Session affinity config for index-gateway Service | `{}` |
| `indexGateway.service.trafficDistribution` | string | trafficDistribution for index-gateway Service | `""` |
| `indexGateway.service.type` | string | Service Type for index-gateway Service | `"ClusterIP"` |
| `indexGateway.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `indexGateway.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `indexGateway.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `indexGateway.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `indexGateway.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `indexGateway.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `indexGateway.serviceAnnotations` | object | Annotations for index-gateway service @deprecated -- This option is deprecated in favor of `indexGateway.service.annotations` | `{}` |
| `indexGateway.serviceLabels` | object | Labels for index-gateway service @deprecated -- This option is deprecated in favor of `indexGateway.service.labels` | `{}` |
| `indexGateway.serviceType` | string | Service type for index-gateway service @deprecated -- This option is deprecated in favor of `indexGateway.service.type` | `"ClusterIP"` |
| `indexGateway.statefulSetRecreateJob` | object | EXPERIMENTAL: Enable creating a Job to recreate the compactor StatefulSet when the compactor configuration is changed. # Only applicable if `compactor.kind` is StatefulSet. Useful for PVC size changes, which require recreating the StatefulSet. | `{"enabled":false}` |
| `indexGateway.strategy.type` | string | One of 'OnDelete' or 'RollingUpdate' | `"RollingUpdate"` |
| `indexGateway.terminationGracePeriodSeconds` | int | Grace period to allow the index-gateway to shutdown before it is killed. | `300` |
| `indexGateway.topologySpreadConstraints` | list | Topology Spread Constraints for index-gateway pods The value will be passed through tpl. | `[]` |
| `indexGateway.trafficDistribution` | string | trafficDistribution for index-gateway service | `""` |

## ingester

Configuration for the ingester

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `ingester.affinity` | object | Affinity for ingester pods. Ignored if zoneAwareReplication is enabled. The value will be passed through tpl. | `Hard node anti-affinity` |
| `ingester.annotations` | object | Annotations for ingester | `{}` |
| `ingester.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" @deprecated -- This option is deprecated in favor of `ingester.service.appProtocol.grpc` | `""` |
| `ingester.autoscaling.behavior.enabled` | bool | Enable autoscaling behaviours | `false` |
| `ingester.autoscaling.behavior.scaleDown` | object | define scale down policies, must conform to HPAScalingRules | `{}` |
| `ingester.autoscaling.behavior.scaleUp` | object | define scale up policies, must conform to HPAScalingRules | `{}` |
| `ingester.autoscaling.customMetrics` | list | Allows one to define custom metrics using the HPA/v2 schema (for example, Pods, Object or External metrics) | `[]` |
| `ingester.autoscaling.enabled` | bool | Enable autoscaling for the ingester | `false` |
| `ingester.autoscaling.maxReplicas` | int | Maximum autoscaling replicas for the ingester | `3` |
| `ingester.autoscaling.minReplicas` | int | Minimum autoscaling replicas for the ingester | `1` |
| `ingester.autoscaling.targetCPUUtilizationPercentage` | int | Target CPU utilisation percentage for the ingester | `60` |
| `ingester.autoscaling.targetMemoryUtilizationPercentage` | string | Target memory utilisation percentage for the ingester | `nil` |
| `ingester.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `ingester.dnsConfig` | object | DNSConfig for ingester pods | `{}` |
| `ingester.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `Distributed`. | `true` |
| `ingester.extraArgs` | list | Additional CLI args for the ingester | `[]` |
| `ingester.extraContainers` | list | Containers to add to the ingester pods | `[]` |
| `ingester.extraEnv` | list | Environment variables to add to the ingester pods | `[]` |
| `ingester.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the ingester pods | `[]` |
| `ingester.extraVolumeMounts` | list | Volume mounts to add to the ingester pods | `[]` |
| `ingester.extraVolumes` | list | Volumes to add to the ingester pods | `[]` |
| `ingester.hostAliases` | list | hostAliases to add | `[]` |
| `ingester.hostUsers` | string | Use the host's user namespace in the ingester | `nil` |
| `ingester.image.registry` | string | The Docker registry for the ingester image. Overrides `loki.image.registry` | `nil` |
| `ingester.image.repository` | string | Docker image repository for the ingester image. Overrides `loki.image.repository` | `nil` |
| `ingester.image.tag` | string | Docker image tag for the ingester image. Overrides `loki.image.tag` | `nil` |
| `ingester.initContainers` | list | Init containers to add to the ingester pods | `[]` |
| `ingester.kedaAutoscaling.behavior` | object | Behavior configuration for KEDA ScaledObject (horizontalPodAutoscalerConfig.behavior). Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#advanced | `{}` |
| `ingester.kedaAutoscaling.cooldownPeriod` | string | Period to wait after the last trigger reported active before scaling the resource back to 0 (seconds). Defaults to defaults.kedaAutoscaling.cooldownPeriod if not set. | `nil` |
| `ingester.kedaAutoscaling.enabled` | bool | Enable KEDA autoscaling for the ingester. Mutually exclusive with autoscaling.enabled | `false` |
| `ingester.kedaAutoscaling.fallback` | object | Fallback configuration for KEDA ScaledObject. Applied when the trigger metric cannot be retrieved. Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#fallback | `{}` |
| `ingester.kedaAutoscaling.maxReplicas` | int | Maximum replicas for KEDA autoscaling | `3` |
| `ingester.kedaAutoscaling.minReplicas` | int | Minimum replicas for KEDA autoscaling | `1` |
| `ingester.kedaAutoscaling.pollingInterval` | string | Interval (seconds) to poll each trigger. Defaults to defaults.kedaAutoscaling.pollingInterval if not set. | `nil` |
| `ingester.kedaAutoscaling.triggers` | list | KEDA triggers for the ingester. Ref: https://keda.sh/docs/latest/scalers/ | `[]` |
| `ingester.kind` | string | Kind of compactor deployment. StatefulSet and Deployment are supported. | `"StatefulSet"` |
| `ingester.labels` | object | Labels for ingester | `{}` |
| `ingester.lifecycle` | object | Lifecycle for the ingester container | `{}` |
| `ingester.livenessProbe` | object | liveness probe settings for ingester pods. If empty use `loki.livenessProbe` | `{}` |
| `ingester.maxUnavailable` | int | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `1` |
| `ingester.nodeSelector` | object | Node selector for ingester pods | `{}` |
| `ingester.persistence.claims` | list | List of the ingester PVCs |  |
| `ingester.persistence.claims[0].accessModes` | list | Set access modes on the PersistentVolumeClaim | `["ReadWriteOnce"]` |
| `ingester.persistence.claims[0].storageClass` | string | Storage class to be used. If defined, storageClassName: &lt;storageClass&gt;. If set to "-", storageClassName: "", which disables dynamic provisioning. If empty or set to null, no storageClassName spec is set, choosing the default provisioner (gp2 on AWS, standard on GKE, AWS, and OpenStack). | `nil` |
| `ingester.persistence.claims[0].volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `ingester.persistence.enableStatefulSetAutoDeletePVC` | bool | Enable StatefulSetAutoDeletePVC feature | `false` |
| `ingester.persistence.enabled` | bool | Enable creating PVCs which is required when using boltdb-shipper | `false` |
| `ingester.persistence.inMemory` | bool | Use emptyDir with ramdisk for storage. **Please note that all data in ingester will be lost on pod restart** | `false` |
| `ingester.podAnnotations` | object | Annotations for ingester pods | `{}` |
| `ingester.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `ingester.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `ingester.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `ingester.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `ingester.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `ingester.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `ingester.podLabels` | object | Labels for ingester pods. Keys used by the workload selectors (`app.kubernetes.io/name`, `app.kubernetes.io/instance`, `app.kubernetes.io/component` and, with zoneAwareReplication, `name` and `rollout-group`) are reserved and ignored here: the latter are also used by the per-zone headless Services and grafana/rollout-operator. | `{}` |
| `ingester.podManagementPolicy` | string | PodManagementPolicy for the ingester StatefulSet. Only applicable if `compactor.kind` is StatefulSet. # OrderedReady and Parallel are supported. | `"Parallel"` |
| `ingester.priorityClassName` | string | The name of the PriorityClass for ingester pods | `nil` |
| `ingester.readinessProbe` | object | readiness probe settings for ingester pods. If empty, use `loki.readinessProbe` | `{}` |
| `ingester.replicas` | int | Number of replicas for the ingester, when zoneAwareReplication.enabled is true, the total number of replicas will match this value with each zone having 1/3rd of the total replicas. Set to null to omit spec.replicas from the StatefulSet, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `0` |
| `ingester.resizePolicy` | list | Container resize policy for the ingester Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `ingester.resources` | object | Resource requests and limits for the ingester | `{}` |
| `ingester.service.annotations` | object | Annotations for ingester Service | `{}` |
| `ingester.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `ingester.service.ipFamilies` | list | ipFamilies for ingester Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `ingester.service.ipFamilyPolicy` | string | ipFamilyPolicy for ingester Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `ingester.service.labels` | object | Additional labels for ingester Service | `{}` |
| `ingester.service.sessionAffinity` | string | Session affinity for ingester Service | `""` |
| `ingester.service.sessionAffinityConfig` | object | Session affinity config for ingester Service | `{}` |
| `ingester.service.trafficDistribution` | string | trafficDistribution for ingester Service | `""` |
| `ingester.service.type` | string | Service Type for ingester Service | `"ClusterIP"` |
| `ingester.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `ingester.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `ingester.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `ingester.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `ingester.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `ingester.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `ingester.serviceAnnotations` | object | Annotations for ingester service @deprecated -- This option is deprecated in favor of `ingester.service.annotations` | `{}` |
| `ingester.serviceLabels` | object | Labels for ingester service @deprecated -- This option is deprecated in favor of `ingester.service.labels` | `{}` |
| `ingester.serviceType` | string | Service type for ingester service @deprecated -- This option is deprecated in favor of `ingester.service.type` | `"ClusterIP"` |
| `ingester.startupProbe` | object | startup probe settings for ingester pods. If empty use `loki.startupProbe` | `{}` |
| `ingester.statefulSetRecreateJob` | object | EXPERIMENTAL: Enable creating a Job to recreate the ingester StatefulSet # Only applicable if `ingester.kind` is StatefulSet. Useful for PVC size changes, which require recreating the StatefulSet. | `{"enabled":false}` |
| `ingester.strategy` | object | UpdateStrategy or Strategy for the ingester. | `{"type":"RollingUpdate"}` |
| `ingester.terminationGracePeriodSeconds` | int | Grace period to allow the ingester to shutdown before it is killed. Especially for the ingestor, this must be increased. It must be long enough so ingesters can be gracefully shutdown flushing/transferring all data and to successfully leave the member ring on shutdown. | `300` |
| `ingester.tolerations` | list | Tolerations for ingester pods | `[]` |
| `ingester.topologySpreadConstraints` | list | topologySpread for ingester pods. The value will be passed through tpl. | `Defaults to allow skew no more than 1 node` |
| `ingester.trafficDistribution` | string | trafficDistribution for ingester service | `""` |
| `ingester.updateStrategy.type` | string | One of 'OnDelete' or 'RollingUpdate' | `"RollingUpdate"` |
| `ingester.zoneAwareReplication.enabled` | bool | Enable zone awareness. | `true` |
| `ingester.zoneAwareReplication.maxUnavailablePct` | int | The percent of replicas in each zone that will be restarted at once. In a value of 0-100 | `33` |
| `ingester.zoneAwareReplication.migration` | object | The migration block allows migrating non zone aware ingesters to zone aware ingesters. | `{"enabled":false,"excludeDefaultZone":false,"readPath":false,"writePath":false}` |
| `ingester.zoneAwareReplication.zoneA.annotations` | object | Specific annotations to add to zone A statefulset | `{}` |
| `ingester.zoneAwareReplication.zoneA.extraAffinity` | object | optionally define extra affinity rules, by default different zones are not allowed to schedule on the same host The value will be passed through tpl. | `{}` |
| `ingester.zoneAwareReplication.zoneA.nodeSelector` | string | optionally define a node selector for this zone | `nil` |
| `ingester.zoneAwareReplication.zoneA.persistence.storageClass` | string | Storage class to use for every PVC in zone A. Overrides the `storageClass` of each entry in `ingester.persistence.claims` for this zone only. Use this when you have one StorageClass per availability zone (for example a topology-pinned EBS/PD StorageClass). If null, the claim-level `storageClass` is used as before, preserving existing behavior. If set to "-", `storageClassName: ""` is rendered, which disables dynamic provisioning. | `nil` |
| `ingester.zoneAwareReplication.zoneA.podAnnotations` | object | Specific annotations to add to zone A pods | `{}` |
| `ingester.zoneAwareReplication.zoneB.annotations` | object | Specific annotations to add to zone B statefulset | `{}` |
| `ingester.zoneAwareReplication.zoneB.extraAffinity` | object | optionally define extra affinity rules, by default different zones are not allowed to schedule on the same host The value will be passed through tpl. | `{}` |
| `ingester.zoneAwareReplication.zoneB.nodeSelector` | string | optionally define a node selector for this zone | `nil` |
| `ingester.zoneAwareReplication.zoneB.persistence.storageClass` | string | Storage class to use for every PVC in zone B. See `ingester.zoneAwareReplication.zoneA.persistence.storageClass` for details. If null, the claim-level `storageClass` is used as before. | `nil` |
| `ingester.zoneAwareReplication.zoneB.podAnnotations` | object | Specific annotations to add to zone B pods | `{}` |
| `ingester.zoneAwareReplication.zoneC.annotations` | object | Specific annotations to add to zone C statefulset | `{}` |
| `ingester.zoneAwareReplication.zoneC.extraAffinity` | object | optionally define extra affinity rules, by default different zones are not allowed to schedule on the same host The value will be passed through tpl. | `{}` |
| `ingester.zoneAwareReplication.zoneC.nodeSelector` | string | optionally define a node selector for this zone | `nil` |
| `ingester.zoneAwareReplication.zoneC.persistence.storageClass` | string | Storage class to use for every PVC in zone C. See `ingester.zoneAwareReplication.zoneA.persistence.storageClass` for details. If null, the claim-level `storageClass` is used as before. | `nil` |
| `ingester.zoneAwareReplication.zoneC.podAnnotations` | object | Specific annotations to add to zone C pods | `{}` |

## ingress

Ingress configuration Use either this ingress or the gateway, but not both at once. If you enable this, make sure to disable the gateway. You'll need to supply authn configuration for your ingress controller.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `ingress.hosts` | list | Hosts configuration for the ingress, passed through the `tpl` function to allow templating | `["loki.example.com"]` |
| `ingress.paths.compactor` | list | Paths that are exposed by Loki Compactor. If deployment mode is Distributed, the requests are forwarded to the service. If deployment mode is SimpleScalable, the requests are forwarded to k8s service. If deployment mode is SingleBinary, the requests are forwarded to the central/single k8s service` | `["/loki/api/v1/delete"]` |
| `ingress.paths.distributor` | list | Paths that are exposed by Loki Distributor. If deployment mode is Distributed, the requests are forwarded to the service:. If deployment mode is SimpleScalable, the requests are forwarded to write k8s service:. If deployment mode is SingleBinary, the requests are forwarded to the central/single k8s service: | `["/api/prom/push","/loki/api/v1/push","/otlp/v1/logs"]` |
| `ingress.paths.queryFrontend` | list | Paths that are exposed by Loki Query Frontend. If deployment mode is Distributed, the requests are forwarded to the service. If deployment mode is SimpleScalable, the requests are forwarded to write k8s service`. If deployment mode is SingleBinary, the requests are forwarded to the central/single k8s service` | `["/api/prom/query","/api/prom/label","/api/prom/series","/api/prom/tail","/loki/api/v1/query","/loki/api/v1/query_range","/loki/api/v1/tail","/loki/api/v1/label","/loki/api/v1/labels","/loki/api/v1/series","/loki/api/v1/index/stats","/loki/api/v1/index/volume","/loki/api/v1/index/volume_range","/loki/api/v1/format_query","/loki/api/v1/detected_field","/loki/api/v1/detected_fields","/loki/api/v1/detected_labels","/loki/api/v1/patterns","/ui"]` |
| `ingress.paths.ruler` | list | Paths that are exposed by Loki Ruler. If deployment mode is Distributed, the requests are forwarded to the service. If deployment mode is SimpleScalable, the requests are forwarded to k8s service. If deployment mode is SingleBinary, the requests are forwarded to the central/single k8s service | `["/api/prom/rules","/api/prom/api/v1/rules","/api/prom/api/v1/alerts","/loki/api/v1/rules","/prometheus/api/v1/rules","/prometheus/api/v1/alerts"]` |
| `ingress.tls` | list | TLS configuration for the ingress. Hosts passed through the `tpl` function to allow templating | `[]` |

## loki

Configuration for running Loki

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `loki.analytics` | object | Optional analytics configuration | `{}` |
| `loki.annotations` | object | Common annotations for all deployments/StatefulSets | `{}` |
| `loki.block_builder` | object | Optional block builder configuration | `{}` |
| `loki.commonConfig.compactor_grpc_address` | string | The gRPC address of the compactor. The use of compactor_grpc_address is preferred over compactor_address. If a customized compactor_address is set, compactor_grpc_address should be set to an empty string. | `"{{ include \"loki.compactorAddress\" . }}"` |
| `loki.compactor` | object | Optional compactor configuration | `{}` |
| `loki.compactor_grpc_client` | object | Optional compactor grpc client configuration | `{}` |
| `loki.config` | string | Config file contents for Loki | `See values.yaml` |
| `loki.configObjectName` | string | The name of the object which Loki will mount as a volume containing the config. If the configStorageType is Secret, this will be the name of the Secret, if it is ConfigMap, this will be the name of the ConfigMap. The value will be passed through tpl. | `"{{ include \"loki.name\" . }}"` |
| `loki.configStorageType` | string | Defines what kind of object stores the configuration, a ConfigMap or a Secret. In order to move sensitive information (such as credentials) from the ConfigMap/Secret to a more secure location (e.g. vault), it is possible to use [environment variables in the configuration](https://grafana.com/docs/loki/latest/configuration/#use-environment-variables-in-the-configuration). Such environment variables can be then stored in a separate Secret and injected via the global.extraEnvFrom value. For details about environment injection from a Secret please see [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/#use-case-as-container-environment-variables). | `"ConfigMap"` |
| `loki.containerSecurityContext` | object | The SecurityContext for Loki containers | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `loki.distributor` | object | Optional distributor configuration | `{}` |
| `loki.dnsConfig` | object | DNS config for Loki pods | `{}` |
| `loki.enableServiceLinks` | bool | Should enableServiceLinks be enabled. Default to enable | `true` |
| `loki.extraMemberlistConfig` | object | Extra memberlist configuration | `{"abort_if_cluster_join_fails":true,"advertise_port":7946,"bind_port":7946,"max_join_backoff":"1m","max_join_retries":10,"min_join_backoff":"1s","rejoin_interval":"90s"}` |
| `loki.generatedConfigObjectName` | string | The name of the Secret or ConfigMap that will be created by this chart. If empty, no configmap or secret will be created. The value will be passed through tpl. | `"{{ include \"loki.name\" . }}"` |
| `loki.image.digest` | string | Overrides the image tag with an image digest | `nil` |
| `loki.image.pullPolicy` | string | Docker image pull policy | `"IfNotPresent"` |
| `loki.image.registry` | string | The Docker registry | `"docker.io"` |
| `loki.image.repository` | string | Docker image repository | `"grafana/loki"` |
| `loki.image.tag` | string | Overrides the image tag whose default is the chart's appVersion | `nil` |
| `loki.index_gateway` | object | Optional index gateway configuration | `{"mode":"simple"}` |
| `loki.ingester` | object | Optional ingester configuration | `{"wal":{"flush_on_shutdown":true}}` |
| `loki.ingester_client` | object | Optional ingester client configuration | `{}` |
| `loki.limits_config` | object | Limits config | `{"max_cache_freshness_per_query":"10m","query_timeout":"300s","reject_old_samples":true,"reject_old_samples_max_age":"168h","split_queries_by_interval":"15m","volume_enabled":true}` |
| `loki.memberlistConfig` | object | memberlist configuration (overrides embedded default) | `{}` |
| `loki.operational_config` | object | Optional operational configuration | `{}` |
| `loki.pattern_ingester` | object | Optional pattern ingester configuration | `{"enabled":false}` |
| `loki.podAnnotations` | object | Common annotations for all pods | `{}` |
| `loki.podLabels` | object | Common labels for all pods | `{}` |
| `loki.podSecurityContext` | object | The SecurityContext for Loki pods | `{"fsGroup":10001,"fsGroupChangePolicy":"OnRootMismatch","runAsGroup":10001,"runAsNonRoot":true,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `loki.querier` | object | Optional querier configuration | `{}` |
| `loki.query_range` | object | Optional querier configuration | `{}` |
| `loki.query_scheduler` | object | Additional query scheduler config | `{}` |
| `loki.revisionHistoryLimit` | int | The number of old ReplicaSets to retain to allow rollback | `10` |
| `loki.rulerConfig` | object | Check https://grafana.com/docs/loki/latest/configuration/#ruler for more info on configuring ruler | `{"wal":{"dir":"/var/loki/ruler-wal"}}` |
| `loki.runtimeConfig` | object | Provides a reloadable runtime configuration file for some specific configuration | `{}` |
| `loki.schemaConfig` | object | Check https://grafana.com/docs/loki/latest/configuration/#schema_config for more info on how to configure schemas | `{}` |
| `loki.server` | object | Check https://grafana.com/docs/loki/latest/configuration/#server for more info on the server configuration. | `{"graceful_shutdown_timeout":"5s","grpc_listen_port":9095,"grpc_server_max_concurrent_streams":1000,"grpc_server_max_recv_msg_size":104857600,"grpc_server_max_send_msg_size":104857600,"grpc_server_min_time_between_pings":"10s","grpc_server_ping_without_stream_allowed":true,"http_listen_port":3100,"http_server_idle_timeout":"30s","http_server_read_timeout":"10m0s","http_server_write_timeout":"10m0s","log_level":"info"}` |
| `loki.service.ipFamilies` | list | ipFamilies for all services Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `loki.service.ipFamilyPolicy` | string | ipFamilyPolicy for all services Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `loki.service.trafficDistribution` | string | trafficDistribution for services Ref: https://kubernetes.io/docs/concepts/services-networking/service/#traffic-distribution | `""` |
| `loki.serviceAnnotations` | object | Common annotations for all services | `{}` |
| `loki.serviceLabels` | object | Common labels for all services | `{}` |
| `loki.storage.s3.backoff_config` | object | Check https://grafana.com/docs/loki/latest/configure/#s3_storage_config for more info on how to provide a backoff_config | `{}` |
| `loki.storage_config` | object | Additional storage config | `{"bloom_shipper":{"working_directory":"/var/loki/data/bloomshipper"},"boltdb_shipper":{"index_gateway_client":{"server_address":"{{ include \"loki.indexGatewayAddress\" . }}"}},"hedging":{"at":"250ms","max_per_second":20,"up_to":3},"tsdb_shipper":{"index_gateway_client":{"server_address":"{{ include \"loki.indexGatewayAddress\" . }}"}}}` |
| `loki.structuredConfig` | object | Structured loki configuration, takes precedence over `loki.config`, `loki.schemaConfig`, `loki.storageConfig` | `{}` |
| `loki.tenants` | list | Tenants list to be created on nginx htpasswd file, with name and password or passwordHash keys&lt;br&gt;&lt;br&gt; Example: &lt;pre&gt; tenants:&lt;br&gt; - name: "test-user-1"&lt;br&gt; password: "test-password-1"&lt;br&gt; - name: "test-user-2"&lt;br&gt; passwordHash: "$2y$10$7O40CaY1yz7fu9O24k2/u.ct/wELYHRBsn25v/7AyuQ8E8hrLqpva" # generated using `htpasswd -nbBC10 test-user-2 test-password-2` &lt;/pre&gt; | `[]` |
| `loki.tracing` | object | Enable tracing | `{"enabled":false}` |
| `loki.ui` | object | Optional Loki UI: Provides access to a operators UI for Loki distributed. When enabled UI will be available at /ui/ of loki-gateway | `{"enabled":false,"gateway":{"enabled":true}}` |
| `loki.useTestSchema` | bool | a real Loki install requires a proper schemaConfig defined above this, however for testing or playing around you can enable useTestSchema | `false` |

## lokiCanary

Configuration for the Loki Canary, which continuously pushes logs to and queries them from this Loki installation to verify it is working correctly.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `lokiCanary.affinity` | object | Affinity for canary pods | `{}` |
| `lokiCanary.annotations` | object | Additional annotations for the `loki-canary` Daemonset | `{}` |
| `lokiCanary.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `false` |
| `lokiCanary.containerSecurityContext` | object | The SecurityContext for loki-canary containers | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `lokiCanary.dnsConfig` | object | DNS config for canary pods | `{}` |
| `lokiCanary.enabled` | bool |  | `true` |
| `lokiCanary.extraArgs` | list | Additional CLI arguments for the 'loki-canary' command | `[]` |
| `lokiCanary.extraEnv` | list | Environment variables to add to the canary pods | `[]` |
| `lokiCanary.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the canary pods | `[]` |
| `lokiCanary.extraVolumeMounts` | list | Volume mounts to add to the canary pods | `[]` |
| `lokiCanary.extraVolumes` | list | Volumes to add to the canary pods | `[]` |
| `lokiCanary.hostUsers` | string | Use the host's user namespace in loki-canary pods | `nil` |
| `lokiCanary.image.digest` | string | Overrides the image tag with an image digest | `nil` |
| `lokiCanary.image.pullPolicy` | string | Docker image pull policy | `"IfNotPresent"` |
| `lokiCanary.image.registry` | string | The Docker registry | `"docker.io"` |
| `lokiCanary.image.repository` | string | Docker image repository | `"grafana/loki-canary"` |
| `lokiCanary.image.tag` | string | Overrides the image tag whose default is the chart's appVersion | `nil` |
| `lokiCanary.kind` | string | The type of the loki canary k8s rollout. This can be a DaemonSet or Deployment. | `"DaemonSet"` |
| `lokiCanary.labelname` | string | The name of the label to look for at loki when doing the checks. | `"pod"` |
| `lokiCanary.labels` | object | Additional labels for the `loki-canary` Daemonset | `{}` |
| `lokiCanary.livenessProbe` | object | Liveness probe | `{"httpGet":{"path":"/metrics","port":"http-metrics"},"initialDelaySeconds":15,"timeoutSeconds":1}` |
| `lokiCanary.lokiurl` | string | If set overwrites the default value set by loki.host helper function. Use this if gateway not enabled. | `nil` |
| `lokiCanary.nodeSelector` | object | Node selector for canary pods | `{}` |
| `lokiCanary.podAnnotations` | object | Additional annotations for each `loki-canary` pod | `{}` |
| `lokiCanary.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `lokiCanary.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `lokiCanary.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `lokiCanary.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `lokiCanary.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `lokiCanary.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `lokiCanary.podLabels` | object | Additional labels for each `loki-canary` pod | `{}` |
| `lokiCanary.podSecurityContext` | object | The SecurityContext for loki-canary pods | `{"fsGroup":10001,"fsGroupChangePolicy":"OnRootMismatch","runAsGroup":10001,"runAsNonRoot":true,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `lokiCanary.priorityClassName` | string | The name of the PriorityClass for loki-canary pods | `nil` |
| `lokiCanary.push` | bool |  | `true` |
| `lokiCanary.readinessProbe` | object | Readiness probe | `{"httpGet":{"path":"/metrics","port":"http-metrics"},"initialDelaySeconds":15,"timeoutSeconds":1}` |
| `lokiCanary.replicas` | int | Replicas for `loki-canary` when using a Deployment | `1` |
| `lokiCanary.resizePolicy` | list | Container resize policy for the canary Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `lokiCanary.resources` | object | Resource requests and limits for the canary | `{}` |
| `lokiCanary.revisionHistoryLimit` | int | The number of old ReplicaSets to retain to allow rollback | `10` |
| `lokiCanary.service.annotations` | object | Annotations for loki-canary Service | `{}` |
| `lokiCanary.service.labels` | object | Additional labels for loki-canary Service | `{}` |
| `lokiCanary.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `lokiCanary.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `false` |
| `lokiCanary.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `true` |
| `lokiCanary.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `lokiCanary.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `lokiCanary.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `lokiCanary.startupProbe` | object | Startup probe | `{}` |
| `lokiCanary.tenant.name` | string | Name of the tenant for loki-canary | `"self-monitoring"` |
| `lokiCanary.tenant.password` | string | Password of the gateway for Basic auth for loki-canary | `nil` |
| `lokiCanary.tolerations` | list | Tolerations for canary pods | `[]` |
| `lokiCanary.updateStrategy.rollingUpdate.maxUnavailable` | int | Pod Disruption Budget maxUnavailable | `1` |

## memberlist

Configuration for the memberlist service, the gossip-based ring Loki components use for service discovery and hash-ring coordination.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `memberlist.service.annotations` | object |  | `{}` |
| `memberlist.service.publishNotReadyAddresses` | bool |  | `false` |

## memcached

Common configuration shared by the Memcached deployments backing Loki's caches. You can use a self-hosted Memcached by setting enabled to false and providing addresses.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `memcached.containerSecurityContext` | object | The SecurityContext for memcached containers | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `memcached.enabled` | bool | Enable the built in memcached server provided by the chart | `true` |
| `memcached.image.pullPolicy` | string | Memcached Docker image pull policy | `"IfNotPresent"` |
| `memcached.image.registry` | string | Memcached Docker image registry | `"docker.io"` |
| `memcached.image.repository` | string | Memcached Docker image repository | `"library/memcached"` |
| `memcached.image.tag` | string | Memcached Docker image tag | `"1.6.45-alpine"` |
| `memcached.livenessProbe` | object | Liveness probe for memcached pods | `{"failureThreshold":3,"initialDelaySeconds":30,"periodSeconds":10,"tcpSocket":{"port":"client"},"timeoutSeconds":5}` |
| `memcached.podSecurityContext` | object | The SecurityContext override for memcached pods | `{"fsGroup":11211,"runAsGroup":11211,"runAsNonRoot":true,"runAsUser":11211,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `memcached.priorityClassName` | string | The name of the PriorityClass for memcached pods | `nil` |
| `memcached.readinessProbe` | object | Readiness probe for memcached pods (probe port defaults to container port) | `{"failureThreshold":6,"initialDelaySeconds":5,"periodSeconds":5,"tcpSocket":{"port":"client"},"timeoutSeconds":3}` |
| `memcached.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `memcached.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `false` |
| `memcached.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `true` |
| `memcached.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `memcached.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `memcached.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `memcached.startupProbe` | object | Startup probe for memcached pods | `{}` |

## memcachedExporter

Configuration for the Prometheus Memcached exporter sidecar that exposes cache metrics.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `memcachedExporter.containerSecurityContext` | object | The SecurityContext for memcached exporter containers | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `memcachedExporter.enabled` | bool | Whether memcached metrics should be exported | `true` |
| `memcachedExporter.extraArgs` | object | Extra args to add to the exporter container. Example: extraArgs: memcached.tls.enable: true memcached.tls.cert-file: /certs/cert.crt memcached.tls.key-file: /certs/cert.key memcached.tls.ca-file: /certs/ca.crt memcached.tls.insecure-skip-verify: false memcached.tls.server-name: memcached | `{}` |
| `memcachedExporter.image.pullPolicy` | string | Memcached exporter Docker image pull policy | `"IfNotPresent"` |
| `memcachedExporter.image.registry` | string | Memcached exporter Docker image registry | `"docker.io"` |
| `memcachedExporter.image.repository` | string | Memcached exporter Docker image repository | `"prom/memcached-exporter"` |
| `memcachedExporter.image.tag` | string | Memcached exporter Docker image tag | `"v0.17.0"` |
| `memcachedExporter.livenessProbe` | object | Liveness probe for memcached exporter | `{"failureThreshold":3,"httpGet":{"path":"/metrics","port":"http-metrics"},"initialDelaySeconds":30,"periodSeconds":10,"timeoutSeconds":5}` |
| `memcachedExporter.readinessProbe` | object | Readiness probe for memcached exporter | `{"failureThreshold":3,"httpGet":{"path":"/metrics","port":"http-metrics"},"initialDelaySeconds":5,"periodSeconds":5,"timeoutSeconds":3}` |
| `memcachedExporter.resizePolicy` | list | Container resize policy for the memcached exporter Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `memcachedExporter.resources.limits` | object |  | `{}` |
| `memcachedExporter.resources.requests` | object |  | `{}` |
| `memcachedExporter.startupProbe` | object | Startup probe for memcached exporter | `{}` |

## migrate

Options that may be necessary when performing a migration from another helm chart

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `migrate.fromDistributed.enabled` | bool | Set to true if migrating from a distributed helm chart | `false` |
| `migrate.fromDistributed.memberlistService` | string | If migrating from a distributed service, provide the distributed deployment's memberlist service DNS so the new deployment can join its ring. | `""` |

## monitoring

Monitoring section determines which monitoring features to enable

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `monitoring.additionalPrometheusRules` | object | Additional PrometheusRule objects to create alongside the chart-managed ones. Each key becomes the resource name suffix; the value must contain a 'groups' key with standard Prometheus rule groups. Can contain both recording rules and alerts. e.g.: additionalPrometheusRules: my-alerts: groups: - name: my-loki-alerts rules: - alert: MyLokiAlert expr: 'up{job="loki"} == 0' | `{}` |
| `monitoring.alerts.additionalAggregationLabels` | list | Extra label dimensions added to all by() aggregation clauses in alert expressions | `[]` |
| `monitoring.alerts.additionalRuleAnnotations` | object | Additional annotations applied to all individual alert rules | `{}` |
| `monitoring.alerts.additionalRuleLabels` | object | Additional labels applied to all individual alert rules | `{}` |
| `monitoring.alerts.annotations` | object | Additional annotations for the alerts PrometheusRule resource | `{}` |
| `monitoring.alerts.disabled` | object | Selectively disable individual alerts by name. e.g.: disabled: { LokiRequestErrors: true } | `{}` |
| `monitoring.alerts.enabled` | bool | If enabled, create a PrometheusRule resource with Loki alert rules | `false` |
| `monitoring.alerts.keepFiringFor` | string | Global keepFiringFor applied to all alert rules | `""` |
| `monitoring.alerts.labels` | object | Additional labels for the alerts PrometheusRule resource | `{}` |
| `monitoring.alerts.overrides` | object | Override for/severity on individual alerts. e.g.: overrides: { LokiRequestErrors: { for: 5m, severity: warning } } | `{}` |
| `monitoring.appInstanceLabelName` | string | Label name for the app_instance label injected into scraped metrics and recording rules. | `"app_instance"` |
| `monitoring.appInstanceLabelValue` | string | Value for the app_instance label injected into scraped metrics and recording rules. Supports Helm templating. | `"{{ include \"loki.fullname\" . }}"` |
| `monitoring.dashboards.annotations` | object | Additional annotations for the dashboards ConfigMap resources | `{}` |
| `monitoring.dashboards.defaultDashboardsEditable` | bool | Editable flag for the default dashboards | `true` |
| `monitoring.dashboards.defaultDashboardsInterval` | string | Default interval for Grafana dashboards | `"1m"` |
| `monitoring.dashboards.defaultDashboardsTimezone` | string | Timezone for the default dashboards | `"utc"` |
| `monitoring.dashboards.enabled` | bool | If enabled, create configmap resources with dashboards for monitoring Loki | `false` |
| `monitoring.dashboards.grafanaOperator.annotations` | object | Additional annotations for the GrafanaOperator resources | `{}` |
| `monitoring.dashboards.grafanaOperator.enabled` | bool | If enabled, GrafanaOperator resources for Grafana Operator are created to deploy the dashboards. | `false` |
| `monitoring.dashboards.grafanaOperator.folder` | string | Which folder contains all dashboards in Grafana # This folder will be created on the Root level # Only one of 'folder', 'folderUID' or 'folderRef' can be set # | `"General"` |
| `monitoring.dashboards.grafanaOperator.folderRef` | string | Which GrafanaFolder reference contains all dashboards in Grafana # This allows you to use subfolder hierarchy. # Only one of 'folder', 'folderUID' or 'folderRef' can be set # | `nil` |
| `monitoring.dashboards.grafanaOperator.folderUID` | string | Which UID of the target folder contains all dashboards in Grafana # This allows you to use subfolder hierarchy # Only one of 'folder', 'folderUID' or 'folderRef' can be set # | `nil` |
| `monitoring.dashboards.grafanaOperator.instanceSelector` | object | instanceSelector for the GrafanaDashboard resources # The grafana-operator matches this against the labels on your Grafana CR # An empty selector (the default) matches every Grafana instance | `{}` |
| `monitoring.dashboards.grafanaOperator.labels` | object | Additional labels for the GrafanaOperator resources | `{}` |
| `monitoring.dashboards.grafanaOperator.resyncPeriod` | string | How frequently the operator should resync resources (in duration format) | `"10m"` |
| `monitoring.dashboards.labels` | object | Labels for the dashboards ConfigMap resources | `{"grafana_dashboard":"1"}` |
| `monitoring.multiCluster.clusterName` | string | Value for the 'cluster' label. Supports Helm templating. When empty, recording rules and alerts are not modified — only dashboards show the cluster variable. e.g. clusterName: '{{ .Values.global.clusterName }}' | `""` |
| `monitoring.multiCluster.enabled` | bool | When enabled, shows the cluster variable in dashboards. If clusterName is also set, the chart adds 'cluster' labels to scraped metrics, recording rules, and alerts, and includes 'cluster' in recording rule aggregations. If clusterName is left empty, only dashboard changes are applied — use this when an external tool (e.g. Grafana Alloy) manages cluster label injection and PromQL rewriting. | `false` |
| `monitoring.namespace` | string | Alternative namespace to deploy all monitoring resources (ServiceMonitor, PrometheusRule, ConfigMap dashboards) in. Defaults to the release namespace. | `nil` |
| `monitoring.rules.additionalAggregationLabels` | list | Extra label dimensions added to all by() aggregation clauses in recording rules | `[]` |
| `monitoring.rules.additionalRuleAnnotations` | object | Additional annotations applied to all individual recording rules | `{}` |
| `monitoring.rules.additionalRuleLabels` | object | Additional labels applied to all individual recording rules | `{}` |
| `monitoring.rules.annotations` | object | Additional annotations for the PrometheusRule resource | `{}` |
| `monitoring.rules.enabled` | bool | If enabled, create a PrometheusRule resource with Loki recording rules | `false` |
| `monitoring.rules.labels` | object | Additional labels for the PrometheusRule resource | `{}` |
| `monitoring.serviceMonitor.annotations` | object | ServiceMonitor annotations | `{}` |
| `monitoring.serviceMonitor.enabled` | bool | If enabled, ServiceMonitor resources for Prometheus Operator are created | `false` |
| `monitoring.serviceMonitor.interval` | string | ServiceMonitor scrape interval Default is 15s because included recording rules use a 1m rate, and scrape interval needs to be at least 1/4 rate interval. | `"15s"` |
| `monitoring.serviceMonitor.jobPrefix` | string | The job label is required for the included dashboards to work. | `"{{ include \"loki.namespace\" $ }}/"` |
| `monitoring.serviceMonitor.labels` | object | Additional ServiceMonitor labels | `{}` |
| `monitoring.serviceMonitor.metricRelabelings` | list | ServiceMonitor metric relabel configs to apply to samples before ingestion https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#endpoint | `[]` |
| `monitoring.serviceMonitor.namespaceSelector` | object | Namespace selector for ServiceMonitor resources | `{}` |
| `monitoring.serviceMonitor.relabelings` | list | ServiceMonitor relabel configs to apply to samples before scraping https://github.com/prometheus-operator/prometheus-operator/blob/master/Documentation/api.md#relabelconfig | `[]` |
| `monitoring.serviceMonitor.scheme` | string | ServiceMonitor will use http by default, but you can pick https as well | `"http"` |
| `monitoring.serviceMonitor.scrapeTimeout` | string | ServiceMonitor scrape timeout in Go duration format (e.g. 15s) | `nil` |
| `monitoring.serviceMonitor.tlsConfig` | string | ServiceMonitor will use these tlsConfig settings to make the health check requests | `nil` |

## networkPolicy

Configuration for the Kubernetes NetworkPolicies that control inbound and outbound traffic for Loki components.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `networkPolicy.alertmanager.namespaceSelector` | object | Specifies the namespace the Alertmanager is running in | `{}` |
| `networkPolicy.alertmanager.podSelector` | object | Specifies the Alertmanager Pods. As this is cross-namespace communication, you also need the namespaceSelector. | `{}` |
| `networkPolicy.alertmanager.port` | int | Specify the Alertmanager port used for alerting | `9093` |
| `networkPolicy.discovery.namespaceSelector` | object | Specifies the namespace the discovery Pods are running in | `{}` |
| `networkPolicy.discovery.podSelector` | object | Specifies the Pods labels used for discovery. As this is cross-namespace communication, you also need the namespaceSelector. | `{}` |
| `networkPolicy.discovery.port` | int | Specify the port used for discovery | `nil` |
| `networkPolicy.enabled` | bool | Specifies whether Network Policies should be created | `false` |
| `networkPolicy.externalStorage.cidrs` | list | Specifies specific network CIDRs you want to limit access to | `[]` |
| `networkPolicy.externalStorage.ports` | list | Specify the port used for external storage, e.g. AWS S3 | `[]` |
| `networkPolicy.ingress.namespaceSelector` | object | Specifies the namespaces which are allowed to access the http port | `{}` |
| `networkPolicy.ingress.podSelector` | object | Specifies the Pods which are allowed to access the http port. As this is cross-namespace communication, you also need the namespaceSelector. | `{}` |
| `networkPolicy.metrics.cidrs` | list | Specifies specific network CIDRs which are allowed to access the metrics port. In case you use namespaceSelector, you also have to specify your kubelet networks here. The metrics ports are also used for probes. | `[]` |
| `networkPolicy.metrics.namespaceSelector` | object | Specifies the namespaces which are allowed to access the metrics port | `{}` |
| `networkPolicy.metrics.podSelector` | object | Specifies the Pods which are allowed to access the metrics port. As this is cross-namespace communication, you also need the namespaceSelector. | `{}` |

## overridesExporter

Configuration for the overrides-exporter

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `overridesExporter.affinity` | object | Affinity for overrides-exporter pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `overridesExporter.annotations` | object | Annotations for overrides-exporter | `{}` |
| `overridesExporter.appProtocol` | object | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" @deprecated -- This option is deprecated in favor of `overridesExporter.service.appProtocol` | `{"grpc":""}` |
| `overridesExporter.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `overridesExporter.dnsConfig` | object | DNSConfig for overrides-exporter | `{}` |
| `overridesExporter.enabled` | bool | The overrides-exporter component is optional and can be disabled if desired. | `false` |
| `overridesExporter.extraArgs` | list | Additional CLI args for the overrides-exporter | `[]` |
| `overridesExporter.extraContainers` | list | Containers to add to the overrides-exporter pods | `[]` |
| `overridesExporter.extraEnv` | list | Environment variables to add to the overrides-exporter pods | `[]` |
| `overridesExporter.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the overrides-exporter pods | `[]` |
| `overridesExporter.extraVolumeMounts` | list | Volume mounts to add to the overrides-exporter pods | `[]` |
| `overridesExporter.extraVolumes` | list | Volumes to add to the overrides-exporter pods | `[]` |
| `overridesExporter.hostAliases` | list | hostAliases to add | `[]` |
| `overridesExporter.hostUsers` | string | Use the host's user namespace in the overrides-exporter | `nil` |
| `overridesExporter.image.registry` | string | The Docker registry for the overrides-exporter image. Overrides `loki.image.registry` | `nil` |
| `overridesExporter.image.repository` | string | Docker image repository for the overrides-exporter image. Overrides `loki.image.repository` | `nil` |
| `overridesExporter.image.tag` | string | Docker image tag for the overrides-exporter image. Overrides `loki.image.tag` | `nil` |
| `overridesExporter.initContainers` | list | Init containers to add to the overrides-exporter pods | `[]` |
| `overridesExporter.kind` | string | Kind of backend workload. StatefulSet and Deployment are supported. | `"Deployment"` |
| `overridesExporter.labels` | object | Labels for overrides-exporter | `{}` |
| `overridesExporter.maxUnavailable` | string | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `nil` |
| `overridesExporter.nodeSelector` | object | Node selector for overrides-exporter pods | `{}` |
| `overridesExporter.podAnnotations` | object | Annotations for overrides-exporter pods | `{}` |
| `overridesExporter.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `overridesExporter.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `overridesExporter.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `overridesExporter.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `overridesExporter.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `overridesExporter.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `overridesExporter.podLabels` | object | Labels for overrides-exporter pods | `{}` |
| `overridesExporter.priorityClassName` | string | The name of the PriorityClass for overrides-exporter pods | `nil` |
| `overridesExporter.replicas` | int | Number of replicas for the overrides-exporter. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `0` |
| `overridesExporter.resizePolicy` | list | Container resize policy for the overrides-exporter Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `overridesExporter.resources` | object | Resource requests and limits for the overrides-exporter | `{}` |
| `overridesExporter.service.annotations` | object | Annotations for overrides-exporter Service | `{}` |
| `overridesExporter.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `overridesExporter.service.ipFamilies` | list | ipFamilies for overrides-exporter Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `overridesExporter.service.ipFamilyPolicy` | string | ipFamilyPolicy for overrides-exporter Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `overridesExporter.service.labels` | object | Additional labels for overrides-exporter Service | `{}` |
| `overridesExporter.service.sessionAffinity` | string | Session affinity for overrides-exporter Service | `""` |
| `overridesExporter.service.sessionAffinityConfig` | object | Session affinity config for overrides-exporter Service | `{}` |
| `overridesExporter.service.trafficDistribution` | string | trafficDistribution for overrides-exporter Service | `""` |
| `overridesExporter.service.type` | string | Service Type for overrides-exporter Service | `"ClusterIP"` |
| `overridesExporter.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `overridesExporter.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `overridesExporter.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `overridesExporter.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `overridesExporter.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `overridesExporter.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `overridesExporter.serviceAnnotations` | object | Annotations for overrides-exporter service @deprecated -- This option is deprecated in favor of `overridesExporter.service.annotations` | `{}` |
| `overridesExporter.serviceLabels` | object | Labels for overrides-exporter service @deprecated -- This option is deprecated in favor of `overridesExporter.service.labels` | `{}` |
| `overridesExporter.terminationGracePeriodSeconds` | int | Grace period to allow the overrides-exporter to shutdown before it is killed | `300` |
| `overridesExporter.topologySpreadConstraints` | list | Topology Spread Constraints for overrides-exporter pods The value will be passed through tpl. | `[]` |

## patternIngester

Configuration for the pattern ingester

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `patternIngester.affinity` | object | Affinity for pattern ingester pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `patternIngester.annotations` | object | Annotations for pattern ingester | `{}` |
| `patternIngester.appProtocol` | object | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `{"grpc":""}` |
| `patternIngester.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `patternIngester.dnsConfig` | object | DNSConfig for pattern ingester pods | `{}` |
| `patternIngester.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `Distributed`. | `false` |
| `patternIngester.extraArgs` | list | Additional CLI args for the pattern ingester | `[]` |
| `patternIngester.extraContainers` | list | Containers to add to the pattern ingester pods | `[]` |
| `patternIngester.extraEnv` | list | Environment variables to add to the pattern ingester pods | `[]` |
| `patternIngester.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the pattern ingester pods | `[]` |
| `patternIngester.extraVolumeMounts` | list | Volume mounts to add to the pattern ingester pods | `[]` |
| `patternIngester.extraVolumes` | list | Volumes to add to the pattern ingester pods | `[]` |
| `patternIngester.hostAliases` | list | hostAliases to add | `[]` |
| `patternIngester.hostUsers` | string | Use the host's user namespace in the pattern ingester | `nil` |
| `patternIngester.image.registry` | string | The Docker registry for the pattern ingester image. Overrides `loki.image.registry` | `nil` |
| `patternIngester.image.repository` | string | Docker image repository for the pattern ingester image. Overrides `loki.image.repository` | `nil` |
| `patternIngester.image.tag` | string | Docker image tag for the pattern ingester image. Overrides `loki.image.tag` | `nil` |
| `patternIngester.initContainers` | list | Init containers to add to the pattern ingester pods | `[]` |
| `patternIngester.kind` | string | Kind of compactor deployment. StatefulSet and Deployment are supported. | `"StatefulSet"` |
| `patternIngester.labels` | object | Labels for pattern ingester | `{}` |
| `patternIngester.livenessProbe` | object | liveness probe settings for pattern ingester pods. If empty use `loki.livenessProbe` | `{}` |
| `patternIngester.maxUnavailable` | string | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `nil` |
| `patternIngester.nodeSelector` | object | Node selector for pattern ingester pods | `{}` |
| `patternIngester.persistence.claims` | list | List of the pattern ingester PVCs |  |
| `patternIngester.persistence.claims[0].accessModes` | list | Set access modes on the PersistentVolumeClaim | `["ReadWriteOnce"]` |
| `patternIngester.persistence.claims[0].annotations` | object | Annotations for pattern ingester PVCs | `{}` |
| `patternIngester.persistence.claims[0].labels` | object | Labels for pattern ingester PVCs | `{}` |
| `patternIngester.persistence.claims[0].volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `patternIngester.persistence.enableStatefulSetAutoDeletePVC` | bool | Enable StatefulSetAutoDeletePVC feature | `false` |
| `patternIngester.persistence.enabled` | bool | Enable creating PVCs for the pattern ingester | `false` |
| `patternIngester.persistence.size` | string | Size of persistent disk | `"10Gi"` |
| `patternIngester.persistence.storageClass` | string | Storage class to be used. If defined, storageClassName: &lt;storageClass&gt;. If set to "-", storageClassName: "", which disables dynamic provisioning. If empty or set to null, no storageClassName spec is set, choosing the default provisioner (gp2 on AWS, standard on GKE, AWS, and OpenStack). | `nil` |
| `patternIngester.podAnnotations` | object | Annotations for pattern ingester pods | `{}` |
| `patternIngester.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `patternIngester.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `patternIngester.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `patternIngester.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `patternIngester.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `patternIngester.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `patternIngester.podLabels` | object | Labels for pattern ingester pods | `{}` |
| `patternIngester.priorityClassName` | string | The name of the PriorityClass for pattern ingester pods | `nil` |
| `patternIngester.readinessProbe` | object | readiness probe settings for pattern ingester pods. If empty, use `loki.readinessProbe` | `{}` |
| `patternIngester.replicas` | int | Number of replicas for the pattern ingester. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `0` |
| `patternIngester.resizePolicy` | list | Container resize policy for the pattern ingester Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `patternIngester.resources` | object | Resource requests and limits for the pattern ingester | `{}` |
| `patternIngester.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `patternIngester.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `patternIngester.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `patternIngester.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `patternIngester.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `patternIngester.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `patternIngester.startupProbe` | object | startup probe settings for pattern ingester pods. If empty use `loki.startupProbe` | `{}` |
| `patternIngester.statefulSetRecreateJob` | object | EXPERIMENTAL: Enable creating a Job to recreate the compactor StatefulSet when the compactor configuration is changed. # Only applicable if `compactor.kind` is StatefulSet. Useful for PVC size changes, which require recreating the StatefulSet. | `{"enabled":false}` |
| `patternIngester.strategy` | object | UpdateStrategy or Strategy for the pattern ingester. | `{"rollingUpdate":{"partition":0}}` |
| `patternIngester.terminationGracePeriodSeconds` | int | Grace period to allow the pattern ingester to shutdown before it is killed | `30` |
| `patternIngester.topologySpreadConstraints` | list | Topology Spread Constraints for pattern ingester pods The value will be passed through tpl. | `[]` |

## querier

Configuration for the querier

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `querier.affinity` | object | Affinity for querier pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `querier.annotations` | object | Annotations for querier | `{}` |
| `querier.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" @deprecated -- This option is deprecated in favor of `querier.service.appProtocol.grpc` | `""` |
| `querier.autoscaling.behavior.enabled` | bool | Enable autoscaling behaviours | `false` |
| `querier.autoscaling.behavior.scaleDown` | object | define scale down policies, must conform to HPAScalingRules | `{}` |
| `querier.autoscaling.behavior.scaleUp` | object | define scale up policies, must conform to HPAScalingRules | `{}` |
| `querier.autoscaling.customMetrics` | list | Allows one to define custom metrics using the HPA/v2 schema (for example, Pods, Object or External metrics) | `[]` |
| `querier.autoscaling.enabled` | bool | Enable autoscaling for the querier, this is only used if `indexGateway.enabled: true` | `false` |
| `querier.autoscaling.maxReplicas` | int | Maximum autoscaling replicas for the querier | `3` |
| `querier.autoscaling.minReplicas` | int | Minimum autoscaling replicas for the querier | `1` |
| `querier.autoscaling.targetCPUUtilizationPercentage` | int | Target CPU utilisation percentage for the querier | `60` |
| `querier.autoscaling.targetMemoryUtilizationPercentage` | string | Target memory utilisation percentage for the querier | `nil` |
| `querier.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `querier.dnsConfig` | object | DNSConfig for querier pods | `{}` |
| `querier.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `Distributed`. | `true` |
| `querier.extraArgs` | list | Additional CLI args for the querier | `[]` |
| `querier.extraContainers` | list | Containers to add to the querier pods | `[]` |
| `querier.extraEnv` | list | Environment variables to add to the querier pods | `[]` |
| `querier.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the querier pods | `[]` |
| `querier.extraVolumeMounts` | list | Volume mounts to add to the querier pods | `[]` |
| `querier.extraVolumes` | list | Volumes to add to the querier pods | `[]` |
| `querier.hostAliases` | list | hostAliases to add | `[]` |
| `querier.hostUsers` | string | Use the host's user namespace in the querier | `nil` |
| `querier.image.registry` | string | The Docker registry for the querier image. Overrides `loki.image.registry` | `nil` |
| `querier.image.repository` | string | Docker image repository for the querier image. Overrides `loki.image.repository` | `nil` |
| `querier.image.tag` | string | Docker image tag for the querier image. Overrides `loki.image.tag` | `nil` |
| `querier.initContainers` | list | Init containers to add to the querier pods | `[]` |
| `querier.kedaAutoscaling.behavior` | object | Behavior configuration for KEDA ScaledObject (horizontalPodAutoscalerConfig.behavior). Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#advanced | `{}` |
| `querier.kedaAutoscaling.cooldownPeriod` | string | Period to wait after the last trigger reported active before scaling the resource back to 0 (seconds). Defaults to defaults.kedaAutoscaling.cooldownPeriod if not set. | `nil` |
| `querier.kedaAutoscaling.enabled` | bool | Enable KEDA autoscaling for the querier. Mutually exclusive with autoscaling.enabled | `false` |
| `querier.kedaAutoscaling.fallback` | object | Fallback configuration for KEDA ScaledObject. Applied when the trigger metric cannot be retrieved. Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#fallback | `{}` |
| `querier.kedaAutoscaling.maxReplicas` | int | Maximum replicas for KEDA autoscaling | `10` |
| `querier.kedaAutoscaling.minReplicas` | int | Minimum replicas for KEDA autoscaling | `1` |
| `querier.kedaAutoscaling.pollingInterval` | string | Interval (seconds) to poll each trigger. Defaults to defaults.kedaAutoscaling.pollingInterval if not set. | `nil` |
| `querier.kedaAutoscaling.triggers` | list | KEDA triggers for the querier. Defaults to a Prometheus trigger using inflight requests. | `[{"metadata":{"authModes":"{{ .Values.defaults.kedaAutoscaling.authentication.authModes }}","customHeaders":"{{ .Values.defaults.kedaAutoscaling.customHeaders }}","ignoreNullValues":"{{ .Values.defaults.kedaAutoscaling.ignoreNullValues }}","query":"sum(max_over_time(loki_query_scheduler_inflight_requests{quantile=\"0.75\"}[2m]))","serverAddress":"{{ .Values.defaults.kedaAutoscaling.prometheusAddress }}","threshold":"4","unsafeSsl":"{{ .Values.defaults.kedaAutoscaling.unsafeSsl }}"},"name":"querier_autoscaling_metric","type":"prometheus"}]` |
| `querier.kind` | string | Kind of compactor deployment. StatefulSet and Deployment are supported. | `"Deployment"` |
| `querier.labels` | object | Labels for querier | `{}` |
| `querier.maxSurge` | int | Max Surge for querier pods | `0` |
| `querier.maxUnavailable` | string | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `nil` |
| `querier.nodeSelector` | object | Node selector for querier pods | `{}` |
| `querier.podAnnotations` | object | Annotations for querier pods | `{}` |
| `querier.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `querier.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `querier.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `querier.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `querier.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `querier.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `querier.podLabels` | object | Labels for querier pods | `{}` |
| `querier.priorityClassName` | string | The name of the PriorityClass for querier pods | `nil` |
| `querier.replicas` | int | Number of replicas for the querier. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `0` |
| `querier.resizePolicy` | list | Container resize policy for the querier Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `querier.resources` | object | Resource requests and limits for the querier | `{}` |
| `querier.service.annotations` | object | Annotations for querier Service | `{}` |
| `querier.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `querier.service.ipFamilies` | list | ipFamilies for querier Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `querier.service.ipFamilyPolicy` | string | ipFamilyPolicy for querier Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `querier.service.labels` | object | Additional labels for querier Service | `{}` |
| `querier.service.sessionAffinity` | string | Session affinity for querier Service | `""` |
| `querier.service.sessionAffinityConfig` | object | Session affinity config for querier Service | `{}` |
| `querier.service.trafficDistribution` | string | trafficDistribution for querier Service | `""` |
| `querier.service.type` | string | Service Type for querier Service | `"ClusterIP"` |
| `querier.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `querier.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `querier.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `querier.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `querier.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `querier.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `querier.serviceAnnotations` | object | Annotations for querier service @deprecated -- This option is deprecated in favor of `querier.service.annotations` | `{}` |
| `querier.serviceLabels` | object | Labels for querier service @deprecated -- This option is deprecated in favor of `querier.service.labels` | `{}` |
| `querier.serviceType` | string | Service type for querier service @deprecated -- This option is deprecated in favor of `querier.service.type` | `"ClusterIP"` |
| `querier.strategy.rollingUpdate.maxUnavailable` | int | Pod Disruption Budget maxUnavailable | `1` |
| `querier.terminationGracePeriodSeconds` | int | Grace period to allow the querier to shutdown before it is killed | `30` |
| `querier.tolerations` | list | Tolerations for querier pods | `[]` |
| `querier.topologySpreadConstraints` | list | topologySpread for querier pods. The value will be passed through tpl. | `Defaults to allow skew no more then 1 node` |
| `querier.trafficDistribution` | string | trafficDistribution for querier service | `""` |

## queryFrontend

Configuration for the query-frontend

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `queryFrontend.affinity` | object | Affinity for query-frontend pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `queryFrontend.annotations` | object | Annotations for query-frontend | `{}` |
| `queryFrontend.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" @deprecated -- This option is deprecated in favor of `queryFrontend.service.appProtocol.grpc` | `""` |
| `queryFrontend.autoscaling.behavior.enabled` | bool | Enable autoscaling behaviours | `false` |
| `queryFrontend.autoscaling.behavior.scaleDown` | object | define scale down policies, must conform to HPAScalingRules | `{}` |
| `queryFrontend.autoscaling.behavior.scaleUp` | object | define scale up policies, must conform to HPAScalingRules | `{}` |
| `queryFrontend.autoscaling.customMetrics` | list | Allows one to define custom metrics using the HPA/v2 schema (for example, Pods, Object or External metrics) | `[]` |
| `queryFrontend.autoscaling.enabled` | bool | Enable autoscaling for the query-frontend | `false` |
| `queryFrontend.autoscaling.maxReplicas` | int | Maximum autoscaling replicas for the query-frontend | `3` |
| `queryFrontend.autoscaling.minReplicas` | int | Minimum autoscaling replicas for the query-frontend | `1` |
| `queryFrontend.autoscaling.targetCPUUtilizationPercentage` | int | Target CPU utilisation percentage for the query-frontend | `60` |
| `queryFrontend.autoscaling.targetMemoryUtilizationPercentage` | string | Target memory utilisation percentage for the query-frontend | `nil` |
| `queryFrontend.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `queryFrontend.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `Distributed`. | `true` |
| `queryFrontend.extraArgs` | list | Additional CLI args for the query-frontend | `[]` |
| `queryFrontend.extraContainers` | list | Containers to add to the query-frontend pods | `[]` |
| `queryFrontend.extraEnv` | list | Environment variables to add to the query-frontend pods | `[]` |
| `queryFrontend.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the query-frontend pods | `[]` |
| `queryFrontend.extraVolumeMounts` | list | Volume mounts to add to the query-frontend pods | `[]` |
| `queryFrontend.extraVolumes` | list | Volumes to add to the query-frontend pods | `[]` |
| `queryFrontend.hostAliases` | list | hostAliases to add | `[]` |
| `queryFrontend.hostUsers` | string | Use the host's user namespace in the query-frontend | `nil` |
| `queryFrontend.image.registry` | string | The Docker registry for the query-frontend image. Overrides `loki.image.registry` | `nil` |
| `queryFrontend.image.repository` | string | Docker image repository for the query-frontend image. Overrides `loki.image.repository` | `nil` |
| `queryFrontend.image.tag` | string | Docker image tag for the query-frontend image. Overrides `loki.image.tag` | `nil` |
| `queryFrontend.initContainers` | list | init containers to add to the query-frontend pods | `[]` |
| `queryFrontend.kedaAutoscaling.behavior` | object | Behavior configuration for KEDA ScaledObject (horizontalPodAutoscalerConfig.behavior). Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#advanced | `{}` |
| `queryFrontend.kedaAutoscaling.cooldownPeriod` | string | Period to wait after the last trigger reported active before scaling the resource back to 0 (seconds). Defaults to defaults.kedaAutoscaling.cooldownPeriod if not set. | `nil` |
| `queryFrontend.kedaAutoscaling.enabled` | bool | Enable KEDA autoscaling for the query-frontend. Mutually exclusive with autoscaling.enabled | `false` |
| `queryFrontend.kedaAutoscaling.fallback` | object | Fallback configuration for KEDA ScaledObject. Applied when the trigger metric cannot be retrieved. Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#fallback | `{}` |
| `queryFrontend.kedaAutoscaling.maxReplicas` | int | Maximum replicas for KEDA autoscaling | `3` |
| `queryFrontend.kedaAutoscaling.minReplicas` | int | Minimum replicas for KEDA autoscaling | `1` |
| `queryFrontend.kedaAutoscaling.pollingInterval` | string | Interval (seconds) to poll each trigger. Defaults to defaults.kedaAutoscaling.pollingInterval if not set. | `nil` |
| `queryFrontend.kedaAutoscaling.triggers` | list | KEDA triggers for the query-frontend. Ref: https://keda.sh/docs/latest/scalers/ | `[]` |
| `queryFrontend.kind` | string | Kind of compactor deployment. StatefulSet and Deployment are supported. | `"Deployment"` |
| `queryFrontend.labels` | object | Labels for query-frontend | `{}` |
| `queryFrontend.loadBalancer` | object | Enable load balancer port for query-frontend | `{"enabled":true}` |
| `queryFrontend.maxUnavailable` | string | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `nil` |
| `queryFrontend.nodeSelector` | object | Node selector for query-frontend pods | `{}` |
| `queryFrontend.podAnnotations` | object | Annotations for query-frontend pods | `{}` |
| `queryFrontend.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `queryFrontend.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `queryFrontend.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `queryFrontend.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `queryFrontend.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `queryFrontend.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `queryFrontend.podLabels` | object | Labels for query-frontend pods | `{}` |
| `queryFrontend.priorityClassName` | string | The name of the PriorityClass for query-frontend pods | `nil` |
| `queryFrontend.replicas` | int | Number of replicas for the query-frontend. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `0` |
| `queryFrontend.resizePolicy` | list | Container resize policy for the query-frontend Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `queryFrontend.resources` | object | Resource requests and limits for the query-frontend | `{}` |
| `queryFrontend.service.annotations` | object | Annotations for query-frontend Service | `{}` |
| `queryFrontend.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `queryFrontend.service.ipFamilies` | list | ipFamilies for query-frontend Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `queryFrontend.service.ipFamilyPolicy` | string | ipFamilyPolicy for query-frontend Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `queryFrontend.service.labels` | object | Additional labels for query-frontend Service | `{}` |
| `queryFrontend.service.sessionAffinity` | string | Session affinity for query-frontend Service | `""` |
| `queryFrontend.service.sessionAffinityConfig` | object | Session affinity config for query-frontend Service | `{}` |
| `queryFrontend.service.trafficDistribution` | string | trafficDistribution for query-frontend Service | `""` |
| `queryFrontend.service.type` | string | Service Type for query-frontend Service | `"ClusterIP"` |
| `queryFrontend.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `queryFrontend.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `queryFrontend.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `queryFrontend.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `queryFrontend.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `queryFrontend.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `queryFrontend.serviceAnnotations` | object | Annotations for query-frontend service @deprecated -- This option is deprecated in favor of `queryFrontend.service.annotations` | `{}` |
| `queryFrontend.serviceLabels` | object | Labels for query-frontend service @deprecated -- This option is deprecated in favor of `queryFrontend.service.labels` | `{}` |
| `queryFrontend.serviceType` | string | Service type for query-frontend service @deprecated -- This option is deprecated in favor of `queryFrontend.service.type` | `"ClusterIP"` |
| `queryFrontend.strategy.rollingUpdate.maxUnavailable` | int | Pod Disruption Budget maxUnavailable | `1` |
| `queryFrontend.terminationGracePeriodSeconds` | int | Grace period to allow the query-frontend to shutdown before it is killed | `30` |
| `queryFrontend.topologySpreadConstraints` | list | Topology Spread Constraints for query-frontend pods The value will be passed through tpl. | `[]` |
| `queryFrontend.trafficDistribution` | string | trafficDistribution for query-frontend service | `""` |

## queryScheduler

Configuration for the query-scheduler

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `queryScheduler.affinity` | object | Affinity for query-scheduler pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `queryScheduler.annotations` | object | Annotations for query-scheduler | `{}` |
| `queryScheduler.appProtocol` | object | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" @deprecated -- This option is deprecated in favor of `queryScheduler.service.appProtocol` | `{"grpc":""}` |
| `queryScheduler.dnsConfig` | object | DNSConfig for query-scheduler | `{}` |
| `queryScheduler.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `Distributed`. | `true` |
| `queryScheduler.extraArgs` | list | Additional CLI args for the query-scheduler | `[]` |
| `queryScheduler.extraContainers` | list | Containers to add to the query-scheduler pods | `[]` |
| `queryScheduler.extraEnv` | list | Environment variables to add to the query-scheduler pods | `[]` |
| `queryScheduler.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the query-scheduler pods | `[]` |
| `queryScheduler.extraVolumeMounts` | list | Volume mounts to add to the query-scheduler pods | `[]` |
| `queryScheduler.extraVolumes` | list | Volumes to add to the query-scheduler pods | `[]` |
| `queryScheduler.hostAliases` | list | hostAliases to add | `[]` |
| `queryScheduler.hostUsers` | string | Use the host's user namespace in the query-scheduler | `nil` |
| `queryScheduler.image.registry` | string | The Docker registry for the query-scheduler image. Overrides `loki.image.registry` | `nil` |
| `queryScheduler.image.repository` | string | Docker image repository for the query-scheduler image. Overrides `loki.image.repository` | `nil` |
| `queryScheduler.image.tag` | string | Docker image tag for the query-scheduler image. Overrides `loki.image.tag` | `nil` |
| `queryScheduler.initContainers` | list | init containers to add to the query-scheduler pods | `[]` |
| `queryScheduler.kind` | string | Kind of query-scheduler deployment. StatefulSet and Deployment are supported. | `"Deployment"` |
| `queryScheduler.labels` | object | Labels for query-scheduler | `{}` |
| `queryScheduler.maxUnavailable` | int | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `1` |
| `queryScheduler.nodeSelector` | object | Node selector for query-scheduler pods | `{}` |
| `queryScheduler.podAnnotations` | object | Annotations for query-scheduler pods | `{}` |
| `queryScheduler.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `queryScheduler.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `queryScheduler.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `queryScheduler.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `queryScheduler.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `queryScheduler.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `queryScheduler.podLabels` | object | Labels for query-scheduler pods | `{}` |
| `queryScheduler.priorityClassName` | string | The name of the PriorityClass for query-scheduler pods | `nil` |
| `queryScheduler.replicas` | int | Number of replicas for the query-scheduler. It should be lower than `-querier.max-concurrent` to avoid generating back-pressure in queriers; it's also recommended that this value evenly divides the latter Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `0` |
| `queryScheduler.resizePolicy` | list | Container resize policy for the query-scheduler Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `queryScheduler.resources` | object | Resource requests and limits for the query-scheduler | `{}` |
| `queryScheduler.service.annotations` | object | Annotations for query-scheduler Service | `{}` |
| `queryScheduler.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `queryScheduler.service.ipFamilies` | list | ipFamilies for query-scheduler Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `queryScheduler.service.ipFamilyPolicy` | string | ipFamilyPolicy for query-scheduler Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `queryScheduler.service.labels` | object | Additional labels for query-scheduler Service | `{}` |
| `queryScheduler.service.sessionAffinity` | string | Session affinity for query-scheduler Service | `""` |
| `queryScheduler.service.sessionAffinityConfig` | object | Session affinity config for query-scheduler Service | `{}` |
| `queryScheduler.service.trafficDistribution` | string | trafficDistribution for query-scheduler Service | `""` |
| `queryScheduler.service.type` | string | Service Type for query-scheduler Service | `"ClusterIP"` |
| `queryScheduler.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `queryScheduler.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `queryScheduler.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `queryScheduler.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `queryScheduler.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `queryScheduler.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `queryScheduler.serviceAnnotations` | object | Annotations for query-scheduler service @deprecated -- This option is deprecated in favor of `queryScheduler.service.annotations` | `{}` |
| `queryScheduler.serviceLabels` | object | Labels for query-scheduler service @deprecated -- This option is deprecated in favor of `queryScheduler.service.labels` | `{}` |
| `queryScheduler.serviceType` | string | Service type for query-scheduler service @deprecated -- This option is deprecated in favor of `queryScheduler.service.type` | `"ClusterIP"` |
| `queryScheduler.strategy.rollingUpdate.maxUnavailable` | int | Pod Disruption Budget maxUnavailable | `1` |
| `queryScheduler.terminationGracePeriodSeconds` | int | Grace period to allow the query-scheduler to shutdown before it is killed | `30` |
| `queryScheduler.topologySpreadConstraints` | list | Topology Spread Constraints for query-scheduler pods The value will be passed through tpl. | `[]` |
| `queryScheduler.trafficDistribution` | string | trafficDistribution for query-scheduler service | `""` |

## rbac

Configuration for the Kubernetes RBAC resources and Pod Security Policy settings created for Loki.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `rbac.namespaced` | bool | Whether to install RBAC in the namespace only or cluster-wide. Useful if you want to watch ConfigMap globally. | `false` |
| `rbac.sccAllowHostDirVolumePlugin` | bool | Toggle this to true to allow the use of hostPath volumes on OpenShift | `false` |
| `rbac.sccEnabled` | bool | For OpenShift set sccEnabled to 'true' to use the SecurityContextConstraints. | `false` |

## read

Configuration for the read pod(s)

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `read.affinity` | object | Affinity for read pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `read.annotations` | object | Annotations for read deployment | `{}` |
| `read.autoscaling.behavior` | object | Behavior policies while scaling. | `{}` |
| `read.autoscaling.enabled` | bool | Enable autoscaling for the read, this is only used if `queryIndex.enabled: true` | `false` |
| `read.autoscaling.maxReplicas` | int | Maximum autoscaling replicas for the read | `6` |
| `read.autoscaling.minReplicas` | int | Minimum autoscaling replicas for the read | `2` |
| `read.autoscaling.targetCPUUtilizationPercentage` | int | Target CPU utilisation percentage for the read | `60` |
| `read.autoscaling.targetMemoryUtilizationPercentage` | string | Target memory utilisation percentage for the read | `nil` |
| `read.dnsConfig` | object | DNS config for read pods | `{}` |
| `read.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `SimpleScalable`. | `true` |
| `read.extraArgs` | list | Additional CLI args for the read | `[]` |
| `read.extraContainers` | list | Containers to add to the read pods | `[]` |
| `read.extraEnv` | list | Environment variables to add to the read pods | `[]` |
| `read.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the read pods | `[]` |
| `read.extraVolumeMounts` | list | Volume mounts to add to the read pods | `[]` |
| `read.extraVolumes` | list | Volumes to add to the read pods | `[]` |
| `read.fullnameOverride` | string | Override for the read fullname. By default, this is generated using the fullname template with "-backend" | `"{{ include \"loki.name\" . }}-read"` |
| `read.hostUsers` | string | Use the host's user namespace in the read pods. | `nil` |
| `read.image.registry` | string | The Docker registry for the read image. Overrides `loki.image.registry` | `nil` |
| `read.image.repository` | string | Docker image repository for the read image. Overrides `loki.image.repository` | `nil` |
| `read.image.tag` | string | Docker image tag for the read image. Overrides `loki.image.tag` | `nil` |
| `read.initContainers` | list | init containers to add to the read pods | `[]` |
| `read.kedaAutoscaling.behavior` | object | Behavior configuration for KEDA ScaledObject (horizontalPodAutoscalerConfig.behavior). Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#advanced | `{}` |
| `read.kedaAutoscaling.cooldownPeriod` | string | Period to wait after the last trigger reported active before scaling the resource back to 0 (seconds). Defaults to defaults.kedaAutoscaling.cooldownPeriod if not set. | `nil` |
| `read.kedaAutoscaling.enabled` | bool | Enable KEDA autoscaling for the read. Mutually exclusive with autoscaling.enabled | `false` |
| `read.kedaAutoscaling.fallback` | object | Fallback configuration for KEDA ScaledObject. Applied when the trigger metric cannot be retrieved. Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#fallback | `{}` |
| `read.kedaAutoscaling.maxReplicas` | int | Maximum replicas for KEDA autoscaling | `6` |
| `read.kedaAutoscaling.minReplicas` | int | Minimum replicas for KEDA autoscaling | `2` |
| `read.kedaAutoscaling.pollingInterval` | string | Interval (seconds) to poll each trigger. Defaults to defaults.kedaAutoscaling.pollingInterval if not set. | `nil` |
| `read.kedaAutoscaling.triggers` | list | KEDA triggers for the read. Ref: https://keda.sh/docs/latest/scalers/ | `[]` |
| `read.kind` | string | Kind of read deployment. StatefulSet and Deployment are supported. | `"Deployment"` |
| `read.labels` | object | Additional labels for read deployment | `{}` |
| `read.lifecycle` | object | Lifecycle for the read container | `{}` |
| `read.livenessProbe` | object | liveness probe settings for read pods. If empty, applies no livenessProbe | `{}` |
| `read.maxUnavailable` | int | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `1` |
| `read.nodeSelector` | object | Node selector for read pods | `{}` |
| `read.podAnnotations` | object | Annotations for read pods | `{}` |
| `read.podDisruptionBudget.annotations` | object | Annotations for read Pod Disruption Budget | `{}` |
| `read.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget for read pods | `true` |
| `read.podDisruptionBudget.labels` | object | Labels for read Pod Disruption Budget | `{}` |
| `read.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `read.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `read.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `read.podLabels` | object | Additional labels for each `read` pod | `{}` |
| `read.priorityClassName` | string | The name of the PriorityClass for read pods | `nil` |
| `read.replicas` | int | Number of replicas for the read. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `3` |
| `read.resizePolicy` | list | Container resize policy for the read Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `read.resources` | object | Resource requests and limits for the read | `{}` |
| `read.selectorLabels` | object | Additional selector labels for each `read` pod | `{}` |
| `read.service.annotations` | object | Annotations for read Service | `{}` |
| `read.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `read.service.ipFamilies` | list | ipFamilies for read Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `read.service.ipFamilyPolicy` | string | ipFamilyPolicy for read Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `read.service.labels` | object | Additional labels for read Service | `{}` |
| `read.service.sessionAffinity` | string | Session affinity for read Service | `""` |
| `read.service.sessionAffinityConfig` | object | Session affinity config for read Service | `{}` |
| `read.service.trafficDistribution` | string | trafficDistribution for read Service | `""` |
| `read.service.type` | string | Service Type for read Service | `"ClusterIP"` |
| `read.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `read.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `read.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `read.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `read.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `read.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `read.startupProbe` | object | startup probe for the read pods. If empty, applies no startupProbe | `{}` |
| `read.strategy.rollingUpdate.maxUnavailable` | int | Pod Disruption Budget maxUnavailable | `1` |
| `read.terminationGracePeriodSeconds` | int | Grace period to allow the read to shutdown before it is killed | `30` |
| `read.topologySpreadConstraints` | list | Topology Spread Constraints for read pods The value will be passed through tpl. | `[]` |

## resultsCache

Configuration for the Memcached-based results-cache, which caches query results to speed up repeated queries.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `resultsCache.addresses` | string | Comma separated addresses list in DNS Service Discovery format | `"dnssrvnoa+_memcached-client._tcp.{{ include \"loki.resourceName\" (dict \"ctx\" $ \"component\" \"results-cache\") }}.{{ include \"loki.namespace\" $ }}.svc.{{ .Values.global.clusterDomain }}"` |
| `resultsCache.affinity` | object | Affinity for results-cache pods | `{}` |
| `resultsCache.allocatedCPU` | string | Amount of cpu allocated to results-cache for object storage (in integer or millicores). | `"500m"` |
| `resultsCache.allocatedMemory` | int | Amount of memory allocated to results-cache for object storage (in MB). | `1024` |
| `resultsCache.annotations` | object | Annotations for the results-cache pods | `{}` |
| `resultsCache.connectionLimit` | int | Maximum number of connections allowed | `16384` |
| `resultsCache.defaultValidity` | string | Specify how long cached results should be stored in the results-cache before being expired | `"12h"` |
| `resultsCache.dnsConfig` | object | DNSConfig for results-cache | `{}` |
| `resultsCache.enabled` | bool | Specifies whether memcached based results-cache should be enabled | `true` |
| `resultsCache.extraArgs` | object | Additional CLI args for results-cache | `{}` |
| `resultsCache.extraContainers` | list | Additional containers to be added to the results-cache pod. | `[]` |
| `resultsCache.extraExtendedOptions` | string | Add extended options for results-cache memcached container. The format is the same as for the memcached -o/--extend flag. Example: extraExtendedOptions: 'tls,modern,track_sizes' | `""` |
| `resultsCache.extraVolumeMounts` | list | Additional volume mounts to be added to the results-cache pod (applies to both memcached and exporter containers). Example: extraVolumeMounts: - name: extra-volume mountPath: /etc/extra-volume readOnly: true | `[]` |
| `resultsCache.extraVolumes` | list | Additional volumes to be added to the results-cache pod (applies to both memcached and exporter containers). Example: extraVolumes: - name: extra-volume secret: secretName: extra-volume-secret | `[]` |
| `resultsCache.hostUsers` | string | Use the host's user namespace in results-cache pods | `nil` |
| `resultsCache.initContainers` | list | Extra init containers for results-cache pods | `[]` |
| `resultsCache.maxItemMemory` | int | Maximum item results-cache for memcached (in MB). | `5` |
| `resultsCache.maxUnavailable` | int | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `1` |
| `resultsCache.nodeSelector` | object | Node selector for results-cache pods | `{}` |
| `resultsCache.persistence.enabled` | bool | Enable creating PVCs for the results-cache | `false` |
| `resultsCache.persistence.labels` | object | PVC additional labels | `{}` |
| `resultsCache.persistence.mountPath` | string | Volume mount path | `"/data"` |
| `resultsCache.persistence.storageClass` | string | Storage class to be used. If defined, storageClassName: &lt;storageClass&gt;. If set to "-", storageClassName: "", which disables dynamic provisioning. If empty or set to null, no storageClassName spec is set, choosing the default provisioner (gp2 on AWS, standard on GKE, AWS, and OpenStack). | `nil` |
| `resultsCache.persistence.storageSize` | string | Size of persistent disk, must be in G or Gi | `"10G"` |
| `resultsCache.persistence.volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `resultsCache.podAnnotations` | object | Annotations for results-cache pods | `{}` |
| `resultsCache.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `resultsCache.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `resultsCache.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `resultsCache.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `resultsCache.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `resultsCache.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `resultsCache.podLabels` | object | Labels for results-cache pods | `{}` |
| `resultsCache.podManagementPolicy` | string | Management policy for results-cache pods | `"Parallel"` |
| `resultsCache.port` | int | Port of the results-cache service | `11211` |
| `resultsCache.priorityClassName` | string | The name of the PriorityClass for results-cache pods | `nil` |
| `resultsCache.replicas` | int | Total number of results-cache replicas | `1` |
| `resultsCache.resizePolicy` | list | Container resize policy for the results-cache Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `resultsCache.resources` | object | Resource requests and limits for the results-cache By default a safe memory limit will be requested based on allocatedMemory value (floor (* 1.2 allocatedMemory)). | `{}` |
| `resultsCache.service` | object | Service annotations and labels | `{"annotations":{},"labels":{}}` |
| `resultsCache.statefulStrategy` | object | Stateful results-cache strategy | `{"type":"RollingUpdate"}` |
| `resultsCache.terminationGracePeriodSeconds` | int | Grace period to allow the results-cache to shutdown before it is killed | `60` |
| `resultsCache.timeout` | string | Memcached operation timeout | `"500ms"` |
| `resultsCache.tolerations` | list | Tolerations for results-cache pods | `[]` |
| `resultsCache.topologySpreadConstraints` | list | topologySpreadConstraints allows to customize the default topologySpreadConstraints. This can be either a single dict as shown below or a slice of topologySpreadConstraints. labelSelector is taken from the constraint itself (if it exists) or is generated by the chart using the same selectors as for services. | `[]` |
| `resultsCache.writebackBuffer` | int | Max number of objects to use for cache write back | `500000` |
| `resultsCache.writebackParallelism` | int | Number of parallel threads for cache write back | `1` |
| `resultsCache.writebackSizeLimit` | string | Max memory to use for cache write back | `"500MB"` |

## rollout_operator

Setting for the Grafana Rollout Operator https://github.com/grafana/helm-charts/tree/main/charts/rollout-operator

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `rollout_operator.podSecurityContext` | object | podSecurityContext is the pod security context for the rollout operator. When installing on OpenShift, override podSecurityContext settings with rollout_operator: podSecurityContext: fsGroup: null runAsGroup: null runAsUser: null | `{"fsGroup":10001,"runAsGroup":10001,"runAsNonRoot":true,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `rollout_operator.webhooks.enabled` | bool | Enable the rollout-operator webhooks. See https://github.com/grafana/rollout-operator/#webhooks. Note that the webhooks require custom resource definitions. If upgrading, manually apply the files in the `crds` directory. | `true` |
| `rollout_operator.webhooks.failurePolicy` | string | Validating and mutating webhook failure policy. `Ignore` or `Fail`. | `"Ignore"` |
| `rollout_operator.webhooks.timeoutSeconds` | int | Timeout in seconds for the prepare-downscale mutating webhook. Must be between 1 and 30. | `10` |

## route

Gateway API routes for direct routing to Loki services, bypassing the nginx gateway. Use either this or the top-level ingress, but not both at once. Each entry in the map creates a separate HTTPRoute resource. The key "main" produces a route named after the loki fullname; any other key appends the key as a suffix. This allows defining multiple routes, e.g. one HTTPRoute and one GRPCRoute.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `route.main.additionalRules` | list | Additional rules to prepend before the generated path-routing rules. Each entry is rendered into spec.rules as written (templating is supported), so any HTTPRoute/GRPCRoute rule shape is accepted — including filter-only rules with no backendRefs (e.g. RequestRedirect, URLRewrite). | `[]` |
| `route.main.annotations` | object | Annotations for the route | `{}` |
| `route.main.apiVersion` | string | Gateway API version to use. Auto-detected from cluster capabilities if empty. | `""` |
| `route.main.backendPort` | string | Backend port override for all generated rules. When unset, GRPCRoute automatically uses loki.server.grpc_listen_port (default 9095) and all other kinds use loki.server.http_listen_port (default 3100). | `nil` |
| `route.main.enabled` | bool | Specifies whether this Gateway API route should be created | `false` |
| `route.main.hostnames` | list | Hostnames for the route | `[]` |
| `route.main.kind` | string | Kind of the route resource. Valid options are GRPCRoute, HTTPRoute, TCPRoute, TLSRoute, UDPRoute | `"HTTPRoute"` |
| `route.main.labels` | object | Labels for the route | `{}` |
| `route.main.parentRefs` | list | Parent references for the route (required for Gateway API) | `[]` |
| `route.main.paths.compactor` | list | Paths that are exposed by Loki Compactor. If deployment mode is Distributed, the requests are forwarded to the service. If deployment mode is SimpleScalable, the requests are forwarded to k8s service. If deployment mode is SingleBinary, the requests are forwarded to the central/single k8s service. | `["/loki/api/v1/delete"]` |
| `route.main.paths.distributor` | list | Paths that are exposed by Loki Distributor. If deployment mode is Distributed, the requests are forwarded to the service: `{{"loki.distributorFullname"}}`. If deployment mode is SimpleScalable, the requests are forwarded to write k8s service: `{{"loki.writeFullname"}}`. If deployment mode is SingleBinary, the requests are forwarded to the central/single k8s service: `{{"loki.fullname"}}` | `["/api/prom/push","/loki/api/v1/push","/otlp/v1/logs"]` |
| `route.main.paths.queryFrontend` | list | Paths that are exposed by Loki Query Frontend. If deployment mode is Distributed, the requests are forwarded to the service. If deployment mode is SimpleScalable, the requests are forwarded to read k8s service. If deployment mode is SingleBinary, the requests are forwarded to the central/single k8s service` | `["/api/prom/query","/api/prom/label","/api/prom/series","/api/prom/tail","/loki/api/v1/query","/loki/api/v1/query_range","/loki/api/v1/tail","/loki/api/v1/label","/loki/api/v1/labels","/loki/api/v1/series","/loki/api/v1/index/stats","/loki/api/v1/index/volume","/loki/api/v1/index/volume_range","/loki/api/v1/format_query","/loki/api/v1/detected_field","/loki/api/v1/detected_fields","/loki/api/v1/detected_labels","/loki/api/v1/patterns","/ui"]` |
| `route.main.paths.ruler` | list | Paths that are exposed by Loki Ruler. If deployment mode is Distributed, the requests are forwarded to the service. If deployment mode is SimpleScalable, the requests are forwarded to k8s service. If deployment mode is SingleBinary, the requests are forwarded to the central/single k8s service. | `["/api/prom/rules","/api/prom/api/v1/rules","/api/prom/api/v1/alerts","/loki/api/v1/rules","/prometheus/api/v1/rules","/prometheus/api/v1/alerts"]` |

## ruler

Configuration for the ruler

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `ruler.affinity` | object | Affinity for ruler pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `ruler.annotations` | object | Annotations for ruler | `{}` |
| `ruler.appProtocol` | object | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" @deprecated -- This option is deprecated in favor of `ruler.service.appProtocol` | `{"grpc":""}` |
| `ruler.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `ruler.directories` | object | Directories containing rules files. If used, you must also configure `loki.rulerConfig.storage` to use local storage. | `{}` |
| `ruler.dnsConfig` | object | DNSConfig for ruler pods | `{}` |
| `ruler.enabled` | bool | The ruler component is optional and can be disabled if desired. | `true` |
| `ruler.extraArgs` | list | Additional CLI args for the ruler | `[]` |
| `ruler.extraContainers` | list | Containers to add to the ruler pods | `[]` |
| `ruler.extraEnv` | list | Environment variables to add to the ruler pods | `[]` |
| `ruler.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the ruler pods | `[]` |
| `ruler.extraVolumeMounts` | list | Volume mounts to add to the ruler pods | `[]` |
| `ruler.extraVolumes` | list | Volumes to add to the ruler pods | `[]` |
| `ruler.hostAliases` | list | hostAliases to add | `[]` |
| `ruler.hostUsers` | string | Use the host's user namespace in the ruler | `nil` |
| `ruler.image.registry` | string | The Docker registry for the ruler image. Overrides `loki.image.registry` | `nil` |
| `ruler.image.repository` | string | Docker image repository for the ruler image. Overrides `loki.image.repository` | `nil` |
| `ruler.image.tag` | string | Docker image tag for the ruler image. Overrides `loki.image.tag` | `nil` |
| `ruler.initContainers` | list | Init containers to add to the ruler pods | `[]` |
| `ruler.kind` | string | Kind of ruler deployment. StatefulSet and Deployment are supported. | `"StatefulSet"` |
| `ruler.labels` | object | Labels for ruler | `{}` |
| `ruler.maxUnavailable` | string | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `nil` |
| `ruler.nodeSelector` | object | Node selector for ruler pods | `{}` |
| `ruler.persistence.accessModes` | list | Set access modes on the PersistentVolumeClaim | `["ReadWriteOnce"]` |
| `ruler.persistence.annotations` | object | Annotations for ruler PVCs | `{}` |
| `ruler.persistence.enabled` | bool | Enable creating PVCs which is required when using recording rules | `false` |
| `ruler.persistence.labels` | object | Labels for ruler PVCs | `{}` |
| `ruler.persistence.size` | string | Size of persistent disk | `"10Gi"` |
| `ruler.persistence.storageClass` | string | Storage class to be used. If defined, storageClassName: &lt;storageClass&gt;. If set to "-", storageClassName: "", which disables dynamic provisioning. If empty or set to null, no storageClassName spec is set, choosing the default provisioner (gp2 on AWS, standard on GKE, AWS, and OpenStack). | `nil` |
| `ruler.persistence.volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `ruler.podAnnotations` | object | Annotations for ruler pods | `{}` |
| `ruler.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `ruler.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `ruler.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `ruler.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `ruler.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `ruler.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `ruler.podLabels` | object | Labels for ruler pods | `{}` |
| `ruler.priorityClassName` | string | The name of the PriorityClass for ruler pods | `nil` |
| `ruler.replicas` | int | Number of replicas for the ruler. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `0` |
| `ruler.resizePolicy` | list | Container resize policy for the ruler Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `ruler.resources` | object | Resource requests and limits for the ruler | `{}` |
| `ruler.service.annotations` | object | Annotations for ruler Service | `{}` |
| `ruler.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `ruler.service.ipFamilies` | list | ipFamilies for ruler Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `ruler.service.ipFamilyPolicy` | string | ipFamilyPolicy for ruler Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `ruler.service.labels` | object | Additional labels for ruler Service | `{}` |
| `ruler.service.sessionAffinity` | string | Session affinity for ruler Service | `""` |
| `ruler.service.sessionAffinityConfig` | object | Session affinity config for ruler Service | `{}` |
| `ruler.service.trafficDistribution` | string | trafficDistribution for ruler Service | `""` |
| `ruler.service.type` | string | Service Type for ruler Service | `"ClusterIP"` |
| `ruler.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `ruler.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `ruler.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `ruler.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `ruler.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `ruler.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `ruler.serviceAnnotations` | object | Annotations for ruler service @deprecated -- This option is deprecated in favor of `ruler.service.annotations` | `{}` |
| `ruler.serviceLabels` | object | Labels for ruler service @deprecated -- This option is deprecated in favor of `ruler.service.labels` | `{}` |
| `ruler.sidecar` | bool | Whether to enable the rules sidecar | `false` |
| `ruler.statefulSetRecreateJob` | object | EXPERIMENTAL: Enable creating a Job to recreate the ruler StatefulSet when the ruler configuration is changed. # Only applicable if `ruler.kind` is StatefulSet. Useful for PVC size changes, which require recreating the StatefulSet. | `{"enabled":false}` |
| `ruler.terminationGracePeriodSeconds` | int | Grace period to allow the ruler to shutdown before it is killed | `300` |
| `ruler.topologySpreadConstraints` | list | Topology Spread Constraints for ruler pods The value will be passed through tpl. | `[]` |

## serviceAccount

Configuration for the Kubernetes ServiceAccount used by Loki components.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `true` |
| `serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `serviceAccount.labels` | object | Labels for the service account | `{}` |
| `serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |

## sidecar

Configuration for the k8s-sidecar container that watches for ConfigMaps and Secrets (such as rules and dashboards) and loads them into Loki.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `sidecar.disableX509StrictVerification` | bool | Set to true to disable strict x509 verification for kube api calls. | `false` |
| `sidecar.enableUniqueFilenames` | bool | Ensure that rule files aren't conflicting and being overwritten by prefixing their name with the namespace they are defined in. | `false` |
| `sidecar.image.pullPolicy` | string | Docker image pull policy | `"IfNotPresent"` |
| `sidecar.image.registry` | string |  | `"docker.io"` |
| `sidecar.image.repository` | string | The Docker registry and image for the k8s sidecar | `"kiwigrid/k8s-sidecar"` |
| `sidecar.image.sha` | string | Docker image sha. If empty, no sha will be used | `""` |
| `sidecar.image.tag` | string | Docker image tag | `"2.11.2"` |
| `sidecar.livenessProbe` | object | Liveness probe definition. | `{"enabled":true,"failureThreshold":3,"httpGet":{"path":"/healthz","port":"http-sidecar"},"initialDelaySeconds":30,"periodSeconds":30,"successThreshold":1,"timeoutSeconds":1}` |
| `sidecar.readinessProbe` | object | Readiness probe definition. | `{"enabled":true,"failureThreshold":3,"httpGet":{"path":"/healthz","port":"http-sidecar"},"initialDelaySeconds":3,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":1}` |
| `sidecar.resizePolicy` | list | Container resize policy for the sidecar Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `sidecar.resources` | object | Resource requests and limits for the sidecar | `{}` |
| `sidecar.rules.enabled` | bool | Whether or not to create a sidecar to ingest rule from specific ConfigMaps and/or Secrets. | `true` |
| `sidecar.rules.folder` | string | Folder into which the rules will be placed. | `"/rules"` |
| `sidecar.rules.folderAnnotation` | string | The annotation overwriting the folder value. The annotation value can be either an absolute or a relative path. Relative paths will be relative to FOLDER. Useful for multi-tenancy setups. | `nil` |
| `sidecar.rules.healthPort` | int | Health Server port of the sidecar container. | `8080` |
| `sidecar.rules.label` | string | Label that the configmaps/secrets with rules will be marked with. | `"loki_rule"` |
| `sidecar.rules.labelValue` | string | Label value that the configmaps/secrets with rules will be set to. | `""` |
| `sidecar.rules.logLevel` | string | Log level of the sidecar container. | `"INFO"` |
| `sidecar.rules.resource` | string | Search in configmap, secret, or both. | `"both"` |
| `sidecar.rules.script` | string | Absolute path to the shell script to execute after a configmap or secret has been reloaded. | `nil` |
| `sidecar.rules.searchNamespace` | string | Comma separated list of namespaces. If specified, the sidecar will search for config-maps/secrets inside these namespaces. Otherwise the namespace in which the sidecar is running will be used. It's also possible to specify 'ALL' to search in all namespaces. | `nil` |
| `sidecar.rules.watchClientTimeout` | int | WatchClientTimeout: is a client-side timeout, configuring your local socket. If you have a network outage dropping all packets with no RST/FIN, this is how long your client waits before realizing & dropping the connection. Defaults to 66sec. | `60` |
| `sidecar.rules.watchMethod` | string | Method to use to detect ConfigMap changes. With WATCH the sidecar will do a WATCH request, with SLEEP it will list all ConfigMaps, then sleep for 60 seconds. | `"WATCH"` |
| `sidecar.rules.watchServerTimeout` | int | WatchServerTimeout: request to the server, asking it to cleanly close the connection after that. defaults to 60sec; much higher values like 3600 seconds (1h) are feasible for non-Azure K8S. | `60` |
| `sidecar.securityContext` | object | The SecurityContext for the sidecar. | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `sidecar.skipTlsVerify` | bool | Set to true to skip tls verification for kube api calls. | `false` |
| `sidecar.startupProbe` | object | Startup probe definition. | `{"enabled":false}` |

## singleBinary

Configuration for the single binary node(s) used in Monolithic deployment mode, where all Loki components run in a single process.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `singleBinary.affinity` | object | Affinity for single binary pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `singleBinary.annotations` | object | Annotations for single binary StatefulSet | `{}` |
| `singleBinary.autoscaling.behavior.enabled` | bool | Enable autoscaling behaviours | `false` |
| `singleBinary.autoscaling.behavior.scaleDown` | object | define scale down policies, must conform to HPAScalingRules | `{}` |
| `singleBinary.autoscaling.behavior.scaleUp` | object | define scale up policies, must conform to HPAScalingRules | `{}` |
| `singleBinary.autoscaling.customMetrics` | list | Allows one to define custom metrics using the HPA/v2 schema (for example, Pods, Object or External metrics) | `[]` |
| `singleBinary.autoscaling.enabled` | bool | Enable autoscaling | `false` |
| `singleBinary.autoscaling.maxReplicas` | int | Maximum autoscaling replicas for the single binary | `3` |
| `singleBinary.autoscaling.minReplicas` | int | Minimum autoscaling replicas for the single binary | `1` |
| `singleBinary.autoscaling.targetCPUUtilizationPercentage` | int | Target CPU utilisation percentage for the single binary | `60` |
| `singleBinary.autoscaling.targetMemoryUtilizationPercentage` | string | Target memory utilisation percentage for the single binary | `nil` |
| `singleBinary.dnsConfig` | object |  | `{}` |
| `singleBinary.enabled` | bool | Enable the single binary component. Requires `loki.deploymentMode` to be set to `SingleBinary`. | `true` |
| `singleBinary.extraArgs` | list | Labels for single binary service | `[]` |
| `singleBinary.extraContainers` | list | Extra containers to add to the single binary loki pod | `[]` |
| `singleBinary.extraEnv` | list | Environment variables to add to the single binary pods | `[]` |
| `singleBinary.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the single binary pods | `[]` |
| `singleBinary.extraVolumeMounts` | list | Volume mounts to add to the single binary pods | `[]` |
| `singleBinary.extraVolumes` | list | Volumes to add to the single binary pods | `[]` |
| `singleBinary.hostUsers` | string | Use the host's user namespace in the single binary pods | `nil` |
| `singleBinary.image.registry` | string | The Docker registry for the single binary image. Overrides `loki.image.registry` | `nil` |
| `singleBinary.image.repository` | string | Docker image repository for the single binary image. Overrides `loki.image.repository` | `nil` |
| `singleBinary.image.tag` | string | Docker image tag for the single binary image. Overrides `loki.image.tag` | `nil` |
| `singleBinary.initContainers` | list | Init containers to add to the single binary pods | `[]` |
| `singleBinary.kedaAutoscaling.behavior` | object | Behavior configuration for KEDA ScaledObject (horizontalPodAutoscalerConfig.behavior). Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#advanced | `{}` |
| `singleBinary.kedaAutoscaling.cooldownPeriod` | string | Period to wait after the last trigger reported active before scaling the resource back to 0 (seconds). Defaults to defaults.kedaAutoscaling.cooldownPeriod if not set. | `nil` |
| `singleBinary.kedaAutoscaling.enabled` | bool | Enable KEDA autoscaling for the single binary. Mutually exclusive with autoscaling.enabled | `false` |
| `singleBinary.kedaAutoscaling.fallback` | object | Fallback configuration for KEDA ScaledObject. Applied when the trigger metric cannot be retrieved. Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#fallback | `{}` |
| `singleBinary.kedaAutoscaling.maxReplicas` | int | Maximum replicas for KEDA autoscaling | `3` |
| `singleBinary.kedaAutoscaling.minReplicas` | int | Minimum replicas for KEDA autoscaling | `1` |
| `singleBinary.kedaAutoscaling.pollingInterval` | string | Interval (seconds) to poll each trigger. Defaults to defaults.kedaAutoscaling.pollingInterval if not set. | `nil` |
| `singleBinary.kedaAutoscaling.triggers` | list | KEDA triggers for the single binary. Ref: https://keda.sh/docs/latest/scalers/ | `[]` |
| `singleBinary.kind` | string | Kind of read deployment. StatefulSet and Deployment are supported. | `"StatefulSet"` |
| `singleBinary.labels` | object | Additional labels for single binary StatefulSet | `{}` |
| `singleBinary.nodeSelector` | object | Node selector for single binary pods | `{}` |
| `singleBinary.persistence.accessModes` | list | Set access modes on the PersistentVolumeClaim | `["ReadWriteOnce"]` |
| `singleBinary.persistence.annotations` | object | Annotations for volume claim | `{}` |
| `singleBinary.persistence.dataVolumeParameters` | object | Parameters used for the `storage` volume when persistence.enabled is false. Allows replacing the default emptyDir volume with a custom volume definition. For example, to use an existing PVC: dataVolumeParameters: persistentVolumeClaim: claimName: my-existing-pvc | `{}` |
| `singleBinary.persistence.enableStatefulSetAutoDeletePVC` | bool | Enable StatefulSetAutoDeletePVC feature | `false` |
| `singleBinary.persistence.enabled` | bool | Enable persistent disk | `true` |
| `singleBinary.persistence.labels` | object | Labels for volume claim | `{}` |
| `singleBinary.persistence.selector` | string | Selector for persistent disk | `nil` |
| `singleBinary.persistence.size` | string | Size of persistent disk | `"10Gi"` |
| `singleBinary.persistence.storageClass` | string | Storage class to be used. If defined, storageClassName: &lt;storageClass&gt;. If set to "-", storageClassName: "", which disables dynamic provisioning. If empty or set to null, no storageClassName spec is set, choosing the default provisioner (gp2 on AWS, standard on GKE, AWS, and OpenStack). | `nil` |
| `singleBinary.persistence.whenDeleted` | string | What to do with the volumes when the StatefulSet is deleted. | `"Delete"` |
| `singleBinary.persistence.whenScaled` | string | What to do with the volume when the StatefulSet is scaled down. | `"Delete"` |
| `singleBinary.podAnnotations` | object | Annotations for single binary pods | `{}` |
| `singleBinary.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `singleBinary.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `singleBinary.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `singleBinary.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `singleBinary.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `singleBinary.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `singleBinary.podLabels` | object | Additional labels for each `single binary` pod | `{}` |
| `singleBinary.podManagementPolicy` | string | The default is to deploy all pods in parallel. | `"Parallel"` |
| `singleBinary.priorityClassName` | string | The name of the PriorityClass for single binary pods | `nil` |
| `singleBinary.replicas` | int | Number of replicas for the single binary. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `0` |
| `singleBinary.resizePolicy` | list | Container resize policy for the single binary Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `singleBinary.resources` | object | Resource requests and limits for the single binary | `{}` |
| `singleBinary.selectorLabels` | object | Additional selector labels for each `single binary` pod | `{}` |
| `singleBinary.service.annotations` | object | Annotations for single binary Service | `{}` |
| `singleBinary.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `singleBinary.service.ipFamilies` | list | ipFamilies for single binary Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `singleBinary.service.ipFamilyPolicy` | string | ipFamilyPolicy for single binary Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `singleBinary.service.labels` | object | Additional labels for single binary Service | `{}` |
| `singleBinary.service.sessionAffinity` | string | Session affinity for single binary Service | `""` |
| `singleBinary.service.sessionAffinityConfig` | object | Session affinity config for single binary Service | `{}` |
| `singleBinary.service.trafficDistribution` | string | trafficDistribution single binary Service | `""` |
| `singleBinary.service.type` | string | Service Type for single binary Service | `"ClusterIP"` |
| `singleBinary.sidecar` | bool | Whether to enable the rules sidecar | `true` |
| `singleBinary.statefulSetRecreateJob` | object | EXPERIMENTAL: Enable creating a Job to recreate the write StatefulSet when the compactor configuration is changed. # Only applicable if `compactor.kind` is StatefulSet. Useful for PVC size changes, which require recreating the StatefulSet. | `{"enabled":false}` |
| `singleBinary.strategy` | object | UpdateStrategy or Strategy for single binary. | `{"rollingUpdate":{"partition":0}}` |
| `singleBinary.targetModule` | string | Comma-separated list of Loki modules to load for the single binary | `"all"` |
| `singleBinary.terminationGracePeriodSeconds` | int | Grace period to allow the single binary to shutdown before it is killed | `30` |
| `singleBinary.tolerations` | list | Tolerations for single binary pods | `[]` |
| `singleBinary.topologySpreadConstraints` | list | Topology Spread Constraints for single binary pods The value will be passed through tpl. | `[]` |

## tableManager

DEPRECATED Configuration for the table-manager. The table-manager is only necessary when using a deprecated index type such as Cassandra, Bigtable, or DynamoDB, it has not been necessary since loki introduced self- contained index types like 'boltdb-shipper' and 'tsdb'. This will be removed in a future helm chart.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `tableManager.affinity` | object | Affinity for table-manager pods. The value will be passed through tpl. | `Hard node and anti-affinity` |
| `tableManager.annotations` | object | Annotations for table-manager deployment | `{}` |
| `tableManager.command` | string | Command to execute instead of defined in Docker image | `nil` |
| `tableManager.dnsConfig` | object | DNS config table-manager pods | `{}` |
| `tableManager.enabled` | bool | Specifies whether the table-manager should be enabled | `false` |
| `tableManager.extraArgs` | list | Additional CLI args for the table-manager | `[]` |
| `tableManager.extraContainers` | list | Containers to add to the table-manager pods | `[]` |
| `tableManager.extraEnv` | list | Environment variables to add to the table-manager pods | `[]` |
| `tableManager.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the table-manager pods | `[]` |
| `tableManager.extraVolumeMounts` | list | Volume mounts to add to the table-manager pods | `[]` |
| `tableManager.extraVolumes` | list | Volumes to add to the table-manager pods | `[]` |
| `tableManager.hostUsers` | string | Use the host's user namespace in table-manager pods | `nil` |
| `tableManager.image.registry` | string | The Docker registry for the table-manager image. Overrides `loki.image.registry` | `nil` |
| `tableManager.image.repository` | string | Docker image repository for the table-manager image. Overrides `loki.image.repository` | `nil` |
| `tableManager.image.tag` | string | Docker image tag for the table-manager image. Overrides `loki.image.tag` | `nil` |
| `tableManager.nodeSelector` | object | Node selector for table-manager pods | `{}` |
| `tableManager.podAnnotations` | object | Annotations for table-manager pods | `{}` |
| `tableManager.podLabels` | object | Labels for table-manager pods | `{}` |
| `tableManager.priorityClassName` | string | The name of the PriorityClass for table-manager pods | `nil` |
| `tableManager.resizePolicy` | list | Container resize policy for the table-manager Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `tableManager.resources` | object | Resource requests and limits for the table-manager | `{}` |
| `tableManager.retention_deletes_enabled` | bool | Enable deletes by retention | `false` |
| `tableManager.retention_period` | int | Set retention period | `0` |
| `tableManager.service.annotations` | object | Annotations for table-manager Service | `{}` |
| `tableManager.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `tableManager.service.ipFamilies` | list | ipFamilies for table-manager Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `tableManager.service.ipFamilyPolicy` | string | ipFamilyPolicy for table-manager Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `tableManager.service.labels` | object | Additional labels for table-manager Service | `{}` |
| `tableManager.service.sessionAffinity` | string | Session affinity for table-manager Service | `""` |
| `tableManager.service.sessionAffinityConfig` | object | Session affinity config for table-manager Service | `{}` |
| `tableManager.service.trafficDistribution` | string | trafficDistribution for table-manager Service | `""` |
| `tableManager.service.type` | string | Service Type for table-manager Service | `"ClusterIP"` |
| `tableManager.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `tableManager.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `tableManager.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `tableManager.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `tableManager.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `tableManager.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `tableManager.terminationGracePeriodSeconds` | int | Grace period to allow the table-manager to shutdown before it is killed | `30` |
| `tableManager.tolerations` | list | Tolerations for table-manager pods | `[]` |

## test

Section for configuring optional Helm test

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `test.annotations` | object | Additional annotations for test pods | `{}` |
| `test.canaryServiceAddress` | string | Used to directly query the metrics endpoint of the canary for testing, this approach avoids needing prometheus for testing. This in a newer approach to using prometheusAddress such that tests do not have a dependency on prometheus | `"http://{{ include \"loki.resourceName\" (dict \"ctx\" $ \"component\" \"canary\") }}.{{ include \"loki.namespace\" $ }}.svc.{{ .Values.global.clusterDomain }}:3500/metrics"` |
| `test.hostUsers` | string | Use the host's user namespace in test pods | `nil` |
| `test.image.digest` | string | Overrides the image tag with an image digest | `nil` |
| `test.image.pullPolicy` | string | Docker image pull policy | `"IfNotPresent"` |
| `test.image.registry` | string | The Docker registry | `"docker.io"` |
| `test.image.repository` | string | Docker image repository | `"grafana/loki-helm-test"` |
| `test.image.tag` | string | Overrides the image tag whose default is the chart's appVersion | `"latest"` |
| `test.labels` | object | Additional labels for the test pods | `{}` |
| `test.prometheusAddress` | string | Address of the prometheus server to query for the test. This overrides any value set for canaryServiceAddress. This is kept for backward compatibility and may be removed in future releases. Previous value was 'http://prometheus:9090' | `""` |
| `test.timeout` | string | Number of times to retry the test before failing | `"1m"` |

## write

Configuration for the write pod(s) in SimpleScalable mode, which run the distributor and ingester components.

| Key | Type | Description | Default |
| --- | --- | --- | --- |
| `write.affinity` | object | Affinity for write pods. The value will be passed through tpl. | `Hard node anti-affinity` |
| `write.annotations` | object | Annotations for write StatefulSet | `{}` |
| `write.autoscaling.behavior.scaleUp` | object | see https://github.com/grafana/loki/blob/main/docs/sources/operations/storage/wal.md#how-to-scale-updown for scaledown details | `{"policies":[{"periodSeconds":900,"type":"Pods","value":1}]}` |
| `write.autoscaling.enabled` | bool | Enable autoscaling for the write. | `false` |
| `write.autoscaling.maxReplicas` | int | Maximum autoscaling replicas for the write. | `6` |
| `write.autoscaling.minReplicas` | int | Minimum autoscaling replicas for the write. | `2` |
| `write.autoscaling.targetCPUUtilizationPercentage` | int | Target CPU utilisation percentage for the write. | `60` |
| `write.autoscaling.targetMemoryUtilizationPercentage` | string | Target memory utilization percentage for the write. | `nil` |
| `write.dnsConfig` | object | DNS config for write pods | `{}` |
| `write.enabled` | bool | Enable the component. Requires `loki.deploymentMode` to be set to `SimpleScalable`. | `true` |
| `write.extraArgs` | list | Additional CLI args for the write | `[]` |
| `write.extraContainers` | list | Containers to add to the write pods | `[]` |
| `write.extraEnv` | list | Environment variables to add to the write pods | `[]` |
| `write.extraEnvFrom` | list | Environment variables from secrets or configmaps to add to the write pods | `[]` |
| `write.extraVolumeClaimTemplates` | list | volumeClaimTemplates to add to StatefulSet | `[]` |
| `write.extraVolumeMounts` | list | Volume mounts to add to the write pods | `[]` |
| `write.extraVolumes` | list | Volumes to add to the write pods | `[]` |
| `write.fullnameOverride` | string | Override for the write fullname. By default, this is generated using the fullname template with "-backend" | `"{{ include \"loki.name\" . }}-write"` |
| `write.hostUsers` | string | Use the host's user namespace in the write pods. | `nil` |
| `write.image.registry` | string | The Docker registry for the write image. Overrides `loki.image.registry` | `nil` |
| `write.image.repository` | string | Docker image repository for the write image. Overrides `loki.image.repository` | `nil` |
| `write.image.tag` | string | Docker image tag for the write image. Overrides `loki.image.tag` | `nil` |
| `write.initContainers` | list | Init containers to add to the write pods | `[]` |
| `write.kedaAutoscaling.behavior` | object | Behavior configuration for KEDA ScaledObject (horizontalPodAutoscalerConfig.behavior). Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#advanced | `{}` |
| `write.kedaAutoscaling.cooldownPeriod` | string | Period to wait after the last trigger reported active before scaling the resource back to 0 (seconds). Defaults to defaults.kedaAutoscaling.cooldownPeriod if not set. | `nil` |
| `write.kedaAutoscaling.enabled` | bool | Enable KEDA autoscaling for the write. Mutually exclusive with autoscaling.enabled | `false` |
| `write.kedaAutoscaling.fallback` | object | Fallback configuration for KEDA ScaledObject. Applied when the trigger metric cannot be retrieved. Ref: https://keda.sh/docs/latest/reference/scaledobject-spec/#fallback | `{}` |
| `write.kedaAutoscaling.maxReplicas` | int | Maximum replicas for KEDA autoscaling | `6` |
| `write.kedaAutoscaling.minReplicas` | int | Minimum replicas for KEDA autoscaling | `2` |
| `write.kedaAutoscaling.pollingInterval` | string | Interval (seconds) to poll each trigger. Defaults to defaults.kedaAutoscaling.pollingInterval if not set. | `nil` |
| `write.kedaAutoscaling.triggers` | list | KEDA triggers for the write. Ref: https://keda.sh/docs/latest/scalers/ | `[]` |
| `write.kind` | string | Kind of write workload. StatefulSet and Deployment are supported. | `"StatefulSet"` |
| `write.labels` | object | Additional labels for write StatefulSet | `{}` |
| `write.lifecycle` | object | Lifecycle for the write container | `{}` |
| `write.maxUnavailable` | int | Pod Disruption Budget maxUnavailable @deprecated -- This is deprecated in favor of `podDisruptionBudget.maxUnavailable`. | `1` |
| `write.nodeSelector` | object | Node selector for write pods | `{}` |
| `write.persistence.accessModes` | list | Set access modes on the PersistentVolumeClaim | `["ReadWriteOnce"]` |
| `write.persistence.annotations` | object | Annotations for volume claim | `{}` |
| `write.persistence.dataVolumeParameters` | object | Parameters used for the `data` volume when volumeClaimEnabled if false | `{"emptyDir":{}}` |
| `write.persistence.enableStatefulSetAutoDeletePVC` | bool | Enable StatefulSetAutoDeletePVC feature | `false` |
| `write.persistence.labels` | object | Labels for volume claim | `{}` |
| `write.persistence.selector` | string | Selector for persistent disk | `nil` |
| `write.persistence.size` | string | Size of persistent disk | `"10Gi"` |
| `write.persistence.storageClass` | string | Storage class to be used. If defined, storageClassName: &lt;storageClass&gt;. If set to "-", storageClassName: "", which disables dynamic provisioning. If empty or set to null, no storageClassName spec is set, choosing the default provisioner (gp2 on AWS, standard on GKE, AWS, and OpenStack). | `nil` |
| `write.persistence.volumeAttributesClassName` | string | Volume attributes class name to be used. If empty or set to null, no volumeAttributesClassName spec is set. Requires Kubernetes 1.31 | `nil` |
| `write.persistence.volumeClaimsEnabled` | bool | Enable volume claims in pod spec | `true` |
| `write.persistence.whenDeleted` | string | What to do with the volumes when the StatefulSet is deleted. | `"Delete"` |
| `write.persistence.whenScaled` | string | What to do with the volume when the StatefulSet is scaled down. | `"Delete"` |
| `write.podAnnotations` | object | Annotations for write pods | `{}` |
| `write.podDisruptionBudget.annotations` | object | Annotations for Pod Disruption Budget | `{}` |
| `write.podDisruptionBudget.enabled` | bool | Enable Pod Disruption Budget | `true` |
| `write.podDisruptionBudget.labels` | object | Labels for Pod Disruption Budget | `{}` |
| `write.podDisruptionBudget.maxUnavailable` | string | Pod Disruption Budget maxUnavailable | `nil` |
| `write.podDisruptionBudget.minAvailable` | string | Pod Disruption Budget minAvailable | `nil` |
| `write.podDisruptionBudget.unhealthyPodEvictionPolicy` | string | Pod Disruption Budget unhealthyPodEvictionPolicy | `nil` |
| `write.podLabels` | object | Additional labels for each `write` pod | `{}` |
| `write.podManagementPolicy` | string | The default is to deploy all pods in parallel. | `"Parallel"` |
| `write.priorityClassName` | string | The name of the PriorityClass for write pods | `nil` |
| `write.replicas` | int | Number of replicas for the write. Set to null to omit spec.replicas from the workload, allowing an external controller (e.g. HPA, KEDA, rollout-operator) to manage replica counts without Helm resetting them. | `3` |
| `write.resizePolicy` | list | Container resize policy for the write Example: resizePolicy: - resourceName: cpu restartPolicy: NotRequired - resourceName: memory restartPolicy: RestartContainer | `[]` |
| `write.resources` | object | Resource requests and limits for the write | `{}` |
| `write.selectorLabels` | object | Additional selector labels for each `write` pod | `{}` |
| `write.service.annotations` | object | Annotations for write Service | `{}` |
| `write.service.appProtocol.grpc` | string | Set the optional grpc service protocol. Ex: "grpc", "http2" or "https" | `""` |
| `write.service.ipFamilies` | list | ipFamilies for write Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `[]` |
| `write.service.ipFamilyPolicy` | string | ipFamilyPolicy for write Service Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services | `""` |
| `write.service.labels` | object | Additional labels for write Service | `{}` |
| `write.service.sessionAffinity` | string | Session affinity for write Service | `""` |
| `write.service.sessionAffinityConfig` | object | Session affinity config for write Service | `{}` |
| `write.service.trafficDistribution` | string | trafficDistribution for write Service | `""` |
| `write.service.type` | string | Service Type for write Service | `"ClusterIP"` |
| `write.serviceAccount.annotations` | object | Annotations for the service account | `{}` |
| `write.serviceAccount.automountServiceAccountToken` | bool | Set this toggle to false to opt out of automounting API credentials for the service account | `true` |
| `write.serviceAccount.create` | bool | Specifies whether a ServiceAccount should be created | `false` |
| `write.serviceAccount.imagePullSecrets` | list | Image pull secrets for the service account | `[]` |
| `write.serviceAccount.labels` | object | Labels for the service account | `{}` |
| `write.serviceAccount.name` | string | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template | `nil` |
| `write.statefulSetRecreateJob` | object | EXPERIMENTAL: Enable creating a Job to recreate the write StatefulSet when the compactor configuration is changed. # Only applicable if `compactor.kind` is StatefulSet. Useful for PVC size changes, which require recreating the StatefulSet. | `{"enabled":false}` |
| `write.strategy` | object | UpdateStrategy or Strategy for the write. | `{"rollingUpdate":{"partition":0}}` |
| `write.terminationGracePeriodSeconds` | int | Grace period to allow the write to shutdown before it is killed. Especially for the ingester, this must be increased. It must be long enough so writes can be gracefully shutdown flushing/transferring all data and to successfully leave the member ring on shutdown. | `300` |
| `write.tolerations` | list |  | `[]` |
| `write.topologySpreadConstraints` | list | Topology Spread Constraints for write pods The value will be passed through tpl. | `[]` |

# Deploy the Loki Helm chart on AWS

This guide shows how to deploy a minimally viable Loki in **microservice** mode on AWS using the Helm chart. To run through this guide, we expect you to have the necessary tools and permissions to deploy resources on AWS, such as:

- Full access to EKS (Amazon Elastic Kubernetes Service)
- Full access to S3 (Amazon Simple Storage Service)
- Sufficient permissions to create IAM (Identity Access Management) roles and policies

There are two methods for authenticating and connecting Loki to AWS S3. We will guide you through the recommended method of granting access via an IAM role.

{{< youtube id="5lXmWmofqwM" >}}

## Considerations

{{< admonition type="caution" >}}
This guide was accurate at the time it was last updated on **31st October, 2024**.  As cloud providers frequently update their services and offerings, as a best practice, you should refer to the [AWS S3 documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html) before creating your buckets and assigning roles.
{{< /admonition >}}

- **IAM Role:** The IAM role created in this guide is a basic role that allows Loki to read and write to the S3 bucket. You may wish to add more granular permissions based on your requirements.

- **Authentication:** Grafana Loki comes with a basic authentication layer. The Loki gateway (NGINX) is exposed to the internet using basic authentication in this example. NGINX can also be replaced with other open-source reverse proxies. Refer to [Authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/) for more information.

- **Retention:** The retention period is set to 28 days in the `values.yaml` file. You may wish to adjust this based on your requirements.

- **Costs:** Running Loki on AWS will incur costs. Make sure to monitor your usage and costs to avoid any unexpected bills. In this guide we have used a simple EKS cluster with 3 nodes and m5.xlarge instances. You may wish to adjust the instance types and number of nodes based on your workload.

## Prerequisites

- Helm 3 or above. Refer to [Installing Helm](https://helm.sh/docs/intro/install/). This should be installed on your local machine.
- Kubernetes 1.25 or later.
- A running Kubernetes cluster on AWS. A simple way to get started is by using EKSctl. Refer to [Getting started with EKSctl](https://eksctl.io/).
- Kubectl installed on your local machine. Refer to [Install and Set Up kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/).
- (Optional) AWS CLI installed on your local machine. Refer to [Installing the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html). This is required if you plan to use EKSctl to create the EKS cluster and modify the IAM roles and policies locally.
  
### EKS Minimum Requirements

{{< admonition type="caution" >}}
These EKS requirements are the minimum specification needed to deploy Loki using this guide. You may wish to adjust plugins and instance types based on your AWS environment and workload. **If you choose to do so, we cannot guarantee that this sample configuration will still meet your needs.**

In this guide, we deploy Loki using `m5.xlarge` instances. This is a instance type that should work for most scenarios. However, you can modify the instance types and count based on your specific needs.
{{< /admonition >}}

The minimum requirements for deploying Loki on EKS are:

- Kubernetes version `1.30` or above.
- `3` nodes for the EKS cluster.
- Instance type depends on your workload. A good starting point for a production cluster is `m7i.2xlarge`.

Here is the EKSctl cluster configuration file used in this guide:

```yaml
# A simple example of ClusterConfig object:
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: <INSERT_CLUSTER_NAME>
  region: <INSERT_REGION_FOR_CLUSTER>
  version: "1.31"

iam:
  withOIDC: true

addons:
  - name: aws-ebs-csi-driver

managedNodeGroups:
  - name: loki-workers
    instanceType: m7i.2xlarge
    desiredCapacity: 3
    minSize: 2
    maxSize: 3
    amiFamily: AmazonLinux2023
    iam:
      withAddonPolicies:
        ebs: true
    volumeSize: 80
    volumeType: gp3
    ebsOptimized: true
```

The following plugins must also be installed within the EKS cluster:

- **Amazon EBS CSI Driver**: Enables Kubernetes to dynamically provision and manage EBS volumes as persistent storage for applications. We use this to provision the node volumes for Loki.
- **CoreDNS**: Provides internal DNS service for Kubernetes clusters, ensuring that services and pods can communicate with each other using DNS names.
- **kube-proxy**: Maintains network rules on nodes, enabling communication between pods and services within the cluster.

You must also install an **OIDC (OpenID Connect) provider** on the EKS cluster. This is required for the IAM roles and policies to work correctly. If you are using EKSctl, you can install the OIDC provider using the following command:

{{< admonition type="tip" >}}
If you used the above EKSctl configuration file to create the cluster, you do not need to run this command. The OIDC provider is automatically installed.
{{< /admonition >}}

```bash
eksctl utils associate-iam-oidc-provider --cluster loki --approve
```

## Create S3 buckets

{{< admonition type="warning" >}}
 **DO NOT** use the default bucket names;  `chunk`, `ruler` and `admin`. Choose a **unique** name for each bucket. For more information see the following [security update](https://grafana.com/blog/2024/06/27/grafana-security-update-grafana-loki-and-unintended-data-write-attempts-to-amazon-s3-buckets/).
{{< /admonition >}}

Before deploying Loki, you need to create two S3 buckets; one to store logs (chunks), the second to store alert rules. You can create the bucket using the AWS Management Console or the AWS CLI. The bucket name must be globally unique.

{{< admonition type="note" >}}
GEL customers will require a third bucket to store the admin data. This bucket is not required for OSS users.
{{< /admonition >}}

```bash
aws s3api create-bucket --bucket  <YOUR CHUNK BUCKET NAME e.g. `loki-aws-dev-chunks`> --region <S3 region your account is on, e.g. `eu-west-2`> --create-bucket-configuration LocationConstraint=<S3 region your account is on, e.g. `eu-west-2`> \
aws s3api create-bucket --bucket  <YOUR RULER BUCKET NAME e.g. `loki-aws-dev-ruler`> --region <S3 REGION your account is on, e.g. `eu-west-2`> --create-bucket-configuration LocationConstraint=<S3 REGION your account is on, e.g. `eu-west-2`>
```

Make sure to replace the `region` and `bucket` name with your desired values. We will revisit the bucket policy later in this guide.

## Defining IAM roles and policies

The recommended method for connecting Loki to AWS S3 is to use an IAM role. This method is more secure than using access keys and secret keys which are directly stored in the Loki configuration. The role and policy can be created using the AWS CLI or the AWS Management Console. The below steps show how to create the role and policy using the AWS CLI.

1. Create a new directory and navigate to it. Make sure to create the files in this directory. All commands in this guide assume you are in this directory.

1. Create a `loki-s3-policy.json` file with the following content:

    ```json
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "LokiStorage",
                "Effect": "Allow",
                "Action": [
                    "s3:ListBucket",
                    "s3:PutObject",
                    "s3:GetObject",
                    "s3:DeleteObject"
                ],
                "Resource": [
                    "arn:aws:s3:::< CHUNK BUCKET NAME >",
                    "arn:aws:s3:::< CHUNK BUCKET NAME >/*",
                    "arn:aws:s3:::< RULER BUCKET NAME >",
                    "arn:aws:s3:::< RULER BUCKET NAME >/*"
                ]
            }
        ]
    }
    ```

    **Make sure to replace the placeholders with the names of the buckets you created earlier.**

1. Create the IAM policy using the AWS CLI:

    ```bash
    aws iam create-policy --policy-name LokiS3AccessPolicy --policy-document file://loki-s3-policy.json
    ```

1. Create a trust policy document named `trust-policy.json` with the following content:

    ```json
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Federated": "arn:aws:iam::< ACCOUNT ID >:oidc-provider/oidc.eks.<INSERT REGION>.amazonaws.com/id/< OIDC ID >"
                },
                "Action": "sts:AssumeRoleWithWebIdentity",
                "Condition": {
                    "StringEquals": {
                        "oidc.eks.<INSERT REGION>.amazonaws.com/id/< OIDC ID >:sub": "system:serviceaccount:loki:loki",
                        "oidc.eks.<INSERT REGION>.amazonaws.com/id/< OIDC ID >:aud": "sts.amazonaws.com"
                    }
                }
            }
        ]
    }
    ```

   **Make sure to replace the placeholders with your AWS account ID, region, and the OIDC ID (you can find this in the EKS cluster configuration).**

1. Create the IAM role using the AWS CLI:

    ```bash
    aws iam create-role --role-name LokiServiceAccountRole --assume-role-policy-document file://trust-policy.json
    ```

1. Attach the policy to the role:

    ```bash
    aws iam attach-role-policy --role-name LokiServiceAccountRole --policy-arn arn:aws:iam::<Account ID>:policy/LokiS3AccessPolicy
    ```

    **Make sure to replace the placeholder with your AWS account ID.**

## Deploying the Helm chart

Before we can deploy the Loki Helm chart, we need to add the Grafana Community chart repository to Helm. This repository contains the Loki Helm chart.

1. Add the Grafana Community chart repository to Helm:

    ```bash
    helm repo add grafana-community https://grafana-community.github.io/helm-charts
    ```

1. Update the chart repository:

    ```bash
    helm repo update
    ```

1. Create a new namespace for Loki:

    ```bash
    kubectl create namespace loki
    ```

### Loki Basic Authentication

Loki by default does not come with any authentication. Since we will be deploying Loki to AWS and exposing the gateway to the internet, we recommend adding at least basic authentication. In this guide we will give Loki a username and password:

1. To start we will need create a `.htpasswd` file with the username and password. You can use the `htpasswd` command to create the file:

   {{< admonition type="tip" >}}
    If you don't have the `htpasswd` command installed, you can install it using `brew` or `apt-get` or `yum` depending on your OS.
   {{< /admonition >}}

    ```bash
    htpasswd -c .htpasswd <username>
    ```

    This will create a file called `.htpasswd` with the username you provided. You will be prompted to enter a password.

1. Create a Kubernetes secret with the `.htpasswd` file:

    ```bash
    kubectl create secret generic loki-basic-auth --from-file=.htpasswd -n loki
    ```

    This will create a secret called `loki-basic-auth` in the `loki` namespace. We will reference this secret in the Loki Helm chart configuration.
  
1. Create a `canary-basic-auth` secret for the canary:

    ```bash
    kubectl create secret generic canary-basic-auth \
      --from-literal=username=<USERNAME> \
      --from-literal=password=<PASSWORD> \
      -n loki
    ```

    We create a literal secret with the username and password for Loki canary to authenticate with the Loki gateway.
    **Make sure to replace the placeholders with your desired username and password.**

### Loki Helm chart configuration

Create a `values.yaml` file choosing the configuration options that best suit your requirements. Below there is an example of `values.yaml` files for the Loki Helm chart in [microservices](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/#microservices-mode) mode.

```yaml
loki:
  schemaConfig:
    configs:
      - from: "2024-04-01"
        store: tsdb
        object_store: s3
        schema: v13
        index:
          prefix: loki_index_
          period: 24h
  storage_config:
    aws:
      region: <S3 BUCKET REGION> # for example, eu-west-2  
      bucketnames: <CHUNK BUCKET NAME> # Your actual S3 bucket name, for example, loki-aws-dev-chunks
      s3forcepathstyle: false
  ingester:
    chunk_encoding: snappy
  pattern_ingester:
    enabled: true
  limits_config:
    allow_structured_metadata: true
    volume_enabled: true
    retention_period: 672h # 28 days retention
  compactor:
    retention_enabled: true
    delete_request_store: s3
  rulerConfig:
    enable_api: true
    alertmanager_url: http://prom:9093

  querier:
    max_concurrent: 4

  storage:
    type: s3
    bucketNames:
      chunks: "<CHUNK BUCKET NAME>" # Your actual S3 bucket name (loki-aws-dev-chunks)
      ruler: "<RULER BUCKET NAME>" # Your actual S3 bucket name (loki-aws-dev-ruler)
      # admin: "<Insert s3 bucket name>" # Your actual S3 bucket name (loki-aws-dev-admin) - GEL customers only
    s3:
      region: <S3 BUCKET REGION> # eu-west-2
      #insecure: false
      # s3forcepathstyle: false

serviceAccount:
  create: true
  annotations:
    "eks.amazonaws.com/role-arn": "arn:aws:iam::<Account ID>:role/LokiServiceAccountRole" # The service role you created

deploymentMode: Distributed

ingester:
  replicas: 3
  zoneAwareReplication:
    enabled: false

querier:
  replicas: 3
  maxUnavailable: 2

queryFrontend:
  replicas: 2
  maxUnavailable: 1

queryScheduler:
  replicas: 2

distributor:
  replicas: 3
  maxUnavailable: 2
compactor:
  replicas: 1

indexGateway:
  replicas: 2
  maxUnavailable: 1

patternIngester:
  enabled: true
  replicas: 1

ruler:
  replicas: 1
  maxUnavailable: 1


# This exposes the Loki gateway so it can be written to and queried externaly
gateway:
  service:
    type: LoadBalancer
  basicAuth:
    enabled: true
    existingSecret: loki-basic-auth

# Since we are using basic auth, we need to pass the username and password to the canary
lokiCanary:
  extraArgs:
    - -pass=$(LOKI_PASS)
    - -user=$(LOKI_USER)
  extraEnv:
    - name: LOKI_PASS
      valueFrom:
        secretKeyRef:
          name: canary-basic-auth
          key: password
    - name: LOKI_USER
      valueFrom:
        secretKeyRef:
          name: canary-basic-auth
          key: username

# Disable minio storage
minio:
  enabled: false

backend:
  replicas: 0
read:
  replicas: 0
write:
  replicas: 0

singleBinary:
  replicas: 0
```

{{< admonition type="caution" >}}
Make sure to replace the placeholders with your actual values.
{{< /admonition >}}

It is critical to define a valid `values.yaml` file for the Loki deployment. To remove the risk of misconfiguration, let's break down the configuration options to keep in mind when deploying to AWS:

- **Loki Config vs. Values Config:**
  - The `values.yaml` file contains a section called `loki`, which contains a direct representation of the Loki configuration file.
  - This section defines the Loki configuration, including the schema, storage, and querier configuration.
  - The key configuration for chunks is `loki.storage` (type, `bucketNames`, and provider settings such as `loki.storage.s3.region`), which the chart maps into Loki's `common.storage` section. This tells Loki where to store the chunks.
  - The `rulerConfig` section defines ruler-specific settings such as `enable_api` and `alertmanager_url`. Ruler object storage is derived automatically from `loki.storage` and `loki.storage.bucketNames.ruler`.
  - For the full Loki configuration, refer to the [Loki Configuration](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/) documentation.

- **Storage:**
  - Defines where the Helm chart stores data.
  - Set the type to `s3` since we are using Amazon S3.
  - Configure the bucket names for the chunks and ruler to match the buckets created earlier.
  - The `s3` section specifies the region of the bucket.

- **Service Account:**
  - The `serviceAccount` section is used to define the IAM role for the Loki service account.
  - This is where the IAM role created earlier is linked.

- **Zone-Aware Replication:**
  - The Helm chart enables zone-aware ingester replication by default, which creates three ingester StatefulSets (zone-a, zone-b, zone-c) for faster rollouts.
  - In this guide, zone-aware replication is explicitly disabled (`ingester.zoneAwareReplication.enabled: false`) for simplicity. For production deployments, consider enabling it to improve rollout resilience.

- **Gateway:**
  - Defines how the Loki gateway will be exposed.
  - We are using a `LoadBalancer` service type in this configuration.

### Deploy Loki

Now that you have created the `values.yaml` file, you can deploy Loki using the Helm chart.

1. Deploy using the newly created `values.yaml` file:

    ```bash
    helm install --values values.yaml loki grafana-community/loki -n loki --create-namespace
    ```

    **It is important to create a namespace called `loki` as our trust policy is set to allow the IAM role to be used by the `loki` service account in the `loki` namespace. This is configurable but make sure to update your service account.**

1. Verify the deployment:

    ```bash
    kubectl get pods -n loki
    ```

    You should see the Loki pods running.

    ```console
    NAME                                    READY   STATUS    RESTARTS   AGE
    loki-canary-crqpg                       1/1     Running   0          10m
    loki-canary-hm26p                       1/1     Running   0          10m
    loki-canary-v9wv9                       1/1     Running   0          10m
    loki-chunks-cache-0                     2/2     Running   0          10m
    loki-compactor-0                        1/1     Running   0          10m
    loki-distributor-78ccdcc9b4-9wlhl       1/1     Running   0          10m
    loki-distributor-78ccdcc9b4-km6j2       1/1     Running   0          10m
    loki-distributor-78ccdcc9b4-ptwrb       1/1     Running   0          10m
    loki-gateway-5f97f78755-hm6mx           1/1     Running   0          10m
    loki-index-gateway-0                    1/1     Running   0          10m
    loki-index-gateway-1                    1/1     Running   0          10m
    loki-ingester-0                         1/1     Running   0          10m
    loki-ingester-1                         1/1     Running   0          10m
    loki-ingester-2                         1/1     Running   0          10m
    loki-pattern-ingester-0                 1/1     Running   0          10m
    loki-querier-89d4ff448-4vr9b            1/1     Running   0          10m
    loki-querier-89d4ff448-7nvrf            1/1     Running   0          10m
    loki-querier-89d4ff448-q89kh            1/1     Running   0          10m
    loki-query-frontend-678899db5-n5wc4     1/1     Running   0          10m
    loki-query-frontend-678899db5-tf69b     1/1     Running   0          10m
    loki-query-scheduler-7d666bf759-9xqb5   1/1     Running   0          10m
    loki-query-scheduler-7d666bf759-kpb5q   1/1     Running   0          10m
    loki-results-cache-0                    2/2     Running   0          10m
    loki-ruler-0                            1/1     Running   0          10m
    ```
  
### Find the Loki Gateway Service

The Loki Gateway service is a LoadBalancer service that exposes the Loki gateway to the internet. This is where you will write logs to and query logs from. By default NGINX is used as the gateway.

{{< admonition type="caution" >}}
The Loki Gateway service is exposed to the internet. We provide basic authentication using a username and password in this tutorial. Refer to the [Authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/) documentation for more information.
{{< /admonition >}}

To find the Loki Gateway service, run the following command:

```bash
kubectl get svc -n loki
```

You should see the Loki Gateway service with an external IP address. This is the address you will use to write to and query Loki.

```console
  NAME                             TYPE           CLUSTER-IP       EXTERNAL-IP                                                               PORT(S)              AGE
loki-gateway                     LoadBalancer   10.100.201.74    12345678975675456-1433434453245433545656563.eu-west-2.elb.amazonaws.com   80:30707/TCP         46m
```

Congratulations! You have successfully deployed Loki on AWS using the Helm chart. Before we finish, let's test the deployment.

## Testing Your Loki Deployment

k6 is one of the fastest ways to test your Loki deployment. This will allow you to both write and query logs to Loki. To get started with k6, follow the steps below:

1. Install k6 with the Loki extension on your local machine. Refer to [Installing k6 and the xk6-loki extension](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/k6/).

2. Create a `aws-test.js` file with the following content:

   ```javascript
    import {sleep, check} from 'k6';
    import loki from 'k6/x/loki';

    /**
    * URL used for push and query requests
    * Path is automatically appended by the client
    * @constant {string}
    */

    const username = '<USERNAME>';
    const password = '<PASSWORD>';
    const external_ip = '<EXTERNAL-IP>';

    const credentials = `${username}:${password}`;

    const BASE_URL = `http://${credentials}@${external_ip}`;

    /**
    * Helper constant for byte values
    * @constant {number}
    */
    const KB = 1024;

    /**
    * Helper constant for byte values
    * @constant {number}
    */
    const MB = KB * KB;

    /**
    * Instantiate config and Loki client
    */

    const conf = new loki.Config(BASE_URL);
    const client = new loki.Client(conf);

    /**
    * Define test scenario
    */
    export const options = {
      vus: 10,
      iterations: 10,
    };

    export default () => {
      // Push request with 10 streams and uncompressed logs between 800KB and 2MB
      var res = client.pushParameterized(10, 800 * KB, 2 * MB);
      // Check for successful write
      check(res, { 'successful write': (res) => res.status == 204 });

      // Pick a random log format from label pool
      let format = randomChoice(conf.labels["format"]);

      // Execute instant query with limit 1
      res = client.instantQuery(`count_over_time({format="${format}"}[1m])`, 1)
      // Check for successful read
      check(res, { 'successful instant query': (res) => res.status == 200 });

      // Execute range query over last 5m and limit 1000
      res = client.rangeQuery(`{format="${format}"}`, "5m", 1000)
      // Check for successful read
      check(res, { 'successful range query': (res) => res.status == 200 });

      // Wait before next iteration
      sleep(1);
    }

    /**
    * Helper function to get random item from array
    */
    function randomChoice(items) {
      return items[Math.floor(Math.random() * items.length)];
    }
   ```

   **Replace `<EXTERNAL-IP>` with the external IP address of the Loki Gateway service.**

   This script will write logs to Loki and query logs from Loki. It will write logs in a random format between 800KB and 2MB and query logs in a random format over the last 5 minutes.
  
3. Run the test:

    ```bash
    ./k6 run aws-test.js
    ```

    This will run the test and output the results. You should see the test writing logs to Loki and querying logs from Loki.

Now that you have successfully deployed Loki in microservices mode on AWS, you may wish to explore the following:

- [Sending data to Loki](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/)
- [Querying Loki](https://grafana.com/docs/loki/<LOKI_VERSION>/query/)
- [Manage](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/)

# Deploy the Loki Helm chart on Azure

This guide shows how to deploy a minimally viable Loki in **microservice** mode on Azure using the Helm chart. In order to successfully complete this guide, you must have the necessary tools and permissions to deploy resources on Azure, such as:

- Full access to AKS (Azure Kubernetes Service)
- Full access to Azure Blob Storage
- Sufficient permissions to create federated credentials and roles in Azure AD (Active Directory)

There are four primary ways to authenticate Loki with Azure:

- Hard coding a connection string - this is the simplest method but is not recommended for production environments.
- Service principal
- Managed identity
- Federated token

In this guide, we will use the federated token method to deploy Loki on Azure. This method is more secure than hard coding a connection string and is more suitable for production environments.

## Considerations

{{< admonition type="caution" >}}
This guide was accurate at the time it was last updated on **6th of February, 2026**.  As cloud providers frequently update their services and offerings, as a best practice, you should refer to the [Azure documentation](https://learn.microsoft.com/en-us/azure/) before creating your storage account and assigning roles.
{{< /admonition >}}

- **AD Role:** In this tutorial we will create a role in Azure Active Directory (Azure AD) to allow Loki to read and write from Azure Blob Storage. This role will be assigned to the Loki service account. You may want to adjust the permissions based on your requirements.

- **Authentication:** Grafana Loki comes with a basic authentication layer. The Loki gateway (NGINX) is exposed to the internet using basic authentication in this example. NGINX can also be replaced with other open-source reverse proxies. Refer to [Authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/) for more information.

- **Retention:** The retention period is set to 28 days in the `values.yaml` file. You may wish to adjust this based on your requirements.

- **Costs:** Running Loki on Azure will incur costs. Make sure to monitor your usage and costs to avoid any unexpected bills. In this guide we have used a simple AKS cluster with 3 nodes and `Standard_E2ds_v5` instances. You may wish to adjust the instance types and number of nodes based on your workload.

## Prerequisites

- Helm 3 or above. Refer to [Installing Helm](https://helm.sh/docs/intro/install/). This should be installed on your local machine.
- Kubernetes 1.25 or later.
- Kubectl installed on your local machine. Refer to [Install and Set Up kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/).
- Azure CLI installed on your local machine. Refer to [Installing the Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli). This is a requirement for following this guide as all resources will be created using the Azure CLI.
  
### AKS minimum requirements

Before creating an AKS cluster in Azure you need to create a resource group. You can create a resource group using the Azure CLI:

```bash
az group create --name <INSERT-NAME> -location <INSERT-AZURE-REGION>
```

{{< admonition type="caution" >}}
These AKS requirements are the minimum specification needed to deploy Loki using this guide. You may wish to adjust the instance types based on your Azure environment and workload. **If you choose to do so, we cannot guarantee that this sample configuration will still meet your needs.**

In this guide, we deploy Loki using `Standard_E2ds_v5` instances. This is to make sure we remain within the free tier limits for Azure. Which allows us to deploy up to 10 vCPUs within a region. We recommend for large production workloads to scale these nodes up to `Standard_D4_v5`.
{{< /admonition >}}

The minimum requirements for deploying Loki on AKS are:

- Kubernetes version `1.30` or above.
- `3` nodes for the AKS cluster.
- Instance type depends on your workload. A good starting point for a production cluster in the free tier is `Standard_E2ds_v5` instances and for large production workloads `Standard_D4_v5` instances.

Here is how to create an AKS cluster with the minimum requirements:

```bash
az aks create \
  --resource-group <MY_RESOURCE_GROUP_NAME> \
  --name <MY_AKS_CLUSTER_NAME> \
  --node-count 3 \
  --node-vm-size Standard_E2ds_v5 \
  --generate-ssh-keys \
  --enable-workload-identity \
  --enable-oidc-issuer
```

Note in the above command we have enabled workload identity and OIDC issuer. This is required for the Loki service account to authenticate with Azure AD. If you have already created an AKS cluster, you can enable these features using the following command:

```bash
az aks update \
  --resource-group <MY_RESOURCE_GROUP_NAME> \
  --name <MY_AKS_CLUSTER_NAME> \
  --enable-workload-identity \
  --enable-oidc-issuer
```

The Azure CLI also lets you bind the AKS cluster to kubectl. You can do this by running the following command:

```bash
az aks get-credentials --resource-group <MY_RESOURCE_GROUP_NAME> --name <MY_AKS_CLUSTER_NAME>
```

## Configuring Azure Blob Storage

{{< admonition type="tip" >}}
 Consider using unique bucket names rather than:  `chunk`, `ruler`, and `admin`. Although Azure Blog Storage is not directly affected by this [security update](https://grafana.com/blog/2024/06/27/grafana-security-update-grafana-loki-and-unintended-data-write-attempts-to-amazon-s3-buckets/) it is a best practice to use unique container names for buckets.
{{< /admonition >}}

Before deploying Loki, you need to create two Azure storage containers; one to store logs (chunks), the second to store alert rules. You can create the containers using the Azure CLI. Containers must exist inside a storage account.

{{< admonition type="note" >}}
GEL customers will require a third container to store the admin data. This container is not required for OSS users.
{{< /admonition >}}

1. Create a storage account:

    ```bash
    az storage account create \
    --name <NAME> \
    --location <REGION> \
    --sku Standard_ZRS \
    --encryption-services blob \
    --resource-group <MY_RESOURCE_GROUP_NAME>
    ```

    Replace the placeholders with your desired values.

1. Create the containers for chunks and ruler:

    ```bash
    az storage container create --account-name <STORAGE-ACCOUNT-NAME> --name <CHUNK-BUCKET-NAME> --auth-mode login && \
    az storage container create --account-name <STORAGE-ACCOUNT-NAME> --name <RULER-BUCKET-NAME> --auth-mode login
    ```

    Make sure `--account-name` matches the account you just created

With the storage account and containers created, you can now proceed to creating the Azure AD role and federated credentials.

## Creating the Azure AD role and federated credentials

The recommended way to authenticate Loki with Azure Blob Storage is to use federated credentials. This method is more secure than hard coding a connection string directly into the Loki configuration. In this next section, we will create an Azure AD role and federated credentials for Loki to allow it to read and write from Azure Blob Storage:

1. Locate the OpenID Connect (OIDC)  issuer URL:

    ```bash
    az aks show \
    --resource-group <MY_RESOURCE_GROUP_NAME> \
    --name <MY_AKS_CLUSTER_NAME> \
    --query "oidcIssuerProfile.issuerUrl" \
    -o tsv
    ```

    This command will return the OIDC issuer URL. You will need this URL to create the federated credentials.

1. Generate a `credentials.json` file with the following content:

    ```json
    {
        "name": "LokiFederatedIdentity",
        "issuer": "<OIDC-ISSUER-URL>",
        "subject": "system:serviceaccount:loki:loki",
        "description": "Federated identity for Loki accessing Azure resources",
        "audiences": [
          "api://AzureADTokenExchange"
        ]
    }
    ```

    Replace `<OIDC-ISSUER-URL>` with the OIDC issuer URL you found in the previous step.

1. Make sure you to save the `credentials.json` file before continuing.

1. Next generate an Azure directory `app`. We will use this to assign our federated credentials to:

   ```bash
    az ad app create \
    --display-name loki \
    --query appId \
    -o tsv
   ```

    This will return the app ID. Save this for later use. If you need to find the app ID later you can run the following command:

    ```bash
    az ad app list --display-name loki --query "[].appId" -o tsv
    ```

1. The app requires a service principal to authenticate with Azure AD. Create a service principal for the app:

    ```bash
    az ad sp create --id <APP-ID>
    ```

    Replace `<APP-ID>` with the app ID you generated in the previous step.

1. Next assign the federated credentials to the app:

    ```bash
    az ad app federated-credential create \
      --id <APP-ID> \
      --parameters credentials.json 
    ```

    Replace `<APP-ID>` with the app ID you generated in the previous step.

1. Lastly add a role assignment to the app:

    ```bash
    az role assignment create \
      --role "Storage Blob Data Contributor" \
      --assignee <APP-ID> \
      --scope /subscriptions/<SUBSCRIPTION-ID>/resourceGroups/<RESOURCE-GROUP>/providers/Microsoft.Storage/storageAccounts/<STORAGE-ACCOUNT-NAME>
    ```

    Replace the placeholders with your actual values.

Now that you have created the Azure AD role and federated credentials, you can proceed to deploying Loki using the Helm chart.

## Deploying the Helm chart

The following steps require the use of `helm` and `kubectl`. Make sure you have run the `az` command to bind your AKS cluster to `kubectl`:

```bash
az aks get-credentials --resource-group <MY_RESOURCE_GROUP_NAME> --name <MY_AKS_CLUSTER_NAME>
```

Before we can deploy the Loki Helm chart, we need to add the Grafana Community chart repository to Helm. This repository contains the Loki Helm chart.

1. Add the Grafana Community chart repository to Helm:

    ```bash
    helm repo add grafana-community https://grafana-community.github.io/helm-charts
    ```

1. Update the chart repository:

    ```bash
    helm repo update
    ```

1. Create a new namespace for Loki:

    ```bash
    kubectl create namespace loki
    ```

### Loki basic authentication

Loki by default does not come with any authentication. Since we will be deploying Loki to Azure and exposing the gateway to the internet, we recommend adding at least basic authentication. In this guide we will give Loki a username and password:

1. To start we will need create a `.htpasswd` file with the username and password. You can use the `htpasswd` command to create the file:

   {{< admonition type="tip" >}}
    If you don't have the `htpasswd` command installed, you can install it using `brew` or `apt-get` or `yum` depending on your OS.
   {{< /admonition >}}

    ```bash
    htpasswd -c .htpasswd <username>
    ```

    This will create a file called `.htpasswd` with the username you provided. You will be prompted to enter a password.

1. Create a Kubernetes secret with the `.htpasswd` file:

    ```bash
    kubectl create secret generic loki-basic-auth --from-file=.htpasswd -n loki
    ```

    This will create a secret called `loki-basic-auth` in the `loki` namespace. We will reference this secret in the Loki Helm chart configuration.
  
1. Create a `canary-basic-auth` secret for the canary:

    ```bash
    kubectl create secret generic canary-basic-auth \
      --from-literal=username=<USERNAME> \
      --from-literal=password=<PASSWORD> \
      -n loki
    ```

    We create a literal secret with the username and password for Loki canary to authenticate with the Loki gateway. Make sure to replace the placeholders with your desired username and password.

### Loki Helm chart configuration

Create a `values.yaml` file choosing the configuration options that best suit your requirements. Below there is an example of `values.yaml` files for the Loki Helm chart in [microservices](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/#microservices-mode) mode.

```yaml
loki:
  podLabels:
    "azure.workload.identity/use": "true" # Add this label to the Loki pods to enable workload identity
  schemaConfig:
    configs:
      - from: "2024-04-01"
        store: tsdb
        object_store: azure
        schema: v13
        index:
          prefix: loki_index_
          period: 24h
  storage:
    type: azure
    bucketNames:
      chunks: "<CHUNK-CONTAINER-NAME>" # Your actual Azure Blob Storage container name (loki-azure-dev-chunks)
      ruler: "<RULER-CONTAINER-NAME>" # Your actual Azure Blob Storage container name (loki-azure-dev-ruler)
      # admin: "admin-loki-devrel" # Your actual Azure Blob Storage container name (loki-azure-dev-admin)
    azure:
      accountName: <INSERT-STORAGE-ACCOUNT-NAME>
      useFederatedToken: true # Use federated token for authentication
  ingester:
    chunk_encoding: snappy
  pattern_ingester:
    enabled: true
  limits_config:
    allow_structured_metadata: true
    volume_enabled: true
    retention_period: 672h # 28 days retention
  compactor:
    retention_enabled: true
    delete_request_store: azure
  rulerConfig:
    enable_api: true
    alertmanager_url: http://prom:9093

  querier:
    max_concurrent: 4

# Define the Azure workload identity
serviceAccount:
  name: loki
  annotations:
    "azure.workload.identity/client-id": "<APP-ID>" # The app ID of the Azure AD app

deploymentMode: Distributed

ingester:
  replicas: 3
  zoneAwareReplication:
    enabled: false

querier:
  replicas: 3
  maxUnavailable: 2

queryFrontend:
  replicas: 2
  maxUnavailable: 1

queryScheduler:
  replicas: 2

distributor:
  replicas: 3
  maxUnavailable: 2

compactor:
  replicas: 1

indexGateway:
  replicas: 2
  maxUnavailable: 1

patternIngester:
  enabled: true
  replicas: 1

ruler:
  replicas: 1
  maxUnavailable: 1


# This exposes the Loki gateway so it can be written to and queried externaly
gateway:
  service:
    type: LoadBalancer
  basicAuth: 
    enabled: true
    existingSecret: loki-basic-auth

# Since we are using basic auth, we need to pass the username and password to the canary
lokiCanary:
  extraArgs:
    - -pass=$(LOKI_PASS)
    - -user=$(LOKI_USER)
  extraEnv:
    - name: LOKI_PASS
      valueFrom:
        secretKeyRef:
          name: canary-basic-auth
          key: password
    - name: LOKI_USER
      valueFrom:
        secretKeyRef:
          name: canary-basic-auth
          key: username

# Disable minio storage
minio:
  enabled: false

backend:
  replicas: 0
read:
  replicas: 0
write:
  replicas: 0

singleBinary:
  replicas: 0
```

{{< admonition type="caution" >}}
Make sure to replace the placeholders with your actual values.
{{< /admonition >}}

It is critical to define a valid `values.yaml` file for the Loki deployment. To remove the risk of misconfiguration, let's break down the configuration options to keep in mind when deploying to Azure:

- **Loki Config vs. Values Config:**
  - The `values.yaml` file contains a section called `loki`, which contains a direct representation of the Loki configuration file.
  - This section defines the Loki configuration, including the schema, storage, and querier configuration.
  - The key configuration for chunks is `loki.storage` (type, `bucketNames`, and provider settings such as the Azure account name), which the chart maps into Loki's `common.storage` section. This tells Loki where to store the chunks.
  - The `rulerConfig` section defines ruler-specific settings such as `enable_api` and `alertmanager_url`. Ruler object storage is derived automatically from `loki.storage` and `loki.storage.bucketNames.ruler`.
  - For the full Loki configuration, refer to the [Loki Configuration](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/) documentation.

- **Storage:**
  - Defines where the Helm chart stores data.
  - Set the type to `azure` since we are using Azure Blob Storage.
  - Configure the container names for the chunks and ruler to match the containers created earlier.
  - The `azure` section specifies the storage account name and also sets `useFederatedToken` to `true`. This tells Loki to use federated credentials for authentication.

- **Service Account:**
  - The `serviceAccount` section is used to define the federated workload identity Loki will use to authenticate with Azure AD.
  - We set the `azure.workload.identity/client-id` annotation to the app ID of the Azure AD app.

- **Zone-Aware Replication:**
  - The Helm chart enables zone-aware ingester replication by default, which creates three ingester StatefulSets (zone-a, zone-b, zone-c) for faster rollouts.
  - In this guide, zone-aware replication is explicitly disabled (`ingester.zoneAwareReplication.enabled: false`) for simplicity. For production deployments, consider enabling it to improve rollout resilience.

- **Gateway:**
  - Defines how the Loki gateway will be exposed.
  - We are using a `LoadBalancer` service type in this configuration.

### Deploy Loki

Now that you have created the `values.yaml` file, you can deploy Loki using the Helm chart.

1. Deploy using the newly created `values.yaml` file:

    ```bash
    helm install --values values.yaml loki grafana-community/loki -n loki --create-namespace
    ```

    It is important to create a namespace called `loki` as our federated credentials were generated with the  value `system:serviceaccount:loki:loki`. This translates to the `loki` service account in the `loki` namespace. This is configurable but make sure to update the federated credentials file first.

1. Verify the deployment:

    ```bash
    kubectl get pods -n loki
    ```

    You should see the Loki pods running.

    ```console
    NAME                                    READY   STATUS    RESTARTS   AGE
    loki-canary-crqpg                       1/1     Running   0          10m
    loki-canary-hm26p                       1/1     Running   0          10m
    loki-canary-v9wv9                       1/1     Running   0          10m
    loki-chunks-cache-0                     2/2     Running   0          10m
    loki-compactor-0                        1/1     Running   0          10m
    loki-distributor-78ccdcc9b4-9wlhl       1/1     Running   0          10m
    loki-distributor-78ccdcc9b4-km6j2       1/1     Running   0          10m
    loki-distributor-78ccdcc9b4-ptwrb       1/1     Running   0          10m
    loki-gateway-5f97f78755-hm6mx           1/1     Running   0          10m
    loki-index-gateway-0                    1/1     Running   0          10m
    loki-index-gateway-1                    1/1     Running   0          10m
    loki-ingester-0                         1/1     Running   0          10m
    loki-ingester-1                         1/1     Running   0          10m
    loki-ingester-2                         1/1     Running   0          10m
    loki-pattern-ingester-0                 1/1     Running   0          10m
    loki-querier-89d4ff448-4vr9b            1/1     Running   0          10m
    loki-querier-89d4ff448-7nvrf            1/1     Running   0          10m
    loki-querier-89d4ff448-q89kh            1/1     Running   0          10m
    loki-query-frontend-678899db5-n5wc4     1/1     Running   0          10m
    loki-query-frontend-678899db5-tf69b     1/1     Running   0          10m
    loki-query-scheduler-7d666bf759-9xqb5   1/1     Running   0          10m
    loki-query-scheduler-7d666bf759-kpb5q   1/1     Running   0          10m
    loki-results-cache-0                    2/2     Running   0          10m
    loki-ruler-0                            1/1     Running   0          10m
    ```
  
### Find the Loki gateway service

The Loki gateway service is a load balancer service that exposes the Loki gateway to the internet. This is where you will write logs to and query logs from. By default NGINX is used as the gateway.

{{< admonition type="caution" >}}
The Loki gateway service is exposed to the internet. We provide basic authentication using a username and password in this tutorial. Refer to the [Authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/) documentation for more information.
{{< /admonition >}}

To find the Loki gateway service, run the following command:

```bash
kubectl get svc -n loki
```

You should see the Loki gateway service with an external IP address. This is the address you will use to write to and query Loki.

```console
  NAME                             TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)              AGE
loki-gateway                     LoadBalancer   10.100.201.74     134.236.21.145  80:30707/TCP         46m
```

Congratulations! You have successfully deployed Loki on Azure using the Helm chart. Before we finish, let's test the deployment.

## Testing Your Loki Deployment

k6 is one of the fastest ways to test your Loki deployment. This will allow you to both write and query logs to Loki. To get started with k6, follow the steps below:

1. Install k6 with the Loki extension on your local machine. Refer to [Installing k6 and the xk6-loki extension](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/k6/).

2. Create a `azure-test.js` file with the following content:

   ```javascript
    import {sleep, check} from 'k6';
    import loki from 'k6/x/loki';

    /**
    * URL used for push and query requests
    * Path is automatically appended by the client
    * @constant {string}
    */

    const username = '<USERNAME>';
    const password = '<PASSWORD>';
    const external_ip = '<EXTERNAL-IP>';

    const credentials = `${username}:${password}`;

    const BASE_URL = `http://${credentials}@${external_ip}`;

    /**
    * Helper constant for byte values
    * @constant {number}
    */
    const KB = 1024;

    /**
    * Helper constant for byte values
    * @constant {number}
    */
    const MB = KB * KB;

    /**
    * Instantiate config and Loki client
    */

    const conf = new loki.Config(BASE_URL);
    const client = new loki.Client(conf);

    /**
    * Define test scenario
    */
    export const options = {
      vus: 10,
      iterations: 10,
    };

    export default () => {
      // Push request with 10 streams and uncompressed logs between 800KB and 2MB
      var res = client.pushParameterized(10, 800 * KB, 2 * MB);
      // Check for successful write
      check(res, { 'successful write': (res) => res.status == 204 });

      // Pick a random log format from label pool
      let format = randomChoice(conf.labels["format"]);

      // Execute instant query with limit 1
      res = client.instantQuery(`count_over_time({format="${format}"}[1m])`, 1)
      // Check for successful read
      check(res, { 'successful instant query': (res) => res.status == 200 });

      // Execute range query over last 5m and limit 1000
      res = client.rangeQuery(`{format="${format}"}`, "5m", 1000)
      // Check for successful read
      check(res, { 'successful range query': (res) => res.status == 200 });

      // Wait before next iteration
      sleep(1);
    }

    /**
    * Helper function to get random item from array
    */
    function randomChoice(items) {
      return items[Math.floor(Math.random() * items.length)];
    }
   ```

   Replace `<EXTERNAL-IP>` with the external IP address of the Loki Gateway service.

   This script will write logs to Loki and query logs from Loki. It will write logs in a random format between 800KB and 2MB and query logs in a random format over the last 5 minutes.
  
3. Run the test:

    ```bash
    ./k6 run azure-test.js
    ```

    This will run the test and output the results. You should see the test writing logs to Loki and querying logs from Loki.

Now that you have successfully deployed Loki in microservices mode on Microsoft Azure, you may wish to explore the following:

- [Sending data to Loki](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/)
- [Querying Loki](https://grafana.com/docs/loki/<LOKI_VERSION>/query/)
- [Manage Loki](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/)

# Deploy the Loki Helm chart on GCP

This guide shows how to deploy a minimally viable Loki in **microservices** mode on Google Cloud Platform (GCP) using the Helm chart. To run through this guide, we expect you to have the necessary tools and permissions to deploy resources on GCP, such as:

- Full access to Google Kubernetes Engine (GKE)
- Full access to Google Cloud Storage (GCS)
- Sufficient permissions to create Identity Access Management (IAM) roles and policies

There are two methods for authenticating and connecting Loki to GCP GCS. We will guide you through the recommended method of granting access via an IAM role: using Workload Identity Federation.

## Considerations

{{< admonition type="caution" >}}
This guide was accurate at the time it was last updated on **10th of June, 2025**.  As cloud providers frequently update their services and offerings, as a best practice, you should refer to the [GCP GCS documentation](https://cloud.google.com/storage/docs/introduction) before creating your buckets and assigning roles.
{{< /admonition >}}

- **IAM Role:** The IAM role created in this guide is a basic role that allows Loki to read and write to the GCS bucket. You may wish to add more granular permissions based on your requirements.

- **Authentication:** Grafana Loki comes with a basic authentication layer. The Loki gateway (NGINX) is exposed to the internet using basic authentication in this example. NGINX can also be replaced with other open-source reverse proxies. Refer to [Authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/) for more information.

- **Retention:** The retention period is set to 28 days in the `values.yaml` file. You may wish to adjust this based on your requirements.

- **Costs:** Running Loki on GCP will incur costs. Make sure to monitor your usage and costs to avoid any unexpected bills. In this guide we have used a simple GKE cluster with 3 nodes (n2-standard-8 instances). You may wish to adjust the instance types and number of nodes based on your workload.

## Prerequisites

- Helm 3 or above. Refer to [Installing Helm](https://helm.sh/docs/intro/install/). This should be installed on your local machine.
- Kubernetes 1.25 or later.
- A running Kubernetes cluster on GCP. Refer to [Create a cluster and deploy a workload in the Google Cloud console](https://cloud.google.com/kubernetes-engine/docs/quickstarts/create-cluster).
- Kubectl installed on your local machine. Refer to [Install and Set Up kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/).
- gcloud CLI installed on your local machine. Refer to [Install the Google Cloud CLI](https://cloud.google.com/sdk/docs/install-sdk). In this guide, we use gcloud CLI to create the GKE cluster and modify the IAM roles and policies locally.
  
### GKE Minimum Requirements

{{< admonition type="caution" >}}
These GKE requirements are the minimum specification needed to deploy Loki using this guide. You may wish to adjust plugins and instance types based on your GCP environment and workload. **If you choose to do so, we cannot guarantee that this sample configuration will still meet your needs.**

In this guide, we deploy Loki using `n2-standard-8` instances. This is a instance type that should work for most scenarios. However, you can modify the instance types and count based on your specific needs.
{{< /admonition >}}

The minimum requirements for deploying Loki on GKE are:

- Kubernetes version `1.30` or above.
- `3` nodes for the GKE cluster.
- Instance type depends on your workload. A good starting point for a production cluster is `n2-standard-8`.

To allow kubectl to support GKE, install the gcloud kubectl auth plugin:

```bash
gcloud components install gke-gcloud-auth-plugin
```

This plugin is necessary to use kubectl to authenticate with GKE. [Click here for more details on this plugin](https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke).

{{< admonition type="warning" >}}
Regional clusters in GKE are designed for resilience, and thus by default span three zones within the region. In the command below, `num-nodes=1`. Note that if you set `num_nodes=3`, you would get 9 nodes in total for the region: 3 in *each* zone. Therefore, leave `num_nodes=1` when you create your cluster.
{{< /admonition >}}

Here is an example of a command you can run using gcloud CLI to create a new cluster:

```bash
gcloud container clusters create loki-gcp \
  --location=europe-west4 \
  --num-nodes=1 \
  --machine-type=n2-standard-8 \
  --release-channel=regular \
  --workload-pool=<PROJECT_ID>.svc.id.goog \
  --enable-ip-alias \
  --no-enable-basic-auth \
  --no-issue-client-certificate
```

Replace `<PROJECT_ID>` with the ID of the project you want to create the cluster in. This should be something like `my-project-123456`.

## Create GCS buckets

{{< admonition type="warning" >}}
 **DO NOT** use the default bucket names;  `chunks`, `ruler` and `admin`. Choose a **unique** name for each bucket. For more information see the following [security update](https://grafana.com/blog/2024/06/27/grafana-security-update-grafana-loki-and-unintended-data-write-attempts-to-amazon-s3-buckets/).
{{< /admonition >}}

Before deploying Loki, you need to create two GCS buckets: one to store logs (chunks) and another to store alert rules (ruler). You can create the bucket using the GCP Management Console or the GCP CLI. The bucket name must be globally unique.

{{< admonition type="note" >}}
GEL customers will require a third bucket to store the admin data. This bucket is not required for OSS users.
{{< /admonition >}}

```bash
gcloud storage buckets create gs://<CHUNKS_BUCKET_NAME> gs://<RULER_BUCKET_NAME> \
  --location=<REGION> \
  --default-storage-class=STANDARD \
  --public-access-prevention \
  --uniform-bucket-level-access \
  --soft-delete-duration=7d
```

Make sure to replace the `region` and `bucket` name with your desired values. We will revisit the bucket policy later in this guide.

Here's an example with all the variables filled in:

```bash
gcloud storage buckets create gs://loki-gcp-chunks gs://loki-gcp-ruler \
  --location=europe-west4 \
  --default-storage-class=STANDARD \
  --public-access-prevention \
  --uniform-bucket-level-access \
  --soft-delete-duration=7d
```

When you run this command, you should get something like this in response:

```console
Creating gs://loki-gcp-chunks/...
Creating gs://loki-gcp-ruler/...
```

## Defining IAM roles and policies

IAM determines who can access which resources on GCP and can be configured in several ways. The recommended method for allowing Loki to access GCS is to use Workload Identity Federation. This method is more secure than creating and distributing a service account key. The following steps show how to create the role and policy using the gcloud CLI.

### Authenticating to the GKE cluster

You need to be able to run `kubectl` commands on the cluster, so make sure you have it installed (run `gcloud components install kubectl` if not) and then run this command:

```bash
gcloud container clusters get-credentials <CLUSTER_NAME> \
  --region=<REGION>
```

Here's an example of that command with the variables filled in:

```bash
gcloud container clusters get-credentials loki-gcp \
  --region=europe-west4
```

This will authenticate you via your GCP IAM identity, write the cluster's access info to your local kubeconfig (usually `~/.kube/config`), and then allow `kubectl` commands to talk to the right cluster from now on.

Then check that you're connected to the GKE cluster and that you're accessing it via `kubectl` by running:

```bash
kubectl config current-context
```

You should get something like this in return:

```bash
gke_my-project-123456_europe-west4_loki-gcp
```

### Create a Kubernetes Namespace

Create a Kubernetes namespace where you'll install your Loki workloads:

```bash
kubectl create namespace <NAMESPACE>
```

Replace `<NAMESPACE>` with the namespace where your Loki workloads will be located.

Example:

```bash
kubectl create namespace loki
```

You should get the output:

```bash
namespace/loki created
```

### Create Kubernetes Service Account (KSA)

A KSA is a cluster identity (service account, named `default` by default) assigned to pods that allows pods to interact with each other.

Create a KSA on your Kubernetes cluster:

```bash
kubectl create serviceaccount <KSA_NAME> \
  --namespace <NAMESPACE>
```

Replace `<KSA_NAME>` with the name of the KSA created above, and `<NAMESPACE>` with the namespace where your Loki workloads are located.

Example:

```bash
kubectl create serviceaccount loki-gcp-ksa \
  --namespace loki
```

You should get this in response:

```bash
serviceaccount/loki-gcp-ksa created
```

### Add IAM Policy to Buckets

{{< admonition type="note" >}}
The [pre-defined `roles/storage.objectUser` role](https://cloud.google.com/storage/docs/access-control/iam-roles#storage.objectUser) is sufficient for Loki to
 operate. See [IAM permissions for Cloud Storage](https://cloud.google.com/storage/docs/access-control/iam-permissions) for details about each individual
 permission. You can use this predefined role or create your own with matching permissions.
{{< /admonition >}}

Create an IAM policy binding on the buckets using the KSA created previously and the roles of your choice. Use a separate command for each bucket, one for chunks, and another for the ruler.

```bash
gcloud storage buckets add-iam-policy-binding gs://<BUCKET_NAME> \
 --role=roles/storage.objectUser \
  --member=principal://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/<PROJECT_ID>.svc.id.goog/subject/ns/<NAMESPACE>/sa/<KSA_NAME> \
  --condition=None
```

Replace `<PROJECT_ID>` with the GCP project ID (for example, project-name), `<PROJECT_NUMBER>` with the project number (for example, 1234567890),
`<NAMESPACE>` with the namespace where Loki is installed, and `<KSA_NAME>` with the name of the KSA you created above.

Then do the same thing for the other bucket.

Examples:

```bash
gcloud storage buckets add-iam-policy-binding gs://loki-gcp-chunks \
  --role=roles/storage.objectUser \
  --member=principal://iam.googleapis.com/projects/12345678901/locations/global/workloadIdentityPools/my-project-123456.svc.id.goog/subject/ns/loki/sa/loki-gcp-ksa \
  --condition=None
```

and

```bash
gcloud storage buckets add-iam-policy-binding gs://loki-gcp-ruler \
  --role=roles/storage.objectUser \
  --member=principal://iam.googleapis.com/projects/12345678901/locations/global/workloadIdentityPools/my-project-123456.svc.id.goog/subject/ns/loki/sa/loki-gcp-ksa \
  --condition=None
```

You should get something like this in response:

```bash
bindings:
- members:
  - projectEditor:my-project-123456
  - projectOwner:my-project-123456
  role: roles/storage.legacyBucketOwner
- members:
  - projectViewer:my-project-123456
  role: roles/storage.legacyBucketReader
- members:
  - projectEditor:my-project-123456
  - projectOwner:my-project-123456
  role: roles/storage.legacyObjectOwner
- members:
  - projectViewer:my-project-123456
  role: roles/storage.legacyObjectReader
- members:
  - principal://iam.googleapis.com/projects/12345678901/locations/global/workloadIdentityPools/my-project-123456.svc.id.goog/subject/ns/loki/sa/loki-gcp-ksa
  role: roles/storage.objectUser
etag: CAI=
kind: storage#policy
resourceId: projects/_/buckets/loki-gcp-chunks
version: 1
```

## Deploying the Helm chart

Before we can deploy the Loki Helm chart, we need to add the Grafana Community chart repository to Helm. This repository contains the Loki Helm chart.

1. Add the Grafana Community chart repository to Helm:

    ```bash
    helm repo add grafana-community https://grafana-community.github.io/helm-charts
    ```

2. Update the chart repository:

    ```bash
    helm repo update
    ```

### Loki Basic Authentication

Loki by default does not come with any authentication. Since we will be deploying Loki to GCP and exposing the gateway to the internet, we recommend adding at least basic authentication. In this guide we will give Loki a username and password:

1. To start we will need create a `.htpasswd` file with the username and password. You can use the `htpasswd` command to create the file:

   {{< admonition type="tip" >}}
    If you don't have the `htpasswd` command installed, you can install it using `brew` or `apt-get` or `yum` depending on your OS.
   {{< /admonition >}}

    ```bash
    htpasswd -c .htpasswd <username>
    ```

    This will create a file called `.htpasswd` with the username you provided. You will be prompted to enter a password.

1. Create a Kubernetes secret with the `.htpasswd` file:

    ```bash
    kubectl create secret generic loki-basic-auth --from-file=.htpasswd -n loki
    ```

    This will create a secret called `loki-basic-auth` in the `loki` namespace. We will reference this secret in the Loki Helm chart configuration.
  
1. Create a `canary-basic-auth` secret for the canary:

    ```bash
    kubectl create secret generic canary-basic-auth \
      --from-literal=username=<USERNAME> \
      --from-literal=password=<PASSWORD> \
      -n loki
    ```

    We create a literal secret with the username and password for Loki canary to authenticate with the Loki gateway.
    **Make sure to replace the placeholders with your desired username and password.**

### Loki Helm chart configuration

Create a `values.yaml` file choosing the configuration options that best suit your requirements. Below there is an example of `values.yaml` files for the Loki Helm chart in [microservices](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/#microservices-mode) mode.

```yaml
loki:
  schemaConfig:
    configs:
      - from: "2024-04-01"
        store: tsdb
        object_store: gcs
        schema: v13
        index:
          prefix: loki_index_
          period: 24h
  storage_config:
    gcs:
      bucket_name: <CHUNK_BUCKET_NAME> # Your actual gcs bucket name, for example, loki-gcp-chunks
  ingester:
    chunk_encoding: snappy
  pattern_ingester:
    enabled: true
  limits_config:
    allow_structured_metadata: true
    volume_enabled: true
    retention_period: 672h # 28 days retention
  compactor:
    retention_enabled: true
    delete_request_store: gcs
  rulerConfig:
    enable_api: true
    alertmanager_url: http://prom:9093 # The URL of the Alertmanager to send alerts (Prometheus, Mimir, etc.)

  querier:
    max_concurrent: 4

  storage:
    type: gcs
    bucketNames:
      chunks: <CHUNK_BUCKET_NAME> # Your actual gcs bucket name, for example, loki-gcp-chunks
      ruler: <RULER_BUCKET_NAME> # Your actual gcs bucket name, for example, loki-gcp-ruler
      
serviceAccount:
  create: false
  name: <KSA_NAME>

deploymentMode: Distributed

ingester:
  replicas: 3
  zoneAwareReplication:
    enabled: false

querier:
  replicas: 3
  maxUnavailable: 2

queryFrontend:
  replicas: 2
  maxUnavailable: 1

queryScheduler:
  replicas: 2

distributor:
  replicas: 3
  maxUnavailable: 2
 
compactor:
  replicas: 1

indexGateway:
  replicas: 2
  maxUnavailable: 1

patternIngester:
  enabled: true
  replicas: 1

ruler:
  replicas: 1
  maxUnavailable: 1


# This exposes the Loki gateway so it can be written to and queried externaly
gateway:
  service:
    type: LoadBalancer
  basicAuth:
    enabled: true
    existingSecret: loki-basic-auth

# Since we are using basic auth, we need to pass the username and password to the canary
lokiCanary:
  extraArgs:
    - -pass=$(LOKI_PASS)
    - -user=$(LOKI_USER)
  extraEnv:
    - name: LOKI_PASS
      valueFrom:
        secretKeyRef:
          name: canary-basic-auth
          key: password
    - name: LOKI_USER
      valueFrom:
        secretKeyRef:
          name: canary-basic-auth
          key: username

# Disable minio storage
minio:
  enabled: false

backend:
  replicas: 0
read:
  replicas: 0
write:
  replicas: 0

singleBinary:
  replicas: 0
```

{{< admonition type="caution" >}}
Make sure to replace the placeholders with your actual values.
{{< /admonition >}}

{{< admonition type="note" >}}
In `values.yaml` above, you may notice that `serviceAccount` is set to `create: false`. This is because you want to use the service account that you created earlier instead of creating a new one.
{{< /admonition >}}

It is critical to define a valid `values.yaml` file for the Loki deployment. To remove the risk of misconfiguration, let's break down the configuration options to keep in mind when deploying to GCP:

- **Loki Config vs. Values Config:**
  - The `values.yaml` file contains a section called `loki`, which contains a direct representation of the Loki configuration file.
  - This section defines the Loki configuration, including the schema, storage, and querier configuration.
  - The key configuration for chunks is `loki.storage` (type, `bucketNames`, and provider settings), which the chart maps into Loki's `common.storage` section. This tells Loki where to store the chunks.
  - The `rulerConfig` section defines ruler-specific settings such as `enable_api` and `alertmanager_url`. Ruler object storage is derived automatically from `loki.storage` and `loki.storage.bucketNames.ruler`.
  - For the full Loki configuration, refer to the [Loki Configuration](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/) documentation.

- **Storage:**
  - Defines where the Helm chart stores data.
  - Set the type to `GCS` since we are using Google Cloud Storage.
  - Configure the bucket names for the chunks and ruler to match the buckets created earlier.
  - The `GCS` section specifies the region of the bucket.

- **Service Account:**
  - The `serviceAccount` section is used to define the IAM role for the Loki service account.
  - This is where the IAM role created earlier is linked.

- **Zone-Aware Replication:**
  - The Helm chart enables zone-aware ingester replication by default, which creates three ingester StatefulSets (zone-a, zone-b, zone-c) for faster rollouts.
  - In this guide, zone-aware replication is explicitly disabled (`ingester.zoneAwareReplication.enabled: false`) for simplicity. For production deployments, consider enabling it to improve rollout resilience.

- **Gateway:**
  - Defines how the Loki gateway will be exposed.
  - We are using a `LoadBalancer` service type in this configuration.

### Deploy Loki

Now that you have created the `values.yaml` file, you can deploy Loki using the Helm chart.

1. Deploy using the newly created `values.yaml` file:

    ```bash
    helm install --values values.yaml loki grafana-community/loki -n loki --create-namespace
    ```

    **Ensure the namespace and service account name in your values (`serviceAccount.name`) match the Workload Identity principal used in the bucket IAM bindings.**

2. Verify the deployment:

    ```bash
    kubectl get pods -n loki
    ```

    You should see the Loki pods running.

    ```console
    NAME                                    READY   STATUS    RESTARTS   AGE
    loki-canary-crqpg                       1/1     Running   0          10m
    loki-canary-hm26p                       1/1     Running   0          10m
    loki-canary-v9wv9                       1/1     Running   0          10m
    loki-chunks-cache-0                     2/2     Running   0          10m
    loki-compactor-0                        1/1     Running   0          10m
    loki-distributor-78ccdcc9b4-9wlhl       1/1     Running   0          10m
    loki-distributor-78ccdcc9b4-km6j2       1/1     Running   0          10m
    loki-distributor-78ccdcc9b4-ptwrb       1/1     Running   0          10m
    loki-gateway-5f97f78755-hm6mx           1/1     Running   0          10m
    loki-index-gateway-0                    1/1     Running   0          10m
    loki-index-gateway-1                    1/1     Running   0          10m
    loki-ingester-0                         1/1     Running   0          10m
    loki-ingester-1                         1/1     Running   0          10m
    loki-ingester-2                         1/1     Running   0          10m
    loki-pattern-ingester-0                 1/1     Running   0          10m
    loki-querier-89d4ff448-4vr9b            1/1     Running   0          10m
    loki-querier-89d4ff448-7nvrf            1/1     Running   0          10m
    loki-querier-89d4ff448-q89kh            1/1     Running   0          10m
    loki-query-frontend-678899db5-n5wc4     1/1     Running   0          10m
    loki-query-frontend-678899db5-tf69b     1/1     Running   0          10m
    loki-query-scheduler-7d666bf759-9xqb5   1/1     Running   0          10m
    loki-query-scheduler-7d666bf759-kpb5q   1/1     Running   0          10m
    loki-results-cache-0                    2/2     Running   0          10m
    loki-ruler-0                            1/1     Running   0          10m
    ```
  
### Find the Loki Gateway Service

The Loki Gateway service is a LoadBalancer service that exposes the Loki gateway to the internet. This is where you will write logs to and query logs from. By default NGINX is used as the gateway.

{{< admonition type="caution" >}}
The Loki Gateway service is exposed to the internet. We provide basic authentication using a username and password in this tutorial. Refer to the [Authentication](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/authentication/) documentation for more information.
{{< /admonition >}}

To find the Loki Gateway service, run the following command:

```bash
kubectl get svc -n loki
```

You should see the Loki Gateway service with an external IP address. This is the address you will use to write to and query Loki.

```console
NAME           TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)        AGE
loki-gateway   LoadBalancer   34.118.239.140   34.91.203.240   80:30566/TCP   25m
```

In this case, the external IP address is `34.91.203.240`.

Congratulations! You have successfully deployed Loki on GCP using the Helm chart. Before we finish, let's test the deployment.

## Testing Your Loki Deployment

k6 is one of the fastest ways to test your Loki deployment. This will allow you to both write and query logs to Loki. To get started with k6, follow the steps below:

1. Install k6 with the Loki extension on your local machine. Refer to [Installing k6 and the xk6-loki extension](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/k6/).

2. Create a `gcp-test.js` file with the following content:

   ```javascript
    import {sleep, check} from 'k6';
    import loki from 'k6/x/loki';

    /**
    * URL used for push and query requests
    * Path is automatically appended by the client
    * @constant {string}
    */

    const username = '<USERNAME>';
    const password = '<PASSWORD>';
    const external_ip = '<EXTERNAL-IP>';

    const credentials = `${username}:${password}`;

    const BASE_URL = `http://${credentials}@${external_ip}`;

    /**
    * Helper constant for byte values
    * @constant {number}
    */
    const KB = 1024;

    /**
    * Helper constant for byte values
    * @constant {number}
    */
    const MB = KB * KB;

    /**
    * Instantiate config and Loki client
    */

    const conf = new loki.Config(BASE_URL);
    const client = new loki.Client(conf);

    /**
    * Define test scenario
    */
    export const options = {
      vus: 10,
      iterations: 10,
    };

    export default () => {
      // Push request with 10 streams and uncompressed logs between 800KB and 2MB
      var res = client.pushParameterized(10, 800 * KB, 2 * MB);
      // Check for successful write
      check(res, { 'successful write': (res) => res.status == 204 });

      // Pick a random log format from label pool
      let format = randomChoice(conf.labels["format"]);

      // Execute instant query with limit 1
      res = client.instantQuery(`count_over_time({format="${format}"}[1m])`, 1)
      // Check for successful read
      check(res, { 'successful instant query': (res) => res.status == 200 });

      // Execute range query over last 5m and limit 1000
      res = client.rangeQuery(`{format="${format}"}`, "5m", 1000)
      // Check for successful read
      check(res, { 'successful range query': (res) => res.status == 200 });

      // Wait before next iteration
      sleep(1);
    }

    /**
    * Helper function to get random item from array
    */
    function randomChoice(items) {
      return items[Math.floor(Math.random() * items.length)];
    }
   ```

   **Replace `<EXTERNAL-IP>` with the external IP address of the Loki Gateway service.**

   This script will write logs to Loki and query logs from Loki. It will write logs in a random format between 800KB and 2MB and query logs in a random format over the last 5 minutes.
  
3. Run the test:

    ```bash
    ./k6 run gcp-test.js
    ```

    This will run the test and output the results. You should see the test writing logs to Loki and querying logs from Loki.

Now that you have successfully deployed Loki in microservices mode on GCP, you may wish to explore the following:

- [Monitor a Loki Cluster](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/)
- [Sending data to Loki](https://grafana.com/docs/loki/<LOKI_VERSION>/send-data/)
- [Querying Loki](https://grafana.com/docs/loki/<LOKI_VERSION>/query/)
- [Manage](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/)

# Migrate from `loki-distributed` Helm chart

This guide will walk you through migrating to the `loki` Helm Chart, v3.0 or higher, from the `loki-distributed` Helm Chart (v0.63.2 at time of writing). The process consists of deploying the new `loki` Helm Chart alongside the existing `loki-distributed` installation. By joining the new cluster to the existing cluster's ring, you will create one large cluster. This will allow you to manually bring down the `loki-distributed` components in a safe way to avoid any data loss.

**Before you begin:**

We recommend having a Grafana instance available to monitor both the existing and new clusters, to make sure there is no data loss during the migration process. The `loki` chart ships with self-monitoring features, including dashboards. These are useful for monitoring the health of the new cluster as it spins up.

Start by updating your existing Grafana Agent or Promtail config (whatever is scraping logs from your environment) to _exclude_ the new deployment. The new `loki` chart ships with its own self-monitoring mechanisms, and we want to make sure it's not scraped twice, which would produce duplicate logs. The best way to do this is via a relabel config that will drop logs from the new deployment, for example something like:

```yaml
- source_labels:
    - "__meta_kubernetes_pod_label_app_kubernetes_io_component"
  regex: "(canary|read|write)"
  action: "drop"
```

This leverages the fact that the new deployment adds a `app.kubernetes.io/component` label of either `read` for the Read pods, `write` for the Write pods, and `canary` for the Loki Canary pods. These annotations are not present in the `loki-distributed` deployment, so this should only match logs from the new deployment.

**To Migrate from `loki-distributed` to `loki`**

1. Deploy new Loki Cluster

   Next you will deploy the new `loki` chart in the same namespace as your existing `loki-distributed` installation. Make sure to use the same buckets and storage configuration as your existing installation. You will need to set some special `migrate` values as well:

   ```yaml
   migrate:
     fromDistributed:
       enabled: true
       memberlistService: loki-loki-distributed-memberlist
   ```

   The value of `memberlistService` must be the kubernetes service created for the purpose of Memberlist DNS in the `loki-distributed` deployment. It should match the value of `memberlist.join_members` in the config of the `loki-distributed` deployment. This is what will make the new cluster join the existing clusters ring. It is important to join all existing rings, if you are using different memberlist DNS for different rings, you must manually set the value of each applicable `join_members` configuration for each ring. If using the same memberlist DNS for all rings, as is the default in the `loki-distributed` chart, setting `migrate.memberlistService` should be enough.

   Once the new cluster is up, add the appropriate data source in Grafana for the new cluster. Check that the following queries return results:

   - Confirm new and old logs are in the new deployment. Using the new deployment's Loki data source in Grafana, look for:
     - Logs with a job that is unique to your existing Promtail or Grafana Agent, the one we adjusted above to exclude logs from the new deployment which is not yet pushing logs to the new deployment. If you can query those via the new deployment in shows we have not lost historical logs.
     - Logs with the label `job="loki/loki-read"`. The read component does not exist in `loki-distributed`, so this show the new Loki cluster's self monitoring is working correctly.
   - Confirm new logs are in the old deployment. Using the old deployment's Loki data source in Grafana, look for:
     - Logs with the label `job="loki/loki-read"`. Since you have excluded logs from the new deployment from going to the `loki-distributed` deployment, if you can query them through the `loki-distributed` Loki data source that show the ingesters have joined the same ring, and are queryable from the `loki-distributed` queriers.

1. Convert all Clients to Push Logs to New `loki` Deployment

   Assuming everything is working as expected, you can now modify the `clients` section of your Grafana Agent or Promtail configuration to push logs to the new deployment. After this change is made, the `loki-distributed` cluster will no longer receive new logs and can be carefully scaled down.

   Once this has deployed, query the new `loki` cluster's Loki data source for new logs to make sure they're still being ingested.

1. Tear Down the Old Loki Canary

   If you had previously deployed the canary via the `loki-canary` Helm Chart, you can now tear it down. The new chart ships the canary by default and is automatically configured to scrape it.

1. Update Flush Config On `loki-distributed` Deployment

   You are almost ready to start scaling down the old `loki-distributed` cluster. Before you start, however, it is important to make sure the `loki-distributed` ingesters are configured to flush on shutdown. Since these ingesters will not be coming back, there will be no opportunity for them to replay their WALs, so they need to flush all in-memory logs before shutting down to prevent data loss.

   The easiest way to do this is via the `extraArgs` argument in the `ingester` section of the `loki-distributed` chart. You may also want to set the ingester's log level to `debug` to see messages about the flushing process.

   ````yaml
   ingester:
     replicas: 3
     extraArgs:
       - '-ingester.flush-on-shutdown=true'
       - '-log.level=debug'
       ```

   Deploy this change, and make sure all ingesters restart and are running the latest configuration.

   ````

1. Scale Down Ingesters One at a Time

   It is now time to start scaling down `loki-distributed`. Scale down the ingester StatefulSet or Deployment (depending on how your `loki-distributed` chart is deployed) 1 replica at a time. If `debug` logs were enabled, you can monitor the logs of each ingester as it's terminating to make sure the flushing process was successful. Once the ingester pod is fully terminated, continue decrementing by another 1 replica. Continue until there are 0 instances of the ingester running.

   You can use the following command to edit the ingester StatefulSet in order to change the number of replicas (making sure to replace _\<NAMESPACE\>_ with the correct namespace):

   ```bash
   kubectl -n <NAMESPACE> edit statefulsets.apps loki-distributed-ingester
   ```

1. Remove `loki-distributed` cluster

   Double check that logs are still coming in to the new cluster. If something is wrong, it will be much easier to quickly scale back up `loki-distributed` ingesters before tearing down the whole cluster so you can investigate. If everything looks good, you can tear down `loki-distributed` using `helm uninstall`. For example:

   ```bash
   helm uninstall -n loki loki-distributed
   ```

   Now edit the new `loki` cluster to remove the `migrate` options you added when first installing. Remove all of the following from your `values.yaml`:

   ```yaml
   migrate:
     fromDistributed:
       enabled: true
       memberlistService: loki-loki-distributed-memberlist
   ```

   To apply the new configuration (assuming you installed the new chart as `loki` in the _loki_ namespace):

   ```bash
   helm upgrade -n loki loki grafana-community/loki --values values.yaml
   ```

   The `migrate.fromDistributed.memberlistService` was added as an _additional_ memberlist join member, so once this new config is pushed, the components should roll without interruption.

1. Check the Dashboards

   Now that the migration is finished, you can check the dashboards included with the new `loki` chart to make sure everything is working as expected. You can also check the loki canary metrics to make sure nothing was lost during the migration. Assuming everything was in the `loki` namespace, the following query, if run over a time period that starts before the migration, and ends after it was complete, should clearly illustrate when metrics started coming from the new canary, and if and when there was data detected by either during the process:

   ```logql
   (
     sum(increase(loki_canary_missing_entries_total{namespace="loki"}[$__range])) by (job)
     /
     sum(increase(loki_canary_entries_total{namespace="loki"}[$__range])) by (job)
   )*100
   ```

# Upgrade Loki 1.x versions

Every attempt is made to keep Grafana Loki backwards compatible, such that upgrades should be low risk and low friction.

Unfortunately Loki is software and software is hard and sometimes we are forced to make decisions between ease of use and ease of maintenance.

If we have any expectation of difficulty upgrading, we will document it here.

As more versions are released it becomes more likely unexpected problems arise moving between multiple versions at once. If possible try to stay current and do sequential updates. If you want to skip versions, try it in a development environment before attempting to upgrade production.

The following are upgrade instructions for Grafana Loki 1.x versions.

## Checking for config changes

<!-- vale Grafana.Spelling = NO -->

Using docker you can check changes between 2 versions of Loki with a command like this:

```bash
export OLD_LOKI=2.9.4
export NEW_LOKI=3.0.0
export CONFIG_FILE=local-config.yaml
diff --color=always --side-by-side <(docker run --rm -t -v "${PWD}":/config grafana/loki:${OLD_LOKI} -config.file=/etc/loki/${CONFIG_FILE} -print-config-stderr 2>&1 | sed '/Starting Loki/q' | tr -d '\r') <(docker run --rm -t -v "${PWD}":/config grafana/loki:${NEW_LOKI} -config.file=/etc/loki/${CONFIG_FILE} -print-config-stderr 2>&1 | sed '/Starting Loki/q' | tr -d '\r') | less -R
```

The `tr -d '\r'` is likely not necessary for most people, seems like WSL2 was sneaking in some windows newline characters...

The output is incredibly verbose as it shows the entire internal config struct used to run Loki, you can play around with the diff command if you prefer to only show changes or a different style output.

## 1.6.0

### Important: Ksonnet port changed and removed NET_BIND_SERVICE capability from Docker image

In 1.5.0 we changed the Loki user to not run as root which created problems binding to port 80.
To address this we updated the docker image to add the NET_BIND_SERVICE capability to the loki process
which allowed Loki to bind to port 80 as a non root user, so long as the underlying system allowed that
linux capability.

This has proved to be a problem for many reasons and in PR [2294](https://github.com/grafana/loki/pull/2294/files)
the capability was removed.

It is now no longer possible for the Loki to be started with a port less than 1024 with the published docker image.

The default for Helm has always been port 3100, and Helm users should be unaffected unless they changed the default.

**Ksonnet users however should closely check their configuration, in PR 2294 the loki port was changed from 80 to 3100**

### IMPORTANT: If you run Loki in microservices mode, special rollout instructions

A new ingester GRPC API has been added allowing to speed up metric queries, to ensure a rollout without query errors **make sure you upgrade all ingesters first.**
Once this is done you can then proceed with the rest of the deployment, this is to ensure that queriers won't look for an API not yet available.

If you roll out everything at once, queriers with this new code will attempt to query ingesters which may not have the new method on the API and queries will fail.

This will only affect reads(queries) and not writes and only for the duration of the rollout.

### IMPORTANT: Scrape config changes to both Helm and Ksonnet will affect labels created by Promtail

Loki PR [2091](https://github.com/grafana/loki/pull/2091) makes several changes to the Promtail scrape config:

This is triggered by jsonnet-libs PR [261](https://github.com/grafana/jsonnet-libs/pull/261):

The above PR changes the instance label to be actually unique within
a scrape config. It also adds a pod and a container target label
so that metrics can easily be joined with metrics from cAdvisor, KSM,
and the Kubelet.

This commit adds the same to the Loki scrape config. It also removes
the container_name label. It is the same as the container label
and was already added to Loki previously. However, the
container_name label is deprecated and has disappeared in Kubernetes 1.16,
so that it will soon become useless for direct joining.

TL;DR

The following label have been changed in both the Helm and Ksonnet Promtail scrape configs:

`instance` -> `pod`
`container_name` -> `container`

### Experimental boltdb-shipper changes

PR [2166](https://github.com/grafana/loki/pull/2166) now forces the index to have a period of exactly `24h`:

Loki will fail to start with an error if the active schema or upcoming schema are not set to a period of `24h`

You can add a new schema config like this:

```yaml
schema_config:
  configs:
    - from: 2020-01-01      <----- This is your current entry, date will be different
      store: boltdb-shipper
      object_store: aws
      schema: v11
      index:
        prefix: index_
        period: 168h
    - from: [INSERT FUTURE DATE HERE]   <----- Add another entry, set a future date
      store: boltdb-shipper
      object_store: aws
      schema: v11
      index:
        prefix: index_
        period: 24h   <--- This must be 24h
```

If you are not on `schema: v11` this would be a good opportunity to make that change _in the new schema config_ also.

{{< admonition type="note" >}}
If the current time in your timezone is after midnight UTC already, set the date one additional day forward.
{{< /admonition >}}

There was also a significant overhaul to how boltdb-shipper internals, this should not be visible to a user but as this
feature is experimental and under development bug are possible!

The most noticeable change if you look in the storage, Loki no longer updates an existing file and instead creates a
new index file every 15mins, this is an important move to make sure objects in the object store are immutable and
will simplify future operations like compaction and deletion.

### Breaking CLI flags changes

The following CLI flags where changed to improve consistency, they are not expected to be widely used

```diff
- querier.query_timeout
+ querier.query-timeout

- distributor.extra-query-delay
+ querier.extra-query-delay

- max-chunk-batch-size
+ store.max-chunk-batch-size

- ingester.concurrent-flushed
+ ingester.concurrent-flushes
```

### Loki Canary metric name changes

When adding some new features to the canary we realized the existing metrics were not compliant with standards for counter names, the following metrics have been renamed:

```nohighlight
loki_canary_total_entries               ->      loki_canary_entries_total
loki_canary_out_of_order_entries        ->      loki_canary_out_of_order_entries_total
loki_canary_websocket_missing_entries   ->      loki_canary_websocket_missing_entries_total
loki_canary_missing_entries             ->      loki_canary_missing_entries_total
loki_canary_unexpected_entries          ->      loki_canary_unexpected_entries_total
loki_canary_duplicate_entries           ->      loki_canary_duplicate_entries_total
loki_canary_ws_reconnects               ->      loki_canary_ws_reconnects_total
loki_canary_response_latency            ->      loki_canary_response_latency_seconds
```

### Ksonnet Changes

In `production/ksonnet/loki/config.libsonnet` the variable `storage_backend` used to have a default value of `'bigtable,gcs'`.
This has been changed to providing no default and will error if not supplied in your environment jsonnet,
here is an example of what you should add to have the same behavior as the default (namespace and cluster should already be defined):

```jsonnet
_config+:: {
    namespace: 'loki-dev',
    cluster: 'us-central1',
    storage_backend: 'gcs,bigtable',
```

Defaulting to `gcs,bigtable` was confusing for anyone using ksonnet with other storage backends as it would manifest itself with obscure bigtable errors.

## 1.5.0

{{< admonition type="note" >}}
The required upgrade path outlined for version 1.4.0 below is still true for moving to 1.5.0 from any release older than 1.4.0 (e.g. 1.3.0 -> 1.5.0 needs to also look at the 1.4.0 upgrade requirements).
{{< /admonition >}}

### Breaking config changes

Loki 1.5.0 vendors Cortex v1.0.0 (congratulations!), which has a [massive list of changes](https://cortexmetrics.io/docs/changelog/#1-0-0-2020-04-02).

While changes in the command line flags affect Loki as well, we usually recommend people to use configuration file instead.

Cortex has done lot of cleanup in the configuration files, and you are strongly urged to take a look at the [annotated diff for config file](https://cortexmetrics.io/docs/changelog/#config-file-breaking-changes) before upgrading to Loki 1.5.0.

Following fields were removed from YAML configuration completely: `claim_on_rollout` (always true), `normalise_tokens` (always true).

#### Test Your Config

To see if your config needs to change, one way to quickly test is to download a 1.5.0 (or newer) binary from the [release page](https://github.com/grafana/loki/releases/tag/v1.5.0)

Then run the binary providing your config file `./loki-linux-amd64 -config.file=myconfig.yaml`

If there are configs which are no longer valid you will see errors immediately:

```shell
./loki-linux-amd64 -config.file=loki-local-config.yaml
failed parsing config: loki-local-config.yaml: yaml: unmarshal errors:
  line 35: field dynamodbconfig not found in type aws.StorageConfig
```

Referencing the [list of diffs](https://cortexmetrics.io/docs/changelog/#config-file-breaking-changes) I can see this config changed:

```diff
-  dynamodbconfig:
+  dynamodb:
```

Also several other AWS related configs changed and would need to update those as well.

### Loki Docker Image User and File Location Changes

To improve security concerns, in 1.5.0 the Docker container no longer runs the loki process as `root` and instead the process runs as user `loki` with UID `10001` and GID `10001`

This may affect people in a couple ways:

#### Loki Port

If you are running Loki with a config that opens a port number above 1024 (which is the default, 3100 for HTTP and 9095 for GRPC) everything should work fine in regards to ports.

If you are running Loki with a config that opens a port number less than 1024 Linux normally requires root permissions to do this, HOWEVER in the Docker container we run `setcap cap_net_bind_service=+ep /usr/bin/loki`

This capability lets the loki process bind to a port less than 1024 when run as a non root user.

Not every environment will allow this capability however, it's possible to restrict this capability in linux.  If this restriction is in place, you will be forced to run Loki with a config that has HTTP and GRPC ports above 1024.

#### Filesystem

{{< admonition type="note" >}}
The location Loki is looking for files with the provided config in the docker image has changed.
{{< /admonition >}}

In 1.4.0 and earlier the included config file in the docker container was using directories:

```shell
/tmp/loki/index
/tmp/loki/chunks
```

In 1.5.0 this has changed:

```shell
/loki/index
/loki/chunks
```

This will mostly affect anyone using docker-compose or docker to run Loki and are specifying a volume to persist storage.

**There are two concerns to track here, one is the correct ownership of the files and the other is making sure your mounts updated to the new location.**

One possible upgrade path would look like this:

If I were running Loki with this command `docker run -d --name=loki --mount source=loki-data,target=/tmp/loki -p 3100:3100 grafana/loki:1.4.0`

This would mount a docker volume named `loki-data` to the `/tmp/loki` folder which is where Loki will persist the `index` and `chunks` folder in 1.4.0

To move to 1.5.0 I can do the following (note that your container names and paths and volumes etc may be different):

```bash
docker stop loki
docker rm loki
docker run --rm --name="loki-perm" -it --mount source=loki-data,target=/mnt ubuntu /bin/bash
cd /mnt
chown -R 10001:10001 ./*
exit
docker run -d --name=loki --mount source=loki-data,target=/loki -p 3100:3100 grafana/loki:1.5.0
```

Notice the change in the `target=/loki` for 1.5.0 to the new data directory location specified in the [included Loki config file](https://github.com/grafana/loki/blob/main/cmd/loki/loki-docker-config.yaml).

The intermediate step of using an ubuntu image to change the ownership of the Loki files to the new user might not be necessary if you can easily access these files to run the `chown` command directly.
That is if you have access to `/var/lib/docker/volumes` or if you mounted to a different local filesystem directory, you can change the ownership directly without using a container.

### Loki Duration Configs

If you get an error like:

```nohighlight
 ./loki-linux-amd64-1.5.0 -log.level=debug -config.file=/etc/loki/config.yml
failed parsing config: /etc/loki/config.yml: not a valid duration string: "0"
```

This is because of some underlying changes that no longer allow durations without a unit.

Unfortunately the yaml parser doesn't give a line number but it's likely to be one of these two:

```yaml
chunk_store_config:
  max_look_back_period: 0s # DURATION VALUES MUST HAVE A UNIT EVEN IF THEY ARE ZERO

table_manager:
  retention_deletes_enabled: false
  retention_period: 0s # DURATION VALUES MUST HAVE A UNIT EVEN IF THEY ARE ZERO
```

### Promtail Config Changes

The underlying backoff library used in Promtail had a config change which wasn't originally noted in the release notes:

If you get this error:

```nohighlight
Unable to parse config: /etc/promtail/promtail.yaml: yaml: unmarshal errors:
  line 3: field maxbackoff not found in type util.BackoffConfig
  line 4: field maxretries not found in type util.BackoffConfig
  line 5: field minbackoff not found in type util.BackoffConfig
```

The new values are:

```yaml
min_period:
max_period:
max_retries:
```

## 1.4.0

Loki 1.4.0 vendors Cortex v0.7.0-rc.0 which contains [several breaking config changes](https://github.com/cortexproject/cortex/blob/v0.7.0-rc.0/CHANGELOG).

In the [cache_config](https://grafana.com/docs/loki/<LOKI_VERSION>/configure#cache_config), `defaul_validity` has changed to `default_validity`.

If you configured your schema via arguments and not a config file, this is no longer supported. This is not something we had ever provided as an option via docs and is unlikely anyone is doing, but worth mentioning.

The other config changes should not be relevant to Loki.

### Required Upgrade Path

The newly vendored version of Cortex removes code related to de-normalized tokens in the ring. What you need to know is this:

A "shared ring" as mentioned below refers to using _consul_ or _etcd_ for values in the following config:

```yaml
kvstore:
  # The backend storage to use for the ring. Supported values are
  # consul, etcd, inmemory
  store: <string>
```

- Running without using a shared ring (inmemory): No action required
- Running with a shared ring and upgrading from v1.3.0 -> v1.4.0: No action required
- Running with a shared ring and upgrading from any version less than v1.3.0 (e.g. v1.2.0) -> v1.4.0: **ACTION REQUIRED**

There are two options for upgrade if you are not on version 1.3.0 and are using a shared ring:

- Upgrade first to v1.3.0 **BEFORE** upgrading to v1.4.0

OR

- If you are running a single binary you only need to add this flag to your single binary command.

1. Add the following configuration to your ingesters command: `-ingester.normalise-tokens=true`
1. Restart your ingesters with this config
1. Proceed with upgrading to v1.4.0
1. Remove the config option (only do this after everything is running v1.4.0)

It is also possible to enable this flag via config file, see the [`lifecycler_config`](https://github.com/grafana/loki/tree/v1.3.0/docs/configuration#lifecycler_config) configuration option.

If using the Helm Loki chart:

```yaml
extraArgs:
  ingester.normalise-tokens: true
```

If using the Helm Loki-Stack chart:

```yaml
loki:
  extraArgs:
    ingester.normalise-tokens: true
```

#### What will go wrong

If you attempt to add a v1.4.0 ingester to a ring created by Loki v1.2.0 or older which does not have the commandline argument `-ingester.normalise-tokens=true` (or configured via [config file](https://github.com/grafana/loki/tree/v1.3.0/docs/configuration#lifecycler_config)), the v1.4.0 ingester will remove all the entries in the ring for all the other ingesters as it cannot "see" them.

This will result in distributors failing to write and a general ingestion failure for the system.

If this happens to you, you will want to rollback your deployment immediately. You need to remove the v1.4.0 ingester from the ring ASAP, this should allow the existing ingesters to re-insert their tokens.  You will also want to remove any v1.4.0 distributors as they will not understand the old ring either and will fail to send traffic.
<!-- vale Grafana.Spelling = YES -->

# Upgrade Loki 2.x versions

Every attempt is made to keep Grafana Loki backwards compatible, such that upgrades should be low risk and low friction.

Unfortunately Loki is software and software is hard and sometimes we are forced to make decisions between ease of use and ease of maintenance.

If we have any expectation of difficulty upgrading, we will document it here.

As more versions are released it becomes more likely unexpected problems arise moving between multiple versions at once. If possible try to stay current and do sequential updates. If you want to skip versions, try it in a development environment before attempting to upgrade production.

The following are upgrade instructions for Grafana Loki 2.x versions.

## Checking for config changes

<!-- vale Grafana.Spelling = NO -->

Using docker you can check changes between 2 versions of Loki with a command like this:

```bash
export OLD_LOKI=2.9.4
export NEW_LOKI=3.0.0
export CONFIG_FILE=local-config.yaml
diff --color=always --side-by-side <(docker run --rm -t -v "${PWD}":/config grafana/loki:${OLD_LOKI} -config.file=/etc/loki/${CONFIG_FILE} -print-config-stderr 2>&1 | sed '/Starting Loki/q' | tr -d '\r') <(docker run --rm -t -v "${PWD}":/config grafana/loki:${NEW_LOKI} -config.file=/etc/loki/${CONFIG_FILE} -print-config-stderr 2>&1 | sed '/Starting Loki/q' | tr -d '\r') | less -R
```

The `tr -d '\r'` is likely not necessary for most people, seems like WSL2 was sneaking in some windows newline characters...

The output is incredibly verbose as it shows the entire internal config struct used to run Loki, you can play around with the diff command if you prefer to only show changes or a different style output.

## 2.9.0

### Loki 2.9.0

#### Index gateway shuffle sharding

The index gateway now supports shuffle sharding of index data when running in
"ring" mode. The index data is sharded by tenant where each tenant gets
assigned a sub-set of all available instances of the index gateways in the ring.

If you configured a high replication factor to accommodate for load, since
in the past this was the only option to give a tenant more instances for
querying, you should consider reducing the replication factor to a meaningful
value for replication (for example, from 12 to 3) and instead set the shard factor for
individual tenants as required.

If the global shard factor (no per-tenant) is 0 (default value), the global
shard factor is set to replication factor. It can still be overwritten per
tenant.

In the context of the index gateway, sharding is synonymous to replication.

#### Index shipper multi-store support

In previous releases, if you did not explicitly configure `-boltdb.shipper.shared-store`, `-tsdb.shipper.shared-store`, those values default to the `object_store` configured in the latest `period_config` of the corresponding index type.
These defaults are removed in favor of uploading indexes to multiple stores. If you do not explicitly configure a `shared-store`, the boltdb and tsdb indexes will be shipped to the `object_store` configured for that period.

#### Shutdown marker file

A shutdown marker file can be written by the `/ingester/prepare_shutdown` endpoint.
If the new `ingester.shutdown_marker_path` config setting has a value that value is used.
If not the`common.path_prefix` config setting is used if it has a value. Otherwise a warning is shown
in the logs on startup and the `/ingester/prepare_shutdown` endpoint will return a 500 status code.

#### Compactor multi-store support

In previous releases, setting `-boltdb.shipper.compactor.shared-store` configured the following:

- store used for managing delete requests.
- store on which index compaction should be performed.

If `-boltdb.shipper.compactor.shared-store` was not set, it used to default to the `object_store` configured in the latest `period_config` that uses either the tsdb or boltdb-shipper index.

Compactor now supports index compaction on multiple buckets/object stores.
And going forward loki will not set any defaults on `-boltdb.shipper.compactor.shared-store`, this has a couple of side effects detailed as follows:

##### store on which index compaction should be performed

If `-boltdb.shipper.compactor.shared-store` is configured by the user, loki would run index compaction only on the store specified by the config.
If not set, compaction would be performed on all the object stores that contain either a boltdb-shipper or tsdb index.

##### store used for managing delete requests

A new config option `-boltdb.shipper.compactor.delete-request-store` decides where delete requests should be stored. This new option takes precedence over `-boltdb.shipper.compactor.shared-store`.

In the case where neither of these options are set, the `object_store` configured in the latest `period_config` that uses either a tsdb or boltdb-shipper index is used for storing delete requests to ensure pending requests are processed.

#### logfmt parser non-strict parsing

logfmt parser now performs non-strict parsing which helps scan semi-structured log lines.
It skips invalid tokens and tries to extract as many key/value pairs as possible from the rest of the log line.
If you have a use-case that relies on strict parsing where you expect the parser to throw an error, use `| logfmt --strict` to enable strict mode.

logfmt parser doesn't include standalone keys(keys without a value) in the resulting label set anymore.
You can use `--keep-empty` flag to retain them.

### Jsonnet 2..9.0

#### Deprecated PodDisruptionBudget definition has been removed

The `policy/v1beta1` API version of PodDisruptionBudget is no longer served as of Kubernetes v1.25.
To support the latest versions of the Kubernetes, it was necessary to replace `policy/v1beta1` with the new definition `policy/v1` that is available since v1.21.

No impact is expected if you use Kubernetes v1.21 or newer.

Please refer to [official migration guide](https://kubernetes.io/docs/reference/using-api/deprecation-guide/#poddisruptionbudget-v125) for more details.

## 2.8.0

### Loki 2.8.0

#### Change in LogQL behavior

When there are duplicate labels in a log line, only the first value will be kept. Previously only the last value
was kept.

#### Default retention_period has changed

This change will affect you if you have:

```yaml
compactor:
  retention_enabled: true
```

And did *not* define a `retention_period` in `limits_config`, thus relying on the previous default of `744h`

In this release the default has been changed to `0s`.

A value of `0s` is the same as "retain forever" or "disable retention".

If, **and only if**, you wish to retain the previous default of 744h, apply this config.

```yaml
limits_config:
  retention_period: 744h
```

{{< admonition type="note" >}}
In previous versions, the zero value of `0` or `0s` will result in **immediate deletion of all logs**,
only in 2.8 and forward releases does the zero value disable retention.
{{< /admonition >}}

#### metrics.go log line `subqueries` replaced with `splits` and `shards`

The metrics.go log line emitted for every query had an entry called `subqueries` which was intended to represent the amount a query was parallelized on execution.

In the current form it only displayed the count of subqueries generated with the Loki split by time logic and did not include counts for shards.

There wasn't a clean way to update subqueries to include sharding information and there is value in knowing the difference between the subqueries generated when we split by time vs sharding factors, especially now that TSDB can do dynamic sharding.

In 2.8 we no longer include `subqueries` in metrics.go, it does still exist in the statistics API data returned but just for backwards compatibility, the value will always be zero now.

Instead, now you can use `splits` to see how many split by time intervals were created and `shards` to see the total number of shards created for a query.

{{< admonition type="note" >}}
Currently not every query can be sharded and a shards value of zero is a good indicator the query was not able to be sharded.
{{< /admonition >}}

### Promtail 2.8.0

#### The go build tag `promtail_journal_enabled` was introduced

The go build tag `promtail_journal_enabled` should be passed to include Journal support to the promtail binary.
If you need Journal support you will need to run go build with tag `promtail_journal_enabled`:

```shell
go build --tags=promtail_journal_enabled ./clients/cmd/promtail
```

Introducing this tag aims to relieve Linux/CentOS users with CGO enabled from installing libsystemd-dev/systemd-devel libraries if they don't need Journal support.

### Ruler

#### CLI flag `ruler.wal-cleaer.period` deprecated

CLI flag `ruler.wal-cleaer.period` is now deprecated and replaced with a typo fix `ruler.wal-cleaner.period`.
The yaml configuration remains unchanged:

```yaml
ruler:
  wal_cleaner:
    period: 5s
```

### Querier

#### query-frontend Kubernetes headless service changed to load balanced service

{{< admonition type="note" >}}
This is relevant only if you are using [jsonnet for deploying Loki in Kubernetes](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/tanka/).
{{< /admonition >}}

The `query-frontend` Kubernetes service was previously headless and was used for two purposes:

* Distributing the Loki query requests amongst all the available Query Frontend pods.
* Discover IPs of Query Frontend pods from Queriers to connect as workers.

The problem here is that a headless service does not support load balancing and leaves it up to the client to balance the load.
Additionally, a load-balanced service does not let us discover the IPs of the underlying pods.

To meet both these requirements, we have made the following changes:

* Changed the existing `query-frontend` Kubernetes service from headless to load-balanced to have a fair load distribution on all the Query Frontend instances.
* Added `query-frontend-headless` to discover QF pod IPs from queriers to connect as workers.

If you are deploying Loki with Query Scheduler by setting [query_scheduler_enabled](https://github.com/grafana/loki/blob/cc4ab7487ab3cd3b07c63601b074101b0324083b/production/ksonnet/loki/config.libsonnet#L18) config to `true`, then there is nothing to do here for this change.
If you are not using Query Scheduler, then to avoid any issues on the Read path until the rollout finishes, it would be good to follow below steps:

- Create just the `query-frontend-headless` service without applying any changes to the `query-frontend` service.
- Rollout changes to `queriers`.
- Roll out the rest of the changes.

### General

#### Store & Cache Statistics

Statistics are now logged in `metrics.go` lines about how long it takes to download chunks from the store, as well as how long it takes to download chunks, index query, and result cache responses from cache.

Example (note the `*_download_time` fields):

```bash
level=info ts=2022-12-20T15:27:54.858554127Z caller=metrics.go:147 component=frontend org_id=docker latency=fast query="sum(count_over_time({job=\"generated-logs\"}[1h]))" query_type=metric range_type=range length=6h17m48.865587821s start_delta=6h17m54.858533178s end_delta=5.99294552s step=1m30s duration=5.990829396s status=200 limit=30 returned_lines=0 throughput=123MB total_bytes=738MB total_entries=1 store_chunks_download_time=2.319297059s queue_time=2m21.476090991s subqueries=8 cache_chunk_req=81143 cache_chunk_hit=32390 cache_chunk_bytes_stored=1874098 cache_chunk_bytes_fetched=94289610 cache_chunk_download_time=56.96914ms cache_index_req=994 cache_index_hit=710 cache_index_download_time=1.587842ms cache_result_req=7 cache_result_hit=0 cache_result_download_time=380.555µs
```

These statistics are also displayed when using `--stats` with LogCLI.

## 2.7.0

### Loki 2.7.0

### Loki Canary Permission

The new `push` mode to [Loki canary](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/loki-canary/) can push logs that are generated by a Loki canary directly to a given Loki URL. Previously, it only wrote to a local file and you needed some agent, such as promtail, to scrape and push it to Loki.
So if you run Loki behind some proxy with different authorization policies to read and write to Loki, then auth credentials we pass to Loki canary now needs to have both `READ` and `WRITE` permissions.

### `engine.timeout` and `querier.query_timeout` are deprecated

Previously, we had two configurations to define a query timeout: `engine.timeout` and `querier.query-timeout`.
As they were conflicting and `engine.timeout` isn't as expressive as `querier.query-timeout`,
we're deprecating it and moving it to [Limits Config](https://grafana.com/docs/loki/<LOKI_VERSION>/configuration/#limits_config) `limits_config.query_timeout` with same default values.

#### `fifocache` has been renamed

The in-memory `fifocache` has been renamed to `embedded-cache`. This allows us to replace the implementation (currently a simple FIFO data structure) with something else in the future without causing confusion

#### Evenly spread Memcached pods for chunks across kubernetes nodes

We now evenly spread memcached_chunks pods across the available kubernetes nodes, but allowing more than one pod to be scheduled into the same node.
If you want to run at most a single pod per node, set `$.memcached.memcached_chunks.use_topology_spread` to false.

While we attempt to schedule at most 1 memcached_chunks pod per Kubernetes node with the `topology_spread_max_skew: 1` field,
if no more nodes are available then multiple pods will be scheduled on the same node.
This can potentially impact your service's reliability so consider tuning these values according to your risk tolerance.

#### Evenly spread distributors across kubernetes nodes

We now evenly spread distributors across the available kubernetes nodes, but allowing more than one distributors to be scheduled into the same node.
If you want to run at most a single distributors per node, set `$._config.distributors.use_topology_spread` to false.

While we attempt to schedule at most 1 distributor per Kubernetes node with the `topology_spread_max_skew: 1` field,
if no more nodes are available then multiple distributors will be scheduled on the same node.
This can potentially impact your service's reliability so consider tuning these values according to your risk tolerance.

#### Evenly spread queriers across kubernetes nodes

We now evenly spread queriers across the available kubernetes nodes, but allowing more than one querier to be scheduled into the same node.
If you want to run at most a single querier per node, set `$._config.querier.use_topology_spread` to false.

While we attempt to schedule at most 1 querier per Kubernetes node with the `topology_spread_max_skew: 1` field,
if no more nodes are available then multiple queriers will be scheduled on the same node.
This can potentially impact your service's reliability so consider tuning these values according to your risk tolerance.

#### Default value for `server.http-listen-port` changed

This value now defaults to 3100, so the Loki process doesn't require special privileges. Previously, it had been set to port 80, which is a privileged port. If you need Loki to listen on port 80, you can set it back to the previous default using `-server.http-listen-port=80`.

#### docker-compose setup has been updated

The docker-compose [setup](https://github.com/grafana/loki/blob/main/production/docker) has been updated to **v2.6.0** and includes many improvements.

Notable changes include:

- authentication (multi-tenancy) is **enabled** by default; you can disable it in `production/docker/config/loki.yaml` by setting `auth_enabled: false`
- storage is now using Minio instead of local filesystem
  - move your current storage into `.data/minio` and it should work transparently
- log-generator was added - if you don't need it, simply remove the service from `docker-compose.yaml` or don't start the service

#### Configuration for deletes has changed

The global `deletion_mode` option in the compactor configuration moved to runtime configurations.

- The `deletion_mode` option needs to be removed from your compactor configuration
- The `deletion_mode` global override needs to be set to the desired mode: `disabled`, `filter-only`, or `filter-and-delete`. By default, `filter-and-delete` is enabled.
- Any `allow_delete` per-tenant overrides need to be removed or changed to `deletion_mode` overrides with the desired mode.

#### Metric name for `loki_log_messages_total` changed

The name of this metric was changed to `loki_internal_log_messages_total` to reduce ambiguity. The previous name is still present but is deprecated.

#### Usage Report  / Telemetry config has changed named

The configuration for anonymous usage statistics reporting to Grafana has changed from `usage_report` to `analytics`.

#### TLS `cipher_suites` and `tls_min_version` have moved

These were previously configurable under `server.http_tls_config` and `server.grpc_tls_config` separately. They are now under `server.tls_cipher_suites` and `server.tls_min_version`. These values are also now configurable for individual clients, for example: `distributor.ring.etcd` or `querier.ingester_client.grpc_client_config`.

#### `ruler.storage.configdb` has been removed

ConfigDB was disallowed as a Ruler storage option back in 2.0. The config struct has finally been removed.

#### `ruler.remote_write.client` has been removed

Can no longer specify a remote write client for the ruler.

### Promtail 2.7.0

#### `gcp_push_target_parsing_errors_total` has a new `reason` label

The `gcp_push_target_parsing_errors_total` GCP Push Target metrics has been added a new label named `reason`. This includes detail on what might have caused the parsing to fail.

#### Windows event logs: now correctly includes `user_data`

The contents of the `user_data` field was erroneously set to the same value as `event_data` in previous versions. This was fixed in [#7461](https://github.com/grafana/loki/pull/7461) and log queries relying on this broken behavior may be impacted.

## 2.6.0

### Loki 2.6.0

#### Implementation of unwrapped `rate` aggregation changed

The implementation of the `rate()` aggregation function changed back to the previous implementation prior to [#5013](https://github.com/grafana/loki/pulls/5013).
This means that the rate per second is calculated based on the sum of the extracted values, instead of the average increase over time.

If you want the extracted values to be treated as [Counter](https://prometheus.io/docs/concepts/metric_types/#counter) metric, you should use the new `rate_counter()` aggregation function, which calculates the per-second average rate of increase of the vector.

#### Default value for `azure.container-name` changed

This value now defaults to `loki`, it was previously set to `cortex`. If you are relying on this container name for your chunks or ruler storage, you will have to manually specify `-azure.container-name=cortex` or `-ruler.storage.azure.container-name=cortex` respectively.

## 2.5.0

### Loki 2.5.0

#### `split_queries_by_interval` yaml configuration has moved

It was previously possible to define this value in two places

```yaml
query_range:
  split_queries_by_interval: 10m
```

and/or

```yaml
limits_config:
  split_queries_by_interval: 10m
```

In 2.5.0 it can only be defined in the `limits_config` section, **Loki will fail to start if you do not remove the `split_queries_by_interval` config from the `query_range` section.**

Additionally, it has a new default value of `30m` rather than `0`.

The CLI flag is not changed and remains `querier.split-queries-by-interval`.

#### Dropped support for old Prometheus rules configuration format

Alerting rules previously could be specified in two formats: 1.x format (legacy one, named `v0` internally) and 2.x.
We decided to drop support for format `1.x` as it is fairly old and keeping support for it required a lot of code.

In case you're still using the legacy format, take a look at
[Alerting Rules](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/) for instructions
on how to write alerting rules in the new format.

For reference, the newer format follows a structure similar to the one below:

```yaml
 groups:
 - name: example
   rules:
   - alert: HighErrorRate
     expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5
     for: 10m
     labels:
       severity: page
     annotations:
       summary: High request latency
```

Meanwhile, the legacy format is a string in the following format:

```
 ALERT <alert name>
   IF <expression>
   [ FOR <duration> ]
   [ LABELS <label set> ]
   [ ANNOTATIONS <label set> ]
```

#### Changes to default configuration values

* `parallelize_shardable_queries` under the `query_range` config now defaults to `true`.
* `split_queries_by_interval` under the `limits_config` config now defaults to `30m`, it was `0s`.
* `max_chunk_age` in the `ingester` config now defaults to `2h` previously it was `1h`.
* `query_ingesters_within` under the `querier` config now defaults to `3h`, previously it was `0s`. Any query (or subquery) that has an end time more than `3h` ago will not be sent to the ingesters, this saves work on the ingesters for data they normally don't contain. If you regularly write old data to Loki you may need to return this value to `0s` to always query ingesters.
* `max_concurrent` under the `querier` config now defaults to `10` instead of `20`.
* `match_max_concurrent` under the `frontend_worker` config now defaults to true, this supersedes the `parallelism` setting which can now be removed from your config. Controlling query parallelism of a single process can now be done with the `querier` `max_concurrent` setting.
* `flush_op_timeout` under the `ingester` configuration block now defaults to `10m`, increased from `10s`. This can help when replaying a large WAL on Loki startup, and avoid `msg="failed to flush" ... context deadline exceeded` errors.

### Promtail 2.5.0

#### `gcplog` labels have changed

- Resource labels have been moved from `__<NAME>` to `__gcp_resource_labels_<NAME>` for example, if you previously used `__project_id` then you'll need to update your relabel config to use `__gcp_resource_labels_project_id`.
- `resource_type` has been moved to `__gcp_resource_type`

#### `promtail_log_entries_bytes_bucket` histogram has been removed

This histogram reports the distribution of log line sizes by file. It has 8 buckets for every file being tailed.

This creates a lot of series and we don't think this metric has enough value to offset the amount of series generated so we are removing it.

#### `added Docker target` log message has been demoted from level=error to level=info

If you have dashboards that depended on the log level, change them to search for the `msg="added
Docker target"` property.

### Jsonnet 2.5.0

#### Compactor config defined as command line args moved to yaml config

Following 2 compactor configs that were defined as command line arguments in jsonnet are now moved to yaml config:

```yaml
# Directory where files can be downloaded for compaction.
# CLI flag: -boltdb.shipper.compactor.working-directory
[working_directory: <string>]

# The shared store used for storing boltdb files.
# Supported types: gcs, s3, azure, swift, cos, filesystem.
# CLI flag: -boltdb.shipper.compactor.shared-store
[shared_store: <string>]
```

## 2.4.0

The following are important changes which should be reviewed and understood prior to upgrading Loki.

### Loki 2.4.0

The following changes pertain to upgrading Loki.

#### The single binary no longer runs a table-manager

Single binary Loki means running loki with `-target=all` which is the default if no `-target` flag is passed.

This will impact anyone in the following scenarios:

1. Running a single binary Loki with any index type other than `boltdb-shipper` or `boltdb`
2. Relying on retention with the configs `retention_deletes_enabled` and `retention_period`

Anyone in situation #1 who is not using `boltdb-shipper` or `boltdb` (e.g. `cassandra` or `bigtable`) should modify their Loki command to include `-target=all,table-manager` this will instruct Loki to run a table-manager for you.

Anyone in situation #2, you have two options, the first (and not recommended) is to run Loki with a table-manager by adding `-target=all,table-manager`.

The second and recommended solution, is to use deletes via the compactor:

```yaml
compactor:
  retention_enabled: true
limits_config:
  retention_period: [30d]
```

See the [retention docs](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/retention/) for more info.

#### Log messages on startup: proto: duplicate proto type registered

PR [#3842](https://github.com/grafana/loki/pull/3842) **cyriltovena**: Fork cortex chunk storage into Loki.

Since Cortex doesn't plan to use the `chunk` package anymore, we decided to fork it into our storage package to
be able to evolve and modify it easily. However, as a side-effect, we still vendor Cortex which includes this forked
code and protobuf files resulting in log messages like these at startup:

```bash
2021-11-04 15:30:02.437911 I | proto: duplicate proto type registered: purgeplan.DeletePlan
2021-11-04 15:30:02.437936 I | proto: duplicate proto type registered: purgeplan.ChunksGroup
2021-11-04 15:30:02.437939 I | proto: duplicate proto type registered: purgeplan.ChunkDetails
...
```

The messages are harmless and we will work to remove them in the future.

#### Change of some default limits to common values

PR [4415](https://github.com/grafana/loki/pull/4415) **DylanGuedes**: the default value of some limits were changed to protect users from overwhelming their cluster with ingestion load caused by relying on default configs.

We suggest you double check if the following parameters are
present in your Loki config: `ingestion_rate_strategy`, `max_global_streams_per_user`
`max_query_length` `max_query_parallelism` `max_streams_per_user`
`reject_old_samples` `reject_old_samples_max_age`. If they are not present, we recommend you double check that the new values will not negatively impact your system. The changes are:

| config | new default | old default |
| --- | --- | --- |
| ingestion_rate_strategy | "global" | "local" |
| max_global_streams_per_user | 5000 | 0 (no limit) |
| max_query_length | "721h" | "0h" (no limit) |
| max_query_parallelism | 32 | 14 |
| max_streams_per_user | 0 (no limit) | 10000 |
| reject_old_samples | true | false |
| reject_old_samples_max_age | "168h" | "336h" |
| per_stream_rate_limit | 3MB | - |
| per_stream_rate_limit_burst | 15MB | - |

#### Change of configuration defaults

| config | new default | old default |
| --- | --- | --- |
| chunk_retain_period | 0s | 30s |
| chunk_idle_period | 30m | 1h |
| chunk_target_size | 1572864 | 1048576 |

* chunk_retain_period is necessary when using an index queries cache which is not enabled by default. If you have configured an index_queries_cache_config section make sure that you set chunk_retain_period larger than your cache TTL
* chunk_idle_period is how long before a chunk which receives no logs is flushed.
* chunk_target_size was increased to flush slightly larger chunks, if using memcache for a chunks store make sure it will accept files up to 1.5MB in size.

#### In memory FIFO caches enabled by default

Loki now enables a results cache and chunks cache in memory to improve performance. This can however increase memory usage as the cache's by default are allowed to consume up to 1GB of memory.

If you would like to disable these caches or change this memory limit:

Disable:

```yaml
chunk_store_config:
  chunk_cache_config:
    enable_fifocache: false
query_range:
  results_cache:
    cache:
      enable_fifocache: false
```

Resize:

```yaml
chunk_store_config:
  chunk_cache_config:
    enable_fifocache: true
    fifocache:
      max_size_bytes: 500MB
query_range:
  results_cache:
    cache:
      enable_fifocache: true
      fifocache:
        max_size_bytes: 500MB
```

#### Ingester Lifecycler `final_sleep` now defaults to `0s`

* [4608](https://github.com/grafana/loki/pull/4608) **trevorwhitney**: Change default value of ingester lifecycler's `final_sleep` from `30s` to `0s`

This final sleep exists to keep Loki running for long enough to get one final Prometheus scrape before shutting down, however it also causes Loki to sit idle for 30s on shutdown which is an annoying experience for many people.

We decided the default would be better to disable this sleep behavior but anyone can set this config variable directly to return to the previous behavior.

#### Ingester WAL now defaults to on, and chunk transfers are disabled by default

* [4543](https://github.com/grafana/loki/pull/4543) **trevorwhitney**: Change more default values and improve application of common storage config
* [4629](https://github.com/grafana/loki/pull/4629) **owen-d**: Default the WAL to enabled in the Loki jsonnet library
* [4624](https://github.com/grafana/loki/pull/4624) **chaudum**: Disable chunk transfers in jsonnet lib

This changes a few default values, resulting in the ingester WAL now being on by default,
and chunk transfer retries are disabled by default. Note, this now means Loki will depend on local disk by default for its WAL (write ahead log) directory. This defaults to `wal` but can be overridden via the `--ingester.wal-dir` or via `path_prefix` in the common configuration section. Below are config snippets with the previous defaults, and another with the new values.

Previous defaults:

```yaml
ingester:
  max_transfer_retries: 10
  wal:
    enabled: false
```

New defaults:

```yaml
ingester:
  max_transfer_retries: 0
  wal:
    enabled: true
```

Using the write ahead log (WAL) is recommended and is now the default. However using the WAL is incompatible with chunk transfers, if you have explicitly configured `ingester.max-transfer-retries` to a non-zero value, you must set it to 0 to disable transfers.

#### Memberlist config now automatically applies to all non-configured rings

* [4400](https://github.com/grafana/loki/pull/4400) **trevorwhitney**: Config: automatically apply memberlist config too all rings when provided

This change affects the behavior of the ingester, distributor, and ruler rings. Previously, if you wanted to use memberlist for all of these rings, you
had to provide a `memberlist` configuration as well as specify `store: memberlist` for the `kvstore` of each of the rings you wanted to use memberlist.
For example, your configuration might look something like this:

```yaml
memberlist:
  join_members:
    - loki.namespace.svc.cluster.local
distributor:
  ring:
    kvstore:
      store: memberlist
ingester:
    lifecycler:
      ring:
        kvstore:
          store: memberlist
ruler:
  ring:
    kvstore:
      store: memberlist
```

Now, if your provide a `memberlist` configuration with at least one `join_members`, loki will default all rings to use a `kvstore` of type `memberlist`.
You can change this behavior by overriding specific configurations. For example, if you wanted to use `consul` for you `ruler` rings, but `memberlist`
for the `ingester` and `distributor`, you could do so with the following config (although we don't know why someone would want to do this):

```yaml
memberlist:
  join_members:
    - loki.namespace.svc.cluster.local
ruler:
  ring:
    kvstore:
      store: consul
      consul:
        host: consul.namespace.svc.cluster.local:8500
```

#### Changed defaults for some GRPC server settings

* [4435](https://github.com/grafana/loki/pull/4435) **trevorwhitney**: Change default values for two GRPC settings so querier can connect to frontend/scheduler

This changes two default values, `grpc_server_min_time_between_pings` and `grpc_server_ping_without_stream_allowed` used by the GRPC server.

*Previous Values*:

```yaml
server:
  grpc_server_min_time_between_pings: '5m'
  grpc_server_ping_without_stream_allowed: false
```

*New Values*:

```yaml
server:
  grpc_server_min_time_between_pings: '10s'
  grpc_server_ping_without_stream_allowed: true
```

[This issue](https://github.com/grafana/loki/issues/4375) has some more information on the change.

#### Some metric prefixes have changed from `cortex_` to `loki_`

* [#3842](https://github.com/grafana/loki/pull/3842)/[#4253](https://github.com/grafana/loki/pull/4253) **jordanrushing**: Metrics related to chunk storage and runtime config have changed their prefixes from `cortex_` to `loki_`.

```yaml
cortex_runtime_config* -> loki_runtime_config*
cortex_chunks_store* -> loki_chunks_store*
```

#### Recording rules storage is now durable

* [4344](https://github.com/grafana/loki/pull/4344) **dannykopping**: per-tenant WAL

Previously, samples generated by recording rules would only be buffered in memory before being remote-written to Prometheus; from this
version, the `ruler` now writes these samples to a per-tenant Write-Ahead Log for durability. More details about the
per-tenant WAL can be found [here](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/recording-rules/).

The `ruler` now requires persistent storage - see the
[Operations](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/recording-rules/#deployment) page for more details about deployment.

### Promtail 2.4.0

The following changes pertain to upgrading Promtail.

#### Promtail no longer insert `promtail_instance` label when scraping `gcplog` target

* [4556](https://github.com/grafana/loki/pull/4556) **james-callahan**: Remove `promtail_instance` label that was being added by promtail when scraping `gcplog` target.

## 2.3.0

### Loki 2.3.0

#### Query restriction introduced for queries which do not have at least one equality matcher

PR [3216](https://github.com/grafana/loki/pull/3216) **sandeepsukhani**: check for stream selectors to have at least one equality matcher

This change now rejects any query which does not contain at least one equality matcher, an example may better illustrate:

`{namespace=~".*"}`

This query will now be rejected, however there are several ways to modify it for it to succeed:

Add at least one equals label matcher:

`{cluster="us-east-1",namespace=~".*"}`

Use `.+` instead of `.*`

`{namespace=~".+"}`

This difference may seem subtle but if we break it down `.` matches any character, `*` matches zero or more of the preceding character and `+` matches one or more of the preceding character. The `.*` case will match empty values where `.+` will not, this is the important difference. `{namespace=""}` is an invalid request (unless you add another equals label matcher like the example above)

The reasoning for this change has to do with how index lookups work in Loki, if you don't have at least one equality matcher Loki has to perform a complete index table scan which is an expensive and slow operation.

## 2.2.0

### Loki 2.2.0

**Be sure to upgrade to 2.0 or 2.1 BEFORE upgrading to 2.2**

In Loki 2.2 we changed the internal version of our chunk format from v2 to v3, this is a transparent change and is only relevant if you every try to _downgrade_ a Loki installation. We incorporated the code to read v3 chunks in 2.0.1 and 2.1, as well as 2.2 and any future releases.

**If you upgrade to 2.2+ any chunks created can only be read by 2.0.1, 2.1 and 2.2+**

This makes it important to first upgrade to 2.0, 2.0.1, or 2.1 **before** upgrading to 2.2 so that if you need to rollback for any reason you can do so easily.

{{< admonition type="note" >}}
2.0 and 2.0.1 are identical in every aspect except 2.0.1 contains the code necessary to read the v3 chunk format. Therefor if you are on 2.0 and upgrade to 2.2, if you want to rollback, you must rollback to 2.0.1.
{{< /admonition >}}

### Loki Config

**Read this if you use the query-frontend and have `sharded_queries_enabled: true`**

We discovered query scheduling related to sharded queries over long time ranges could lead to unfair work scheduling by one single query in the per tenant work queue.

The `max_query_parallelism` setting is designed to limit how many split and sharded units of 'work' for a single query are allowed to be put into the per tenant work queue at one time. The previous behavior would split the query by time using the `split_queries_by_interval` and compare this value to `max_query_parallelism` when filling the queue, however with sharding enabled, every split was then sharded into 16 additional units of work after the `max_query_parallelism` limit was applied.

In 2.2 we changed this behavior to apply the `max_query_parallelism` after splitting _and_ sharding a query resulting a more fair and expected queue scheduling per query.

**What this means** Loki will be putting much less work into the work queue per query if you are using the query frontend and have sharding_queries_enabled (which you should).  **You may need to increase your `max_query_parallelism` setting if you are noticing slower query performance** In practice, you may not see a difference unless you were running a cluster with a LOT of queriers or queriers with a very high `parallelism` frontend_worker setting.

You could consider multiplying your current `max_query_parallelism` setting by 16 to obtain the previous behavior, though in practice we suspect few people would really want it this high unless you have a significant querier worker pool.

**Also be aware to make sure `max_outstanding_per_tenant` is always greater than `max_query_parallelism` or large queries will automatically fail with a 429 back to the user.**

### Promtail 2.2.0

For 2.0 we eliminated the long deprecated `entry_parser` configuration in Promtail configs, however in doing so we introduced a very confusing and erroneous default behavior:

If you did not specify a `pipeline_stages` entry you would be provided with a default which included the `docker` pipeline stage.  This can lead to some very confusing results.

In [3404](https://github.com/grafana/loki/pull/3404), we corrected this behavior

**If you are using docker, and any of your `scrape_configs` are missing a `pipeline_stages` definition**, you should add the following to obtain the correct behavior:

```yaml
pipeline_stages:
  - docker: {}
```

## 2.1.0

The upgrade from 2.0.0 to 2.1.0 should be fairly smooth, be aware of these two things:

### Helm charts have moved

Helm charts are now located in the [Grafana Helm charts repo](https://github.com/grafana/helm-charts/).

The helm repo URL is now: [https://grafana.github.io/helm-charts](https://grafana.github.io/helm-charts).

### Fluent Bit plugin renamed

Fluent bit officially supports Loki as an output plugin now.

However this created a naming conflict with our existing output plugin (the new native output uses the name `loki`) so we have renamed our plugin.

In time our plan is to deprecate and eliminate our output plugin in favor of the native Loki support. However until then you can continue using the plugin with the following change:

Old:

```shell
[Output]
    Name loki
```

New:

```shell
[Output]
    Name grafana-loki
```

## 2.0.0

This is a major Loki release and there are some very important upgrade considerations.
For the most part, there are very few impactful changes and for most this will be a seamless upgrade.

2.0.0 Upgrade Topics:

* [IMPORTANT If you are using a docker image, read this!](#important-if-you-are-using-a-docker-image-read-this)
* [IMPORTANT boltdb-shipper upgrade considerations](#important-boltdb-shipper-upgrade-considerations)
* [IMPORTANT results_cachemax_freshness removed from yaml config](#important-results_cachemax_freshness-removed-from-yaml-config)
* [Promtail removed entry_parser config](#promtail-config-removed)
* [If you would like to use the new single store index and v11 schema](#upgrading-schema-to-use-boltdb-shipper-andor-v11-schema)

### **IMPORTANT If you are using a docker image, read this!**

(This includes, Helm, Tanka, docker-compose etc.)

The default config file in the docker image, as well as the default helm values.yaml and jsonnet for Tanka all specify a schema definition to make things easier to get started.

{{< admonition type="caution" >}}
If you have not specified your own config file with your own schema definition (or you do not have a custom schema definition in your values.yaml), upgrading to 2.0 will break things!
{{< /admonition >}}

In 2.0 the defaults are now v11 schema and the `boltdb-shipper` index type.

If you are using an index type of `aws`, `bigtable`, or `cassandra` this means you have already defined a custom schema and there is _nothing_ further you need to do regarding the schema.
You can consider however adding a new schema entry to use the new `boltdb-shipper` type if you want to move away from these separate index stores and instead use just one object store.

#### What to do

The minimum action required is to create a config which specifies the schema to match what the previous defaults were.

(Keep in mind this will only tell Loki to use the old schema default, if you would like to upgrade to v11 and/or move to the single store boltdb-shipper, [see below](#upgrading-schema-to-use-boltdb-shipper-andor-v11-schema))

There are three places we have hard coded the schema definition:

##### Helm

Helm has shipped with the same internal schema in the values.yaml file for a very long time.

If you are providing your own values.yaml file then there is no _required_ action because you already have a schema definition.

**If you are not providing your own values.yaml file, you will need to make one!**

We suggest using the included [values.yaml file from the 1.6.0 tag](https://raw.githubusercontent.com/grafana/loki/v1.6.0/production/helm/loki/values.yaml)

This matches what the default values.yaml file had prior to 2.0 and is necessary for Loki to work post 2.0

As mentioned above, you should also consider looking at moving to the v11 schema and boltdb-shipper [see below](#upgrading-schema-to-use-boltdb-shipper-andor-v11-schema) for more information.

##### Tanka

This likely only affects a small portion of tanka users because the default schema config for Loki was forcing `GCS` and `bigtable`.

**If your `main.jsonnet` (or somewhere in your manually created jsonnet) does not have a schema config section then you will need to add one like this!**

```jsonnet
{
  _config+:: {
    using_boltdb_shipper: false,
    loki+: {
      schema_config+: {
        configs: [{
          from: '2018-04-15',
          store: 'bigtable',
          object_store: 'gcs',
          schema: 'v11',
          index: {
            prefix: '%s_index_' % $._config.table_prefix,
            period: '168h',
          },
        }],
      },
    },
  }
}
```

{{< admonition type="note" >}}
If you had set `index_period_hours` to a value other than 168h (the previous default) you must update this in the above config `period:` to match what you chose.
{{< /admonition >}}

{{< admonition type="note" >}}
We have changed the default index store to `boltdb-shipper` it's important to add `using_boltdb_shipper: false,` until you are ready to change (if you want to change)
{{< /admonition >}}

Changing the jsonnet config to use the `boltdb-shipper` type is the same as [below](#upgrading-schema-to-use-boltdb-shipper-andor-v11-schema) where you need to add a new schema section.

**HOWEVER** Be aware when you change `using_boltdb_shipper: true` the deployment type for the ingesters and queriers will change to StatefulSets! StatefulSets are required for the ingester and querier using boltdb-shipper.

##### Docker (e.g. docker-compose)

For docker related cases you will have to mount a Loki config file separate from what's shipped inside the container

I would recommend taking the previous default file from the [1.6.0 tag on github](https://raw.githubusercontent.com/grafana/loki/v1.6.0/cmd/loki/loki-docker-config.yaml)

How you get this mounted and in use by Loki might vary based on how you are using the image, but this is a common example:

```shell
docker run -d --name=loki --mount type=bind,source="path to loki-config.yaml",target=/etc/loki/local-config.yaml
```

The Loki docker image is expecting to find the config file at `/etc/loki/local-config.yaml`

### IMPORTANT: boltdb-shipper upgrade considerations

Significant changes have taken place between 1.6.0 and 2.0.0 for boltdb-shipper index type, if you are already running this index and are upgrading some extra caution is warranted.

Take a complete backup of the `index` directory in your object store, this location might be slightly different depending on what store you use.
It should be a folder named index with a bunch of folders inside with names like `index_18561`,`index_18560`...

The chunks directory should not need any special backups.

If you have an environment to test this in, do so before upgrading against critical data.

There are 2 significant changes warranting the backup of this data because they will make rolling back impossible:

* A compactor is included which will take existing index files and compact them to one per day and remove non compacted files
* All index files are now gzipped before uploading

The second part is important because 1.6.0 does not understand how to read the gzipped files, so any new files uploaded or any files compacted become unreadable to 1.6.0 or earlier.

_THIS BEING SAID_ we are not expecting problems, our testing so far has not uncovered any problems, but some extra precaution might save data loss in unforeseen circumstances!

Report any problems via GitHub issues or reach us on the #loki slack channel.

{{< admonition type="note" >}}
If are using boltdb-shipper and were running with high availability and separate filesystems, this was a poorly documented and even more experimental mode we toyed with using boltdb-shipper. For now we removed the documentation and also any kind of support for this mode.
{{< /admonition >}}

To use boltdb-shipper in 2.0 you need a shared storage (S3, GCS, etc), the mode of running with separate filesystem stores in HA using a ring is not officially supported.

We didn't do anything explicitly to limit this functionality however we have not had any time to actually test this which is why we removed the docs and are listing it as not supported.

#### If running in microservices, deploy ingesters before queriers

Ingesters now expose a new RPC method that queriers use when the index type is `boltdb-shipper`.
Queriers generally roll out faster than ingesters, so if new queriers query older ingesters using the new RPC, the queries would fail.
To avoid any query downtime during the upgrade, rollout ingesters before queriers.

#### If running the compactor, ensure it has delete permissions for the object storage

The compactor is an optional but suggested component that combines and deduplicates the boltdb-shipper index files. When compacting index files, the compactor writes a new file and deletes unoptimized files. Ensure that the compactor has appropriate permissions for deleting files, for example, s3:DeleteObject permission for AWS S3.

### IMPORTANT: `results_cache.max_freshness` removed from YAML config

The `max_freshness` config from `results_cache` has been removed in favour of another flag called `max_cache_freshness_per_query` in `limits_config` which has the same effect.
If you happen to have `results_cache.max_freshness` set, use `limits_config.max_cache_freshness_per_query` YAML config instead.

### Promtail config removed

The long deprecated `entry_parser` config in Promtail has been removed.

### Upgrading schema to use boltdb-shipper and/or v11 schema

If you would also like to take advantage of the new Single Store (boltdb-shipper) index, as well as the v11 schema if you aren't already using it.

You can do this by adding a new schema entry.

Here is an example:

```yaml
schema_config:
  configs:
    - from: 2018-04-15           ①
      store: boltdb              ①④
      object_store: filesystem   ①④
      schema: v11                ②
      index:
        prefix: index_           ①
        period: 168h             ①
    - from: 2020-10-24           ③
      store: boltdb-shipper
      object_store: filesystem   ④
      schema: v11
      index:
        prefix: index_
        period: 24h              ⑤
```

① Make sure all of these match your current schema config
② Make sure this matches your previous schema version, Helm for example is likely v9
③ Make sure this is a date in the **FUTURE** keep in mind Loki only knows UTC so make sure it's a future UTC date
④ Make sure this matches your existing config (e.g. maybe you were using gcs for your object_store)
⑤ 24h is required for boltdb-shipper

There are more examples on the [Storage description page](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/storage/#examples) including the information you need to setup the `storage` section for boltdb-shipper.

# Upgrade the Helm chart to 3.0

v3.x represents a major milestone for this chart, showing a commitment by the Loki team to provide a better supported, scalable helm chart.
In addition to moving the source code for this helm chart into the Loki repo itself, it also combines what were previously two separate charts,
[`grafana/loki`](https://github.com/grafana/helm-charts/tree/main/charts/loki) and [`grafana/loki-simple-scalable`](https://github.com/grafana/helm-charts/tree/main/charts/loki-simple-scalable) into one chart. This chart will automatically assume the "Single Binary" mode previously deployed by the `grafana/loki` chart if you are using a filesystem backend, and will assume the "Scalable" mode previously deployed by the `grafana/loki-simple-scalable` chart if you are using an object storage backend.

As a result of this major change, upgrades from the charts this replaces might be difficult. We are attempting to support the 3 most common upgrade paths.

  1. Upgrade from `grafana/loki` using local `filesystem` storage
  1. Upgrade from `grafana/loki-simple-scalable` using a cloud based object storage such as S3 or GCS, or an api compatible equivalent like MinIO.

## Upgrading from `grafana/loki`

The default installation of `grafana/loki` is a single instance backed by `filesystem` storage that is not highly available. As a result, this upgrade method will involve downtime. The upgrade will involve deleting the previously deployed loki stateful set, the running the `helm upgrade` which will create the new one with the same name, which should attach to the existing PVC or ephemeral storage, thus preserving you data. We still highly recommend backing up all data before conducting the upgrade.

To upgrade, you will need at least the following in your `values.yaml`:

```yaml
loki:
  commonConfig:
    replication_factor: 1
  storage:
    type: 'filesystem'
```

You will need to 1. Update the grafana helm repo, 2. delete the existing stateful set, and 3. upgrade making sure to have the values above included in your `values.yaml`. If you installed `grafana/loki` as `loki` in namespace `loki`, the commands would be:

```console
helm repo update grafana
kubectl -n loki delete statefulsets.apps loki
helm upgrade loki grafana/loki \
  --values values.yaml \
  --namespace loki
```

You will need to manually delete the existing stateful set for the above command to work.

### Notable changes

The `grafana/loki` chart used `Secret` as storage for configuration.  You can set `.loki.existingSecretForConfig` to continue using `Secret` or migrate your configuration to a `ConfigMap`. Specifying the Loki config in `values.yaml` is still available. In the old chart it was under `.config`, the new chart allows specifying either `.loki.config` or `.loki.structuredConfig` which takes precedence.

Similarly when using `extraVolumes`, the configuration is now nested under `.singleBinary.extraVolumes` or `.read.extraVolumes` + `.write.extraVolumes` if you decide to migrate to the Loki scalable deployment mode.

### Dependencies

The `grafana/loki` chart was only used to install Loki. New charts since `v3.x` also bundle two dependencies - **minio** and **grafana-agent-operator**. If you have already installed either of these independently and wish to continue managing them separately, you can explicitly disable these dependencies in your `values.yaml` as shown in the following examples:

```yaml
minio:
  enabled: false
```

```yaml
monitoring:
  selfMonitoring:
    enabled: false
    grafanaAgent:
      installOperator: false
```

## Upgrading from `grafana/loki-simple-scalable`

As this chart is largely based off the `grafana/loki-simple-scalable` chart, you should be able to use your existing `values.yaml` file and just upgrade to the new chart name. For example, if you installed the `grafana/loki-simple-scalable` chart as `loki` in the namespace `loki`, your upgrade would be:

```console
helm repo update grafana
helm upgrade loki grafana/loki \
  --values values.yaml \
  --namespace loki
```

# Upgrade the Helm chart to 6.0

v6.x of this chart introduces distributed mode but also introduces breaking changes from v5x.

{{< admonition type="caution" >}}
If you have not yet [migrated to TSDB](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/migrate/migrate-to-tsdb/), perform that migration before you upgrade your Loki Helm chart to v6.x.
{{< /admonition >}}

{{< admonition type="note" >}}
Simple Scalable Deployment (SSD) mode is being deprecated and will be removed with the Loki 4.0 release. When you upgrade to Loki 3.x, you should choose either monolithic (single-binary) or microservices (distributed) as your [deployment mode](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/). You will not be able to run Loki 4.0 in SSD mode.
{{< /admonition >}}

{{< admonition type="tip" >}}
With the move to the [Grafana-community/helm-charts repository](https://github.com/grafana-community/helm-charts), the chart URL and numbering has changed. Major version updates signal breaking changes in the chart. For more information, refer to the [README](https://github.com/grafana-community/helm-charts/blob/main/charts/loki/README.md#upgrading).
{{< /admonition >}}

## Changes

The location of the Loki Helm chart has changed from https://grafana.github.io/helm-charts to https://grafana-community.github.io/helm-charts.

### BREAKING: `deploymentMode` setting

This only breaks you if you are running the chart in Single Binary mode, you will need to set

```yaml
deploymentMode: SingleBinary
```

### BREAKING: `lokiCanary` section was moved

This section was moved from within the `monitoring` section to the root level of the values file.

### BREAKING: `topologySpreadConstraints` and `podAffinity` converted to objects

Previously they were strings which were passed through `tpl` now they are normal objects which will be added to deployments.

Also we removed the soft constraint on zone.

### BREAKING: `externalConfigSecretName` was removed and replaced

Instead you can now provide `configObjectName` which is used by Loki components for loading the config.

`generatedConfigObjectName` also can be used to control the name of the config object created by the chart.

This gives greater flexibility in using the chart to still generate a config object but allowing for another process to load and mutate this config into a new object which can be loaded by Loki and `configObjectName`

### Monitoring

After some consideration of how this chart works with other charts provided by Grafana, we decided to deprecate the monitoring sections of this chart and take a new approach entirely to monitoring Loki, Mimir and Tempo with the [Meta Monitoring Chart](https://github.com/grafana/meta-monitoring-chart).

Reasons:

* There were conflicts with this chart and the Mimir chart both installing the Agent Operator.
* The Agent Operator is deprecated.
* The dependency on the Prometheus operator is not one we are able to support well.

The [Meta Monitoring Chart](https://github.com/grafana/meta-monitoring-chart) is an improvement over the previous approach because it allows for installing a clustered Grafana Agent which can send metrics, logs, and traces to Grafana Cloud, or letting you install a monitoring-only local installation of Loki, Mimir, Tempo, and Grafana.

The monitoring sections of this chart still exist but are disabled by default.

If you wish to continue using the self monitoring features you should use the following configuration, but please do note a future version of this chart will remove this capability completely:

```yaml
monitoring:
  enabled: true
  selfMonitoring:
    enabled: true
    grafanaAgent:
      installOperator: true
```

### Memcached is included and enabled by default

Caching is crucial to the proper operation of Loki and Memcached is now included in this chart and enabled by default for the `chunksCache` and `resultsCache`.

If you are already running Memcached separately you can remove your existing installation and use the Memcached deployments built into this chart.

#### Single Binary

Memcached also deploys for the Single Binary, but this may not be desired in resource constrained environments.

You can disable it with the following configuration:

```yaml
chunksCache:
  enabled: false
resultsCache:
  enabled: false
```

With these caches disabled, Loki will return to defaults which enables an in-memory results and chunks cache, so you will still get some caching.

### BREAKING: Zone-aware ingester StatefulSet serviceName fix (6.34.0+)

**Affected users**: Only deployments using zone-aware ingester replication (`ingester.zoneAwareReplication.enabled: true`)

In Helm chart version 6.34.0, [PR #18558](https://github.com/grafana/loki/pull/18558) fixed the `serviceName` field in zone-aware ingester StatefulSets to correctly reference headless services. However, since `serviceName` is an immutable field in Kubernetes StatefulSets, upgrading to 6.34.0 requires manual intervention.

**Required action before upgrading to 6.34.0**:

1. **Check if you're affected**:

   ```bash
   helm get values <RELEASE_NAME> | grep -A5 zoneAwareReplication
   ```

   If `enabled: true` appears, you need to follow these steps.

2. **Delete the StatefulSets** (data will be preserved):

   ```bash
   kubectl delete statefulset \
     <RELEASE_NAME>-ingester-zone-a \
     <RELEASE_NAME>-ingester-zone-b \
     <RELEASE_NAME>-ingester-zone-c \
     --cascade=orphan
   ```

3. **Proceed with the Helm upgrade**:

   ```bash
   helm upgrade <RELEASE_NAME> grafana/loki --version 6.34.0
   ```

**What happens**:

- PersistentVolumeClaims and data are preserved
- New StatefulSets will be created with correct service references
- Pods will restart and reattach to existing storage

**Why this change was necessary**:
The previous configuration caused ingester scaling operations to fail because the rollout-operator couldn't find the correct headless services for the `/ingester/prepare-downscale` endpoint.

### BREAKING: Make access modes for persistence on all PVCs and StatefulSets editable (6.38.0+)

Version 6.38.0 of the Helm charts introduced the ability to edit the access modes for persistence on all PVCs and StatefulSets. This is a breaking change because it requires users to manually orphan StatefulSets before upgrading.

**Required action before upgrading to 6.38.0**:

1. **Delete the StatefulSets** (delete only the ones that exist in your deployment):

   ```bash
   # Core components (SimpleScalable mode)
   kubectl delete statefulset <RELEASE_NAME>-write --cascade=orphan
   kubectl delete statefulset <RELEASE_NAME>-backend --cascade=orphan
   
   # Single binary mode
   kubectl delete statefulset <RELEASE_NAME> --cascade=orphan
   
   # Distributed mode components
   kubectl delete statefulset <RELEASE_NAME>-ingester --cascade=orphan

   # Zone-aware ingester (if zoneAwareReplication.enabled is true)
   kubectl delete statefulset <RELEASE_NAME>-ingester-zone-a --cascade=orphan
   kubectl delete statefulset <RELEASE_NAME>-ingester-zone-b --cascade=orphan
   kubectl delete statefulset <RELEASE_NAME>-ingester-zone-c --cascade=orphan

   kubectl delete statefulset <RELEASE_NAME>-index-gateway --cascade=orphan
   kubectl delete statefulset <RELEASE_NAME>-compactor --cascade=orphan
   kubectl delete statefulset <RELEASE_NAME>-ruler --cascade=orphan
   kubectl delete statefulset <RELEASE_NAME>-pattern-ingester --cascade=orphan
   kubectl delete statefulset <RELEASE_NAME>-bloom-planner --cascade=orphan
   kubectl delete statefulset <RELEASE_NAME>-bloom-gateway --cascade=orphan
   ```

2. **Proceed with the Helm upgrade**:

   ```bash
   helm upgrade <RELEASE_NAME> grafana/loki --version 6.38.0
   ```

### Distributed mode

This chart introduces the ability to run Loki in distributed, or [microservices mode](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/deployment-modes/#microservices-mode). For installation instructions, refer to [Install the microservices Helm chart](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/install/helm/install-microservices/).

# Upgrade from the Loki Helm chart to the Community Helm chart

The Loki Helm chart has moved from the [Loki repository](https://github.com/grafana/loki) to the [Grafana Community Helm Charts repository](https://github.com/grafana-community/helm-charts). Chart version 6.55.0 (appVersion 3.6.7) was the last release from the Loki repository. Chart version 18.7.6 (appVersion 3.7.6) is the current release from the community repository at the time this topic was published.

This guide walks you through upgrading from 6.55.0 to the current community chart version, which spans twelve major chart versions (7 through 18), each with breaking changes.

{{< admonition type="warning" >}}
Grafana Enterprise Logs (GEL) support was removed from the community chart in version 8.0.0. If you are a GEL user, **do not migrate** to the community chart. The `grafana/loki` chart remains available for GEL users.
{{< /admonition >}}

{{< admonition type="caution" >}}
This upgrade spans several major versions with accumulated breaking changes. Test the upgrade in a non-production environment before applying it to production.
{{< /admonition >}}

## Prerequisites

Before you begin:

- Kubernetes 1.25 or later is required (enforced since chart version 7.0.0).
- Helm 3.x is required.
- Back up your current Helm release values:

  ```bash
  helm get values <RELEASE_NAME> -o yaml > backup-values.yaml
  ```

- Back up any persistent volumes and object store indexes.

## Update your Helm repository

The chart is now published to a different Helm repository and Open Container Initiative (OCI) registry.  The official Helm documentation now recommends using OCI registries as they implement unified storage, distribution, and improved security.

Old location for 6.x from the Loki repository:

```bash
helm repo add grafana https://grafana.github.io/helm-charts
helm upgrade <RELEASE_NAME> grafana/loki -f values.yaml
```

New location for 7.x and later from the community repository:

```bash
helm repo add grafana-community https://grafana-community.github.io/helm-charts
helm repo update
helm upgrade <RELEASE_NAME> grafana-community/loki -f values.yaml --version 18.7.6
```

Or if you are using OCI for 7.x and later:

```bash
helm upgrade <RELEASE_NAME> oci://ghcr.io/grafana-community/helm-charts/loki -f values.yaml --version 18.7.6
```

## Update your values file for breaking changes

The following sections describe every breaking change between chart versions 6.55.0 and 18.x, grouped by the major version that introduced each change. Review each section and update your values file accordingly.

{{< admonition type="caution" >}}
Check the Loki Helm Chart [README](https://github.com/grafana-community/helm-charts/tree/main/charts/loki#upgrading) for the latest changes and upgrade information. The Grafana Community is constantly improving the charts.
{{< /admonition >}}

### 6.x to 7.0: Kubernetes API cleanup

Support for deprecated Kubernetes APIs and PodSecurityPolicy has been removed. Kubernetes 1.25 or later is now required.

Remove the following from your values file:

```yaml
rbac:
  pspEnabled: false        # remove this key
  pspAnnotations: {}       # remove this key
```

The `rbac` section in 13.x only contains:

```yaml
rbac:
  sccEnabled: false
  sccAllowHostDirVolumePlugin: false
  namespaced: false
```

| If you have   | using   | update them to |
| ------------- | ------------- | ------------- |
| custom Ingress resources | `networking.k8s.io/v1beta1` | `networking.k8s.io/v1` |
| PodDisruptionBudget resources | `policy/v1beta1` | `policy/v1` |

### 7.x to 8.0: Enterprise support removed

Enterprise support is removed in the community Helm chart.

Remove the entire `enterprise` configuration section from your chart, including any `enterprise.*` keys:

```yaml
enterprise:
  enabled: false
  version: 3.6.5
  cluster_name: null
  license:
    contents: "NOTAVALIDLICENSE"
  useExternalLicense: false
  externalLicenseName: null
  externalConfigName: ""
  gelGateway: true
```

{{< admonition type="note" >}}
The Helm Chart in the Loki repository is being maintained for GEL customers only.
{{< /admonition >}}

### 8.x to 9.0: Self-monitoring and Grafana Agent Operator removed

The `monitoring.selfMonitoring` section and the `grafana-agent-operator` subchart dependency have been removed.

Remove the `monitoring.selfMonitoring` section from your values file:

```yaml
monitoring:
  selfMonitoring:
    enabled: false
    tenant:
      name: "self-monitoring"
      password: null
      secretNamespace: '{{ include "loki.namespace" . }}'
    grafanaAgent:
      installOperator: false
      annotations: {}
      labels: {}
      enableConfigReadAPI: false
      priorityClassName: null
      resources: {}
      tolerations: []
    podLogs:
      apiVersion: monitoring.grafana.com/v1alpha1
      # ...
    logsInstance:
      # ...
```

Also remove `monitoring.serviceMonitor.metricsInstance`, if present. It referenced a Grafana Agent CRD that no longer exists.

Loki canary tenant authentication has moved from `monitoring.selfMonitoring.tenant` to the top-level `lokiCanary.tenant` section. If you were using canary tenant auth, update as follows:

Before (6.x):

```yaml
monitoring:
  selfMonitoring:
    tenant:
      name: "self-monitoring"
      password: "my-password"
```

After (13.x):

```yaml
lokiCanary:
  tenant:
    name: "self-monitoring"
    password: "my-password"
```

{{< admonition type="tip" >}}
[Grafana Alloy](https://grafana.com/docs/alloy/latest/) is the successor to Grafana Agent. If you relied on Agent-based self-monitoring, consider using Alloy for log collection.
{{< /admonition >}}

### 9.x to 10.0: Index gateway persistence restructured

The `indexGateway.persistence.inMemory` field has been replaced with `indexGateway.persistence.dataVolumeParameters` for more consistent configuration across components.

Before (6.x):

```yaml
indexGateway:
  persistence:
    enabled: false
    inMemory: false
    size: 10Gi
```

After (13.x):

```yaml
indexGateway:
  persistence:
    enabled: false
    dataVolumeParameters:
      emptyDir: {}
    size: 10Gi
```

If you were using in-memory storage (`inMemory: true`), the equivalent configuration is:

```yaml
indexGateway:
  persistence:
    dataVolumeParameters:
      emptyDir:
        medium: Memory
        sizeLimit: 10Gi
```

### 10.x to 11.0: Legacy read target removed

The `read.legacyReadTarget` option has been removed. Simple scalable deployments now always require a dedicated backend target (read, write, and backend).

Remove from your values file if present:

```yaml
read:
  legacyReadTarget: false  # remove this key
```

If you had `legacyReadTarget: true` (running the 2-target read/write mode without a backend), you must now run the 3-target mode (read, write, backend) or switch to a different deployment mode.

### 11.x to 12.0: Deployment mode renamed and default changed

The default `deploymentMode` has changed from `SimpleScalable` to `Monolithic`, and `SingleBinary` has been renamed to `Monolithic`.

Before (6.x default):

```yaml
deploymentMode: SimpleScalable
```

After (13.x default):

```yaml
deploymentMode: Monolithic
```

Action required based on your current mode:

If you are running `SimpleScalable` (the 6.x default), explicitly set it in your values file to preserve your current deployment:

```yaml
deploymentMode: SimpleScalable
```

{{< admonition type="note" >}}
`SimpleScalable` is deprecated and will be removed in Loki 4.0. Plan to migrate to `Monolithic` or `Distributed`.
{{< /admonition >}}

If you are running `SingleBinary`, update the value to the new name:

```yaml
# Before
deploymentMode: SingleBinary

# After
deploymentMode: Monolithic
```

If you are running `Distributed`, no change is needed.

### 12.x to 13.0: Ephemeral volume persistence flattened

The persistence configuration for ephemeral volumes has been flattened across all components. The nested `persistence.ephemeralDataVolume` structure has been replaced with a `persistence.type` field.

{{< admonition type="note" >}}
If you are migrating directly from 6.55.0 and have never used the `ephemeralDataVolume` configuration introduced in the community chart, this change doesn't require action on your existing values. However, be aware of the new `persistence.type` field when configuring persistence for components like the compactor or bloom-builder.
{{< /admonition >}}

If you adopted the `ephemeralDataVolume` pattern from any intermediate community chart version, update your values as follows:

Before (12.x):

```yaml
<component>:
  persistence:
    ephemeralDataVolume:
      enabled: true
      accessModes:
        - ReadWriteOnce
      size: 10Gi
      storageClass: null
```

After (13.x):

```yaml
<component>:
  persistence:
    enabled: true
    type: ephemeral
    accessModes:
      - ReadWriteOnce
    size: 10Gi
    storageClass: null
```

The `type` field accepts `pvc` (default for most components) or `ephemeral`. Fields that were nested under `ephemeralDataVolume` (`accessModes`, `size`, `storageClass`, `volumeAttributesClassName`, `selector`, `annotations`, `labels`) now sit directly under `persistence`.

### 13.x to 14.0: Image registry always applied

The dot-based registry format has been removed. Previously, if the `repository` value contained a dot (`.`) in its first path segment, the chart assumed the full registry was already included and silently skipped prepending `global.imageRegistry` or a component-level `image.registry`. This caused configured registries to be ignored for references such as `mirror.gcr.io/grafana/loki`.

The new behavior is that a configured registry is always prepended, unconditionally. `global.imageRegistry` is the highest-precedence registry setting and overrides all component-level `image.registry` values.

If you stored a fully-qualified image reference in `repository` and relied on the old format to prevent double-prefixing, split the value into separate `registry` and `repository` fields:

Before (13.x):

```yaml
loki:
  image:
    repository: private.registry.com/grafana/loki
```

After (14.x):

```yaml
loki:
  image:
    registry: private.registry.com
    repository: grafana/loki
```

Users who only set `repository` to a plain path (for example, `grafana/loki`) or who already use `global.imageRegistry` / `image.registry` correctly are unaffected.

### 14.x to 15.0: Cilium network policies removed

Support for Cilium-specific network policies has been removed. The chart now renders Kubernetes `NetworkPolicy` resources only.

Remove the following from your values file:

```yaml
networkPolicy:
  flavor: cilium        # remove this key
  egressWorld:
    enabled: true       # remove this section
  egressKubeApiserver:
    enabled: true       # remove this section
```

After (15.x):

```yaml
networkPolicy:
  enabled: true
```

If you relied on Cilium-only behavior such as `CiliumNetworkPolicy` resources, manage those rules outside the Loki chart using separate manifests or your GitOps workflow.

### 15.x to 16.0: Loki Canary pod template isolated

The `loki-canary` workload no longer inherits metadata from the shared Loki pod template. Canary-specific metadata that was previously sourced from `loki.*` keys must now be set explicitly under `lokiCanary.*`.

Before (15.x):

```yaml
loki:
  annotations:
    team: observability
  serviceAnnotations:
    prometheus.io/scrape: "true"
  serviceLabels:
    app: loki
```

After (16.x):

```yaml
lokiCanary:
  annotations:
    team: observability
  podAnnotations:
    team: observability
  service:
    annotations:
      prometheus.io/scrape: "true"
    labels:
      app: loki-canary
  automountServiceAccountToken: false
```

If you did not set any `loki.annotations`, `loki.serviceAnnotations`, or `loki.serviceLabels` for canary purposes, no action is required.

### 16.x to 17.0: Built-in MinIO subchart deprecated

The built-in MinIO subchart is officially deprecated and **will be removed on 2026-10-31**. Setting `minio.enabled=true` now fails chart rendering unless the workaround `ignoreMinioDeprecation: true` is also set.

#### Temporary workaround (not for new deployments)

If you need to continue using the built-in MinIO subchart temporarily while you plan a migration:

```yaml
ignoreMinioDeprecation: true  # Remove before 2026-10-31
minio:
  enabled: true
```

#### Migrate to external object storage

Grafana recommends migrating to a dedicated external object storage backend. The following steps preserve data continuity using a dual-store transition period.

**Before (legacy state using built-in MinIO):**

```yaml
minio:
  enabled: true

loki:
  schemaConfig:
    configs:
      - from: "2024-01-01"
        store: tsdb
        object_store: s3
        schema: v13
        index:
          prefix: index_
          period: 24h
```

**Transition release (temporary dual-store period):**

Keep old MinIO data readable while writing new data to external storage:

```yaml
ignoreMinioDeprecation: true
minio:
  enabled: true

loki:
  structuredConfig:
    storage_config:
      named_stores:
        aws:
          minio:
            endpoint: '{{ include "loki.minio" $ }}'
            bucketnames: chunks
            secret_access_key: '{{ $.Values.minio.rootPassword }}'
            access_key_id: '{{ $.Values.minio.rootUser }}'
            s3forcepathstyle: true
            insecure: true
          s3-loki-chunks:
            endpoint: 's3.example.com'
            bucketnames: chunks
            access_key_id: '<s3-access-key>'
            secret_access_key: '<s3-secret-key>'
            s3forcepathstyle: true
            insecure: true
    schema_config:
      configs:
        - from: "2024-01-01"
          store: tsdb
          object_store: minio
          schema: v13
          index:
            prefix: index_
            period: 24h
        - from: "<date shortly after migration start>"
          store: tsdb
          object_store: s3-loki-chunks
          schema: v13
          index:
            prefix: index_
            period: 24h
```

**Final release (after retention has elapsed):**

Once the retention window for data in MinIO has elapsed, remove the MinIO configuration entirely. The chart still requires `loki.storage.bucketNames` for helper-generated storage sections:

```yaml
loki:
  storage:
    bucketNames:
      chunks: chunks
      ruler: ruler
  structuredConfig:
    storage_config:
      named_stores:
        aws:
          s3-loki-chunks:
            endpoint: 's3.example.com'
            bucketnames: chunks
            access_key_id: '<s3-access-key>'
            secret_access_key: '<s3-secret-key>'
            s3forcepathstyle: true
            insecure: true
    schema_config:
      configs:
        - from: "<date from transition release>"
          store: tsdb
          object_store: s3-loki-chunks
          schema: v13
          index:
            prefix: index_
            period: 24h
```

For more information on storage schema configuration, see [Storage schema](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/storage/schema/).

### 17.x to 18.0: Monitoring block refactored

The `.Values.monitoring` block has been refactored. Do not assume backwards compatibility with any prior monitoring configuration — review this section in full before upgrading.

All dashboards, recording rules, and alert rules are now generated from the upstream [loki-mixin](https://github.com/grafana/loki/tree/main/production/loki-mixin). This keeps the chart aligned with the upstream Loki observability stack and ensures dashboard queries work correctly across all deployment modes.

#### `cluster` label replaced by `app_instance`

The metric label that identifies the Loki Helm release has changed from `cluster` to `app_instance`. The `cluster` label is no longer added by default — it is now reserved for multi-cluster environments and controlled by `monitoring.multiCluster`.

Actions required:

- Update any alerting rules, dashboards, or downstream recording rules that filter on `cluster=~"<release-name>"` to use `app_instance=~"<release-name>"` instead.
- Update any Grafana dashboard URLs that encode the `cluster` variable.
- If you run Loki across multiple Kubernetes clusters, enable `monitoring.multiCluster.enabled` and set `monitoring.multiCluster.clusterName` to restore the `cluster` label with a per-cluster value.

#### Alerts separated from recording rules

Alert rules have been split into a new `monitoring.alerts` section, separate from `monitoring.rules` (which now only controls recording rules). The old `monitoring.rules.configs` block — with per-alert `enabled`, `for`, `lookbackPeriod`, `threshold`, and `severity` fields — has been removed.

Before (17.x):

```yaml
monitoring:
  rules:
    enabled: true
    alerting: true
    configs:
      LokiRequestErrors:
        enabled: true
        for: 15m
        severity: critical
```

After (18.x):

```yaml
monitoring:
  rules:
    enabled: true    # recording rules only
  alerts:
    enabled: true
    overrides:
      LokiRequestErrors:
        for: 15m
        severity: critical
```

{{< admonition type="note" >}}
The `lookbackPeriod` and `threshold` fields are not carried forward. They did not generalize across all PromQL alert expressions and are not present in the loki-mixin alerts.
{{< /admonition >}}

#### Removed and renamed values

Remove or rename the following keys in your values file:

| Old value | Replacement |
| --- | --- |
| `ingester.updateStrategy` | `ingester.strategy` |
| `clusterLabelOverride` | `monitoring.appInstanceLabelName` and `monitoring.appInstanceLabelValue` |
| `monitoring.serviceMonitor.clusterLabel` | `monitoring.appInstanceLabelName` and `monitoring.appInstanceLabelValue` |
| `monitoring.dashboards.namespace` | `monitoring.namespace` (now applies to all monitoring resources) |
| `monitoring.rules.namespace` | `monitoring.namespace` |
| `monitoring.rules.additionalGroups` | `monitoring.additionalPrometheusRules` (dict structure, supports both recording rules and alerts) |
| `monitoring.dashboards.multiCluster` | `monitoring.multiCluster` (hoisted to the monitoring level) |

#### Recording rule names changed

Recording rule `record:` names now use the `cluster_job:`, `cluster_job_route:`, and `cluster_namespace_job_route:` conventions from the upstream loki-mixin. If you reference recording rule metric names directly in custom alerts or dashboards, update those queries.

#### Dashboard architecture changed

Dashboards are now generated into individual ConfigMap resources (one per dashboard) instead of a single ConfigMap containing all dashboards. If you reference the dashboard ConfigMap by name in any tooling or GitOps manifests, update those references.

## Review additional deprecations

Several per-component fields have been deprecated in favor of unified blocks. These deprecated per-component service fields apply to indexGateway, compactor, and others.

These still work but will be removed in a future release. Consider updating them now:

| Deprecated                      | Use                                |
| ------------------------------- | ------------------------------- -- |
| indexGateway.serviceLabels      | indexGateway.service.labels        |
| indexGateway.serviceAnnotations | indexGateway.service.annotations   |
| indexGateway.serviceType        | indexGateway.service.type          |
| indexGateway.appProtocol        | indexGateway.service.type          |
| indexGateway.maxUnavailable     | podDisruptionBudget.maxUnavailable |

The global image registry override has also been renamed:

```yaml
# Deprecated
global:
  image:
    registry: null

# Use instead
global:
  imageRegistry: null
```

{{< admonition type="note" >}}
As of chart 18.3.0, `singleBinary.persistence.enableStatefulSetAutoDeletePVC` defaults to `false` (it defaulted to `true` before). Since you're upgrading straight to 18.7.6, your deployment starts on the new default: persistent volume claims (PVCs) for the single binary StatefulSet are retained, not automatically deleted, when the StatefulSet is deleted or scaled down. If you rely on the old auto-delete behavior, set `singleBinary.persistence.enableStatefulSetAutoDeletePVC: true` explicitly in your values file.
{{< /admonition >}}

## Perform the upgrade

After updating your values file, run the upgrade:

```bash
helm upgrade <RELEASE_NAME> grafana-community/loki \
  -f your-updated-values.yaml \
  --version 18.7.6
```

Or using OCI:

```bash
helm upgrade <RELEASE_NAME> oci://ghcr.io/grafana-community/helm-charts/loki \
  -f your-updated-values.yaml \
  --version 18.7.6
```

### Verify the upgrade

```bash
# Check all pods are running
kubectl get pods -l app.kubernetes.io/name=loki

# Verify Loki is accepting writes
kubectl logs -l app.kubernetes.io/component=write --tail=50

# Verify Loki canary is healthy (if enabled)
kubectl logs -l app.kubernetes.io/component=loki-canary --tail=20
```

## Post-migration cleanup

After a successful upgrade:

- Remove the old Helm repository if no longer needed:

  ```bash
  helm repo remove grafana
  ```

- Update any CI/CD pipelines to reference `grafana-community/loki` or the OCI URL `oci://ghcr.io/grafana-community/helm-charts/loki`.
- Review monitoring dashboards for renamed metrics. Refer to the [Loki 3.x upgrade notes](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/#30) for metric namespace changes.

## Troubleshoot

### Pods fail to start with PodSecurityPolicy errors

**Cause:** Leftover `rbac.pspEnabled` or `rbac.pspAnnotations` in your values file.

**Fix:** Remove both keys from your values file and re-run the upgrade.

### Loki canary fails authentication

**Cause:** Canary tenant configuration was under `monitoring.selfMonitoring.tenant` in 6.x and has moved to `lokiCanary.tenant` in 13.x.

**Fix:** Move `tenant.name` and `tenant.password` to the `lokiCanary.tenant` section.

### Index gateway CrashLoopBackOff

**Cause:** The old `indexGateway.persistence.inMemory` field is no longer recognized.

**Fix:** Replace `inMemory: true` with the equivalent `dataVolumeParameters` configuration as described in [9.x to 10.0](#9x-to-100-index-gateway-persistence-restructured).

### Unexpected deployment mode change

**Cause:** The default `deploymentMode` changed from `SimpleScalable` to `Monolithic`. If you relied on the default without explicitly setting it, your deployment mode changed on upgrade.

**Fix:** Explicitly set `deploymentMode: SimpleScalable` in your values file (or choose `Monolithic` or `Distributed`) and re-run the upgrade.

### Unknown field errors from enterprise values

**Cause:** The `enterprise.*` section was removed in 8.0.0. Any leftover keys cause Helm to reject the values.

**Fix:** Remove the entire `enterprise` section from your values file.

### Unknown field `ephemeralDataVolume`

**Cause:** The nested `persistence.ephemeralDataVolume` structure was flattened in 13.0.0. If you adopted this pattern from an intermediate community chart version, the old keys are no longer recognized.

**Fix:** Replace `persistence.ephemeralDataVolume.enabled: true` with `persistence.enabled: true` and `persistence.type: ephemeral`, and move all nested fields directly under `persistence` as described in [12.x to 13.0](#12x-to-130-ephemeral-volume-persistence-flattened).

### `minio.enabled=true` fails chart rendering

**Cause:** The built-in MinIO subchart was deprecated in 17.0.0. Setting `minio.enabled=true` without `ignoreMinioDeprecation: true` causes chart rendering to fail with the error:

`The Loki chart builtin MinIO dependency is deprecated and will be removed 2026-10-31.`

**Fix:** Either set `ignoreMinioDeprecation: true` as a temporary workaround while you plan a migration, or migrate to an external object storage backend. See [16.x to 17.0](#16x-to-170-built-in-minio-subchart-deprecated) for migration steps.

### Monitoring values rejected or dashboards missing after upgrade to 18.x

**Cause:** The `monitoring` block was refactored in 18.0.0. Old keys such as `clusterLabelOverride`, `monitoring.serviceMonitor.clusterLabel`, `monitoring.dashboards.namespace`, `monitoring.rules.namespace`, `monitoring.rules.additionalGroups`, and `monitoring.dashboards.multiCluster` no longer exist. Alert rules under `monitoring.rules.configs` are also removed.

**Fix:** Remove or rename all obsolete monitoring keys as described in [17.x to 18.0](#17x-to-180-monitoring-block-refactored). In particular:

- Move alert configuration from `monitoring.rules` to `monitoring.alerts`.
- Replace `clusterLabelOverride` with `monitoring.appInstanceLabelName` / `monitoring.appInstanceLabelValue`.
- Rename `monitoring.dashboards.multiCluster` to `monitoring.multiCluster`.

### Metrics show `app_instance` instead of `cluster` after upgrade to 18.x

**Cause:** The 18.0.0 refactoring renamed the Loki release identifier label from `cluster` to `app_instance`.

**Fix:** Update any alerting rules, dashboards, or recording rules that filter on `cluster=~"<release-name>"` to use `app_instance=~"<release-name>"`. See [17.x to 18.0](#17x-to-180-monitoring-block-refactored) for details.

### Image pulled from wrong registry after upgrade to 14.x

**Cause:** The dot-based registry format was removed in 14.0.0. If you had a fully-qualified image reference such as `private.registry.com/grafana/loki` in the `repository` field, the configured registry is now prepended unconditionally, resulting in a double-prefixed reference.

**Fix:** Split the fully-qualified reference into separate `registry` and `repository` fields as described in [13.x to 14.0](#13x-to-140-image-registry-always-applied).

### Loki Canary fails to start or has missing annotations after upgrade to 16.x

**Cause:** The `loki-canary` workload no longer inherits metadata from the shared `loki.*` pod template values. Settings such as `loki.annotations` and `loki.serviceAnnotations` are no longer applied to the canary.

**Fix:** Move canary-specific metadata to the `lokiCanary.*` section as described in [15.x to 16.0](#15x-to-160-loki-canary-pod-template-isolated).

## Further reading

- [Community chart changelog](https://grafana-community.github.io/helm-charts/changelog/?chart=loki)
- [Community chart README - Upgrading](https://github.com/grafana-community/helm-charts/blob/main/charts/loki/README.md#upgrading)
- [Helm chart 6.x upgrade guide](https://grafana.com/docs/loki/<LOKI_VERSION>/setup/upgrade/upgrade-helm-to-6.x/)

# Shared

# Grafana Loki configuration parameters

<!-- DO NOT EDIT THIS FILE - This file has been automatically generated from its .template, regenerate with `make doc` from root directory. -->

## Printing Loki config at runtime

If you pass Loki the flag `-print-config-stderr` or `-log-config-reverse-order`, (or `-print-config-stderr=true`)
Loki will dump the entire config object it has created from the built-in defaults combined first with
overrides from config file, and second by overrides from flags.

The result is the value for every config object in the Loki config struct, which is very large...

Many values will not be relevant to your install such as storage configs which you are not using and which you did not define,
this is expected as every option has a default value if it is being used or not.

This config is what Loki will use to run, it can be invaluable for debugging issues related to configuration and
is especially useful in making sure your config files and flags are being read and loaded properly.

`-print-config-stderr` is nice when running Loki directly e.g. `./loki ` as you can get a quick output of the entire Loki config.

`-log-config-reverse-order` is the flag we run Loki with in all our environments, the config entries are reversed so
that the order of configs reads correctly top to bottom when viewed in Grafana's Explore.

## Configuration file reference

To specify which configuration file to load, pass the `-config.file` flag at the
command line. The value can be a list of comma separated paths, then the first
file that exists will be used.
If no `-config.file` argument is specified, Loki will look up the `config.yaml` in the
current working directory and the `config/` subdirectory and try to use that.

The file is written in [YAML
format](https://en.wikipedia.org/wiki/YAML), defined by the scheme below.
Brackets indicate that a parameter is optional. For non-list parameters the
value is set to the specified default.

### Use environment variables in the configuration

> **Note:** This feature is only available in Loki 2.1+.

You can use environment variable references in the configuration file to set values that need to be configurable during deployment.
To do this, pass `-config.expand-env=true` and use:

```bash
${VAR}
```

Where VAR is the name of the environment variable.

Each variable reference is replaced at startup by the value of the environment variable.
The replacement is case-sensitive and occurs before the YAML file is parsed.
References to undefined variables are replaced by empty strings unless you specify a default value or custom error text.

To specify a default value, use:

```bash
${VAR:-default_value}
```

Where default_value is the value to use if the environment variable is undefined.

Pass the `-config.expand-env` flag at the command line to enable this way of setting configs.

### Generic placeholders

- `<boolean>` : a boolean that can take the values `true` or `false`
- `<int>` : A plain integer (for example, `0`, `1024`, `5000`) or a size in bytes with optional unit suffix (for example, `1024`, `256KB`, `64MB`, `4GB`). Supported units: `B`, `KB`, `MB`, `GB`, `TB`, `PB`, `EB`. 
- `<duration>` : a duration with required unit suffix. Supported units depend on the field type. Prometheus duration fields support: 'ms', 's', 'm', 'h', 'd', 'w', 'y' (for example, '30s', '1m', '1h', '1d', '1w'). Go native duration fields support: 'ns', 'us', 'µs', 'ms', 's', 'm', 'h' but not 'd', 'w', 'y'. Note: '0' is allowed without a unit.
- `<labelname>` : a string matching the regular expression `[a-zA-Z_][a-zA-Z0-9_]*`
- `<labelvalue>` : a string of unicode characters
- `<filename>` : a valid path relative to current working directory or an absolute path.
- `<host>` : a valid string consisting of a hostname or IP followed by an optional port number
- `<string>` : a string
- `<secret>` : a string that represents a secret, such as a password

### Supported contents and default values of `loki.yaml`

<!-- vale Grafana.Spelling = NO -->

```yaml
# A comma-separated list of components to run. The default value 'all' runs Loki
# in single binary mode. A full list of available targets can be printed when
# running Loki with the '-list-targets' command line flag.
# CLI flag: -target
[target: <string> | default = "all"]

# Enables authentication through the X-Scope-OrgID header, which must be present
# if true. If false, the OrgID will always be set to the value of
# -auth.no-auth-tenant.
# CLI flag: -auth.enabled
[auth_enabled: <boolean> | default = true]

lbac:
  # Enables label based access control through the X-Prom-Label-Policy header.
  # CLI flag: -lbac.enabled
  [enabled: <boolean> | default = false]

# Tenant ID to use when auth is disabled. Defaults to 'fake' for backwards
# compatibility. Safe to change on a fresh cluster; on an existing cluster, data
# stored under the old tenant path must be migrated first (see cmd/migrate).
# CLI flag: -auth.no-auth-tenant
[no_auth_tenant: <string> | default = "fake"]

# The amount of virtual memory in bytes to reserve as ballast in order to
# optimize garbage collection. Larger ballasts result in fewer garbage
# collection passes, reducing CPU overhead at the cost of heap size. The ballast
# will not consume physical memory, because it is never read from. It will,
# however, distort metrics, because it is counted as live memory.
# CLI flag: -config.ballast-bytes
[ballast_bytes: <int> | default = 0]

# Configures the server of the launched module(s).
[server: <server>]

# Configures the distributor.
[distributor: <distributor>]

# Configures the querier. Only appropriate when running all modules or just the
# querier.
[querier: <querier>]

# The query_scheduler block configures the Loki query scheduler. When configured
# it separates the tenant query queues from the query-frontend.
[query_scheduler: <query_scheduler>]

# The frontend block configures the Loki query-frontend.
[frontend: <frontend>]

# The query_range block configures the query splitting and caching in the Loki
# query-frontend.
[query_range: <query_range>]

# The ruler block configures the Loki ruler.
[ruler: <ruler>]

ruler_storage:
  # The thanos_object_store_config block configures the connection to object
  # storage backend using thanos-io/objstore clients. This will become the
  # default way of configuring object store clients in future releases.
  # Currently this is opt-in and takes effect only when `-use-thanos-objstore`
  # is set to true.
  # The CLI flags prefix for this block configuration is: ruler-storage
  [<thanos_object_store_config>]

  # Backend storage to use. Supported backends are: local, s3, gcs, azure,
  # swift, filesystem, alibabacloud, bos
  # CLI flag: -ruler-storage.backend
  [backend: <string> | default = "filesystem"]

  local:
    # Directory to scan for rules
    # CLI flag: -ruler-storage.local.directory
    [directory: <string> | default = ""]

# The ingester_client block configures how the distributor will connect to
# ingesters. Only appropriate when running all components, the distributor, or
# the querier.
[ingester_client: <ingester_client>]

# The ingester block configures the ingester and how the ingester will register
# itself to a key value store.
[ingester: <ingester>]

pattern_ingester:
  # Whether the pattern ingester is enabled.
  # CLI flag: -pattern-ingester.enabled
  [enabled: <boolean> | default = false]

  # Configures how the lifecycle of the pattern ingester will operate and where
  # it will register for discovery.
  lifecycler:
    ring:
      kvstore:
        # Backend storage to use for the ring. Supported values are: consul,
        # etcd, inmemory, memberlist, multi.
        # CLI flag: -pattern-ingester.store
        [store: <string> | default = "consul"]

        # The prefix for the keys in the store. Should end with a /.
        # CLI flag: -pattern-ingester.prefix
        [prefix: <string> | default = "collectors/"]

        # Configuration for a Consul client. Only applies if the selected
        # kvstore is consul.
        # The CLI flags prefix for this block configuration is: pattern-ingester
        [consul: <consul>]

        # Configuration for an ETCD v3 client. Only applies if the selected
        # kvstore is etcd.
        # The CLI flags prefix for this block configuration is: pattern-ingester
        [etcd: <etcd>]

        multi:
          # Primary backend storage used by multi-client.
          # CLI flag: -pattern-ingester.multi.primary
          [primary: <string> | default = ""]

          # Secondary backend storage used by multi-client.
          # CLI flag: -pattern-ingester.multi.secondary
          [secondary: <string> | default = ""]

          # Mirror writes to the secondary store.
          # CLI flag: -pattern-ingester.multi.mirror-enabled
          [mirror_enabled: <boolean> | default = false]

          # Timeout for storing a value to the secondary store.
          # CLI flag: -pattern-ingester.multi.mirror-timeout
          [mirror_timeout: <duration> | default = 2s]

      # The heartbeat timeout after which ingesters are skipped for
      # reads/writes.
      # CLI flag: -pattern-ingester.ring.heartbeat-timeout
      [heartbeat_timeout: <duration> | default = 1m]

      # The number of ingesters to write to and read from.
      # CLI flag: -pattern-ingester.distributor.replication-factor
      [replication_factor: <int> | default = 1]

      # True to enable the zone-awareness and replicate ingested samples across
      # different availability zones.
      # CLI flag: -pattern-ingester.distributor.zone-awareness-enabled
      [zone_awareness_enabled: <boolean> | default = false]

      # Comma-separated list of zones to exclude from the ring. Instances in
      # excluded zones will be filtered out from the ring.
      # CLI flag: -pattern-ingester.distributor.excluded-zones
      [excluded_zones: <string> | default = ""]

    # Number of tokens for each ingester.
    # CLI flag: -pattern-ingester.num-tokens
    [num_tokens: <int> | default = 128]

    # Period at which to heartbeat to consul.
    # CLI flag: -pattern-ingester.heartbeat-period
    [heartbeat_period: <duration> | default = 5s]

    # Heartbeat timeout after which instance is assumed to be unhealthy.
    # CLI flag: -pattern-ingester.heartbeat-timeout
    [heartbeat_timeout: <duration> | default = 1m]

    # Observe tokens after generating to resolve collisions. Useful when using
    # gossiping ring.
    # CLI flag: -pattern-ingester.observe-period
    [observe_period: <duration> | default = 0s]

    # Period to wait for a claim from another member; will join automatically
    # after this.
    # CLI flag: -pattern-ingester.join-after
    [join_after: <duration> | default = 0s]

    # Minimum duration to wait after the internal readiness checks have passed
    # but before succeeding the readiness endpoint. This is used to slowdown
    # deployment controllers (eg. Kubernetes) after an instance is ready and
    # before they proceed with a rolling update, to give the rest of the cluster
    # instances enough time to receive ring updates.
    # CLI flag: -pattern-ingester.min-ready-duration
    [min_ready_duration: <duration> | default = 15s]

    # Name of network interface to read address from.
    # CLI flag: -pattern-ingester.lifecycler.interface
    [interface_names: <list of strings> | default = [<private network interfaces>]]

    # Enable IPv6 support. Required to make use of IP addresses from IPv6
    # interfaces.
    # CLI flag: -pattern-ingester.enable-inet6
    [enable_inet6: <boolean> | default = false]

    # Duration to sleep for before exiting, to ensure metrics are scraped.
    # CLI flag: -pattern-ingester.final-sleep
    [final_sleep: <duration> | default = 0s]

    # File path where tokens are stored. If empty, tokens are not stored at
    # shutdown and restored at startup.
    # CLI flag: -pattern-ingester.tokens-file-path
    [tokens_file_path: <string> | default = ""]

    # The availability zone where this instance is running.
    # CLI flag: -pattern-ingester.availability-zone
    [availability_zone: <string> | default = ""]

    # Unregister from the ring upon clean shutdown. It can be useful to disable
    # for rolling restarts with consistent naming in conjunction with
    # -distributor.extend-writes=false.
    # CLI flag: -pattern-ingester.unregister-on-shutdown
    [unregister_on_shutdown: <boolean> | default = true]

    # When enabled the readiness probe succeeds only after all instances are
    # ACTIVE and healthy in the ring, otherwise only the instance itself is
    # checked. This option should be disabled if in your cluster multiple
    # instances can be rolled out simultaneously, otherwise rolling updates may
    # be slowed down.
    # CLI flag: -pattern-ingester.readiness-check-ring-health
    [readiness_check_ring_health: <boolean> | default = true]

    # IP address to advertise in the ring.
    # CLI flag: -pattern-ingester.lifecycler.addr
    [address: <string> | default = ""]

    # port to advertise in consul (defaults to server.grpc-listen-port).
    # CLI flag: -pattern-ingester.lifecycler.port
    [port: <int> | default = 0]

    # ID to register in the ring.
    # CLI flag: -pattern-ingester.lifecycler.ID
    [id: <string> | default = "<hostname>"]

  # Configures how the pattern ingester will connect to the ingesters.
  client_config:
    # Configures how connections are pooled.
    pool_config:
      # How frequently to clean up clients for ingesters that have gone away.
      # CLI flag: -pattern-ingester.client-cleanup-period
      [client_cleanup_period: <duration> | default = 15s]

      # Run a health check on each ingester client during periodic cleanup.
      # CLI flag: -pattern-ingester.health-check-ingesters
      [health_check_ingesters: <boolean> | default = true]

      # Timeout for the health check.
      # CLI flag: -pattern-ingester.remote-timeout
      [remote_timeout: <duration> | default = 1s]

    # The remote request timeout on the client side.
    # CLI flag: -pattern-ingester.client.timeout
    [remote_timeout: <duration> | default = 5s]

    # Configures how the gRPC connection to ingesters work as a client.
    # The CLI flags prefix for this block configuration is:
    # pattern-ingester.client
    [grpc_client_config: <grpc_client>]

  # How many flushes can happen concurrently from each stream.
  # CLI flag: -pattern-ingester.concurrent-flushes
  [concurrent_flushes: <int> | default = 32]

  # How often should the ingester see if there are any blocks to flush. The
  # first flush check is delayed by a random time up to 0.8x the flush check
  # period. Additionally, there is +/- 1% jitter added to the interval.
  # CLI flag: -pattern-ingester.flush-check-period
  [flush_check_period: <duration> | default = 1m]

  # The maximum number of detected pattern clusters that can be created by
  # streams.
  # CLI flag: -pattern-ingester.max-clusters
  [max_clusters: <int> | default = 300]

  # The maximum eviction ratio of patterns per stream. Once that ratio is
  # reached, the stream will throttled pattern detection.
  # CLI flag: -pattern-ingester.max-eviction-ratio
  [max_eviction_ratio: <float> | default = 0.25]

  # Configures the metric aggregation and storage behavior of the pattern
  # ingester.
  metric_aggregation:
    # How often to sample metrics and patterns from raw push observations.
    # CLI flag: -pattern-ingester.metric-aggregation.downsample-period
    [sample_period: <duration> | default = 10s]

    # The address of the Loki instance to push aggregated metrics to.
    # CLI flag: -pattern-ingester.metric-aggregation.loki-address
    [loki_address: <string> | default = ""]

    # The timeout for writing to Loki.
    # CLI flag: -pattern-ingester.metric-aggregation.timeout
    [timeout: <duration> | default = 10s]

    # How long to wait in between pushes to Loki.
    # CLI flag: -pattern-ingester.metric-aggregation.push-period
    [push_period: <duration> | default = 30s]

    # The HTTP client configuration for pushing metrics to Loki.
    http_client_config:
      basic_auth:
        [username: <string> | default = ""]

        [username_file: <string> | default = ""]

        [username_ref: <string> | default = ""]

        [password: <string> | default = ""]

        [password_file: <string> | default = ""]

        [password_ref: <string> | default = ""]

      authorization:
        [type: <string> | default = ""]

        [credentials: <string> | default = ""]

        [credentials_file: <string> | default = ""]

        [credentials_ref: <string> | default = ""]

      oauth2:
        [client_id: <string> | default = ""]

        [client_secret: <string> | default = ""]

        [client_secret_file: <string> | default = ""]

        [client_secret_ref: <string> | default = ""]

        [client_certificate_key_id: <string> | default = ""]

        [client_certificate_key: <string> | default = ""]

        [client_certificate_key_file: <string> | default = ""]

        [client_certificate_key_ref: <string> | default = ""]

        [grant_type: <string> | default = ""]

        [signature_algorithm: <string> | default = ""]

        [iss: <string> | default = ""]

        [audience: <string> | default = ""]

        [claims: <map of string to >]

        [scopes: <list of strings>]

        [token_url: <string> | default = ""]

        [endpoint_params: <map of string to string>]

        tls_config:
          [ca: <string> | default = ""]

          [cert: <string> | default = ""]

          [key: <string> | default = ""]

          [ca_file: <string> | default = ""]

          [cert_file: <string> | default = ""]

          [key_file: <string> | default = ""]

          [ca_ref: <string> | default = ""]

          [cert_ref: <string> | default = ""]

          [key_ref: <string> | default = ""]

          [server_name: <string> | default = ""]

          [insecure_skip_verify: <boolean>]

          [min_version: <int>]

          [max_version: <int>]

        proxy_url:
          [url: <url>]

        [no_proxy: <string> | default = ""]

        [proxy_from_environment: <boolean>]

        [proxy_connect_header: <map of string to list of strings>]

      [bearer_token: <string> | default = ""]

      [bearer_token_file: <string> | default = ""]

      tls_config:
        [ca: <string> | default = ""]

        [cert: <string> | default = ""]

        [key: <string> | default = ""]

        [ca_file: <string> | default = ""]

        [cert_file: <string> | default = ""]

        [key_file: <string> | default = ""]

        [ca_ref: <string> | default = ""]

        [cert_ref: <string> | default = ""]

        [key_ref: <string> | default = ""]

        [server_name: <string> | default = ""]

        [insecure_skip_verify: <boolean>]

        [min_version: <int>]

        [max_version: <int>]

      [follow_redirects: <boolean>]

      [enable_http2: <boolean>]

      proxy_url:
        [url: <url>]

      [no_proxy: <string> | default = ""]

      [proxy_from_environment: <boolean>]

      [proxy_connect_header: <map of string to list of strings>]

      http_headers:
        [: <map of string to Header>]

    # Whether to use TLS for pushing metrics to Loki.
    # CLI flag: -pattern-ingester.metric-aggregation.tls
    [use_tls: <boolean> | default = false]

    # The basic auth configuration for pushing metrics to Loki.
    basic_auth:
      # Basic auth username for sending aggregations back to Loki.
      # CLI flag: -pattern-ingester.metric-aggregation..basic-auth.username
      [username: <string> | default = ""]

      # Basic auth password for sending aggregations back to Loki.
      # CLI flag: -pattern-ingester.metric-aggregation..basic-auth.password
      [password: <string> | default = ""]

    # The backoff configuration for pushing metrics to Loki.
    backoff_config:
      # Minimum delay when backing off.
      # CLI flag: -pattern-ingester.metric-aggregation...backoff-min-period
      [min_period: <duration> | default = 100ms]

      # Maximum delay when backing off.
      # CLI flag: -pattern-ingester.metric-aggregation...backoff-max-period
      [max_period: <duration> | default = 10s]

      # Number of times to backoff and retry before failing.
      # CLI flag: -pattern-ingester.metric-aggregation...backoff-retries
      [max_retries: <int> | default = 10]

  # Configures how detected patterns are pushed back to Loki for persistence.
  pattern_persistence:
    # The address of the Loki instance to push patterns to.
    # CLI flag: -pattern-ingester.pattern-persistence.loki-address
    [loki_address: <string> | default = ""]

    # The timeout for writing patterns to Loki.
    # CLI flag: -pattern-ingester.pattern-persistence.timeout
    [timeout: <duration> | default = 10s]

    # How long to wait between pattern pushes to Loki.
    # CLI flag: -pattern-ingester.pattern-persistence.push-period
    [push_period: <duration> | default = 1m]

    # The HTTP client configuration for pushing patterns to Loki.
    http_client_config:
      basic_auth:
        [username: <string> | default = ""]

        [username_file: <string> | default = ""]

        [username_ref: <string> | default = ""]

        [password: <string> | default = ""]

        [password_file: <string> | default = ""]

        [password_ref: <string> | default = ""]

      authorization:
        [type: <string> | default = ""]

        [credentials: <string> | default = ""]

        [credentials_file: <string> | default = ""]

        [credentials_ref: <string> | default = ""]

      oauth2:
        [client_id: <string> | default = ""]

        [client_secret: <string> | default = ""]

        [client_secret_file: <string> | default = ""]

        [client_secret_ref: <string> | default = ""]

        [client_certificate_key_id: <string> | default = ""]

        [client_certificate_key: <string> | default = ""]

        [client_certificate_key_file: <string> | default = ""]

        [client_certificate_key_ref: <string> | default = ""]

        [grant_type: <string> | default = ""]

        [signature_algorithm: <string> | default = ""]

        [iss: <string> | default = ""]

        [audience: <string> | default = ""]

        [claims: <map of string to >]

        [scopes: <list of strings>]

        [token_url: <string> | default = ""]

        [endpoint_params: <map of string to string>]

        tls_config:
          [ca: <string> | default = ""]

          [cert: <string> | default = ""]

          [key: <string> | default = ""]

          [ca_file: <string> | default = ""]

          [cert_file: <string> | default = ""]

          [key_file: <string> | default = ""]

          [ca_ref: <string> | default = ""]

          [cert_ref: <string> | default = ""]

          [key_ref: <string> | default = ""]

          [server_name: <string> | default = ""]

          [insecure_skip_verify: <boolean>]

          [min_version: <int>]

          [max_version: <int>]

        proxy_url:
          [url: <url>]

        [no_proxy: <string> | default = ""]

        [proxy_from_environment: <boolean>]

        [proxy_connect_header: <map of string to list of strings>]

      [bearer_token: <string> | default = ""]

      [bearer_token_file: <string> | default = ""]

      tls_config:
        [ca: <string> | default = ""]

        [cert: <string> | default = ""]

        [key: <string> | default = ""]

        [ca_file: <string> | default = ""]

        [cert_file: <string> | default = ""]

        [key_file: <string> | default = ""]

        [ca_ref: <string> | default = ""]

        [cert_ref: <string> | default = ""]

        [key_ref: <string> | default = ""]

        [server_name: <string> | default = ""]

        [insecure_skip_verify: <boolean>]

        [min_version: <int>]

        [max_version: <int>]

      [follow_redirects: <boolean>]

      [enable_http2: <boolean>]

      proxy_url:
        [url: <url>]

      [no_proxy: <string> | default = ""]

      [proxy_from_environment: <boolean>]

      [proxy_connect_header: <map of string to list of strings>]

      http_headers:
        [: <map of string to Header>]

    # Whether to use TLS for pushing patterns to Loki.
    # CLI flag: -pattern-ingester.pattern-persistence.tls
    [use_tls: <boolean> | default = false]

    # The basic auth configuration for pushing patterns to Loki.
    basic_auth:
      # Basic auth username for sending patterns back to Loki.
      # CLI flag: -pattern-ingester.pattern-persistence..basic-auth.username
      [username: <string> | default = ""]

      # Basic auth password for sending patterns back to Loki.
      # CLI flag: -pattern-ingester.pattern-persistence..basic-auth.password
      [password: <string> | default = ""]

    # The backoff configuration for pushing patterns to Loki.
    backoff_config:
      # Minimum delay when backing off.
      # CLI flag: -pattern-ingester.pattern-persistence...backoff-min-period
      [min_period: <duration> | default = 100ms]

      # Maximum delay when backing off.
      # CLI flag: -pattern-ingester.pattern-persistence...backoff-max-period
      [max_period: <duration> | default = 10s]

      # Number of times to backoff and retry before failing.
      # CLI flag: -pattern-ingester.pattern-persistence...backoff-retries
      [max_retries: <int> | default = 10]

    # The maximum number of patterns to accumulate before pushing.
    # CLI flag: -pattern-ingester.pattern-persistence.batch-size
    [batch_size: <int> | default = 1000]

  # Configures the pattern tee which forwards requests to the pattern ingester.
  tee_config:
    # The max time between batches of raw logs to send for template mining
    # CLI flag: -pattern-ingester.tee.batch-flush-interval
    [batch_flush_interval: <duration> | default = 1s]

    # The number of log flushes to queue before dropping
    # CLI flag: -pattern-ingester.tee.flush-queue-size
    [flush_queue_size: <int> | default = 1000]

    # the number of concurrent workers sending logs to the template service
    # CLI flag: -pattern-ingester.tee.flush-worker-count
    [flush_worker_count: <int> | default = 100]

    # The maximum number of bytes that can be buffered before dropping, 0 means
    # disabled
    # CLI flag: -pattern-ingester.tee.max-buffered-bytes
    [max_buffered_bytes: <int> | default = 0]

    # The max time we will try to flush any remaining logs to be mined when the
    # service is stopped
    # CLI flag: -pattern-ingester.tee.stop-flush-timeout
    [stop_flush_timeout: <duration> | default = 30s]

  # Timeout for connections between the Loki and the pattern ingester.
  # CLI flag: -pattern-ingester.connection-timeout
  [connection_timeout: <duration> | default = 2s]

  # The maximum length of log lines that can be used for pattern detection.
  # CLI flag: -pattern-ingester.max-allowed-line-length
  [max_allowed_line_length: <int> | default = 3000]

  # How long to retain patterns in the pattern ingester after they are pushed.
  # CLI flag: -pattern-ingester.retain-for
  [retain_for: <duration> | default = 3h]

  # The maximum time span for a single pattern chunk.
  # CLI flag: -pattern-ingester.max-chunk-age
  [max_chunk_age: <duration> | default = 1h]

  # The time resolution for pattern samples within chunks.
  # CLI flag: -pattern-ingester.sample-interval
  [pattern_sample_interval: <duration> | default = 10s]

  # The threshold for filtering patterns by volume. Only patterns representing
  # the top X% of log volume will be persisted (0-1).
  # CLI flag: -pattern-ingester.volume-threshold
  [volume_threshold: <float> | default = 0.99]

  # Configures consumption of log entries from Kafka.
  kafka_ingestion:
    # Whether the pattern ingester consumes log entries from Kafka. When
    # enabled, the distributor does not tee log entries to the pattern ingester.
    # When using CLI flags, this must be set on the distributors as well,
    # otherwise it just needs to be set once in the config file.
    # CLI flag: -pattern-ingester.kafka-ingestion-enabled
    [enabled: <boolean> | default = false]

    # The consumer group of the pattern ingesters.
    # CLI flag: -pattern-ingester.kafka-consumer-group
    [consumer_group: <string> | default = "pattern-ingester"]

    # The session timeout of the consumer group. A pattern ingester that
    # restarts and rejoins within this time gets its partitions back without a
    # rebalance, but if it does not rejoin, its partitions are not consumed
    # until the timeout expires.
    # CLI flag: -pattern-ingester.kafka-session-timeout
    [session_timeout: <duration> | default = 3m]

# The index_gateway block configures the Loki index gateway server, responsible
# for serving index queries without the need to constantly interact with the
# object store.
[index_gateway: <index_gateway>]

# Deprecated: The bloom_build block configures the Loki bloom planner and
# builder servers, responsible for building bloom filters.
[bloom_build: <bloom_build>]

# Deprecated: The bloom_gateway block configures the Loki bloom gateway server,
# responsible for serving queries for filtering chunks based on filter
# expressions.
[bloom_gateway: <bloom_gateway>]

# The storage_config block configures one of many possible stores for both the
# index and chunks. Which configuration to be picked should be defined in
# schema_config block.
[storage_config: <storage_config>]

# The chunk_store_config block configures how chunks will be cached and how long
# to wait before saving them to the backing store.
[chunk_store_config: <chunk_store_config>]

# Configures the chunk index schema and where it is stored.
[schema_config: <schema_config>]

# The compactor block configures the compactor component, which compacts index
# shards for performance.
[compactor: <compactor>]

compactor_grpc_client:
  # The grpc_client block configures the gRPC client used to communicate between
  # a client and server component in Loki.
  # The CLI flags prefix for this block configuration is: compactor.grpc-client
  [<grpc_client>]

# The limits_config block configures global and per-tenant limits in Loki. The
# values here can be overridden in the `overrides` section of the runtime_config
# file
[limits_config: <limits_config>]

# The frontend_worker configures the worker - running within the Loki querier -
# picking up and executing queries enqueued by the query-frontend.
[frontend_worker: <frontend_worker>]

# Configuration for memberlist client. Only applies if the selected kvstore is
# memberlist.
# 
# When a memberlist config with atleast 1 join_members is defined, kvstore of
# type memberlist is automatically selected for all the components that require
# a ring unless otherwise specified in the component's configuration section.
[memberlist: <memberlist>]

kafka_config:
  # The Kafka topic name.
  # CLI flag: -kafka.topic
  [topic: <string> | default = ""]

  # The maximum time allowed to open a connection to a Kafka broker.
  # CLI flag: -kafka.dial-timeout
  [dial_timeout: <duration> | default = 2s]

  # How long to wait for an incoming write request to be successfully committed
  # to the Kafka backend.
  # CLI flag: -kafka.write-timeout
  [write_timeout: <duration> | default = 10s]

  reader_config:
    # The Kafka backend address.
    # CLI flag: -kafka.reader.address
    [address: <string> | default = "localhost:9092"]

    # The Kafka client ID.
    # CLI flag: -kafka.reader.client-id
    [client_id: <string> | default = ""]

  writer_config:
    # The Kafka backend address.
    # CLI flag: -kafka.writer.address
    [address: <string> | default = "localhost:9092"]

    # The Kafka client ID.
    # CLI flag: -kafka.writer.client-id
    [client_id: <string> | default = ""]

  # The SASL username for authentication to Kafka using the PLAIN mechanism.
  # Both username and password must be set.
  # CLI flag: -kafka.sasl-username
  [sasl_username: <string> | default = ""]

  # The SASL password for authentication to Kafka using the PLAIN mechanism.
  # Both username and password must be set.
  # CLI flag: -kafka.sasl-password
  [sasl_password: <string> | default = ""]

  # The consumer group used by the consumer to track the last consumed offset.
  # The consumer group must be different for each ingester zone.When empty, Loki
  # uses the ingester instance ID.
  # CLI flag: -kafka.consumer-group
  [consumer_group: <string> | default = ""]

  # How frequently a consumer should commit the consumed offset to Kafka. The
  # last committed offset is used at startup to continue the consumption from
  # where it was left.
  # CLI flag: -kafka.consumer-group-offset-commit-interval
  [consumer_group_offset_commit_interval: <duration> | default = 1s]

  # How long to retry a failed request to get the last produced offset.
  # CLI flag: -kafka.last-produced-offset-retry-timeout
  [last_produced_offset_retry_timeout: <duration> | default = 10s]

  # Enable auto-creation of Kafka topic if it doesn't exist.
  # CLI flag: -kafka.auto-create-topic-enabled
  [auto_create_topic_enabled: <boolean> | default = true]

  # When auto-creation of Kafka topic is enabled and this value is positive,
  # Kafka's num.partitions configuration option is set on Kafka brokers with
  # this value when Loki component that uses Kafka starts. This configuration
  # option specifies the default number of partitions that the Kafka broker uses
  # for auto-created topics. Note that this is a Kafka-cluster wide setting, and
  # applies to any auto-created topic. If the setting of num.partitions fails,
  # Loki proceeds anyways, but auto-created topics could have an incorrect
  # number of partitions.
  # CLI flag: -kafka.auto-create-topic-default-partitions
  [auto_create_topic_default_partitions: <int> | default = 1000]

  # The maximum size of a Kafka record data that should be generated by the
  # producer. An incoming write request larger than this size is split into
  # multiple Kafka records. We strongly recommend to not change this setting
  # unless for testing purposes.
  # CLI flag: -kafka.producer-max-record-size-bytes
  [producer_max_record_size_bytes: <int> | default = 15983616]

  # The maximum size of (uncompressed) buffered and unacknowledged produced
  # records sent to Kafka. The produce request fails once this limit is reached.
  # This limit is per Kafka client. 0 to disable the limit.
  # CLI flag: -kafka.producer-max-buffered-bytes
  [producer_max_buffered_bytes: <int> | default = 1073741824]

  # The maximum number of Produce requests the producer can have in-flight per
  # Kafka broker at any given time. The product of this value and
  # -kafka.producer-linger should exceed the maximum Produce latency expected
  # from the Kafka backend in steady state. If the backend takes longer than
  # that to process a Produce request, the client will buffer data and stop
  # issuing new Produce requests until some in-flight ones complete, which
  # surfaces as added latency to callers.
  # CLI flag: -kafka.producer-max-inflight-requests-per-broker
  [producer_max_inflight_requests_per_broker: <int> | default = 20]

  # How long the producer waits, buffering records for the same partition,
  # before sending a Produce request. The product of this value and
  # -kafka.producer-max-inflight-requests-per-broker should exceed the maximum
  # Produce latency expected from the Kafka backend in steady state. If the
  # backend takes longer than that to process a Produce request, the client will
  # buffer data and stop issuing new Produce requests until some in-flight ones
  # complete, which surfaces as added latency to callers.
  # CLI flag: -kafka.producer-linger
  [producer_linger: <duration> | default = 50ms]

  # The guaranteed maximum lag before a consumer is considered to have caught up
  # reading from a partition at startup, becomes ACTIVE in the hash ring and
  # passes the readiness check. Set -kafka.max-consumer-lag-at-startup to 0 to
  # disable waiting for maximum consumer lag being honored at startup.
  # CLI flag: -kafka.max-consumer-lag-at-startup
  [max_consumer_lag_at_startup: <duration> | default = 15s]

  # The maximum number of workers to use for processing records from Kafka.
  # CLI flag: -kafka.max-consumer-workers
  [max_consumer_workers: <int> | default = 1]

  # Enable collection of the following kafka latency histograms: read-wait,
  # read-timing, write-wait, write-timing
  # CLI flag: -kafka.enable-kafka-histograms
  [enable_kafka_histograms: <boolean> | default = false]

  # Enable tracing.
  # CLI flag: -kafka.tracing-enabled
  [tracing_enabled: <boolean> | default = false]

dataobj:
  builder:
    logsobj_builder:
      # The target maximum amount of uncompressed data to hold in data pages
      # (for columnar sections). Uncompressed size is used for consistent I/O
      # and planning.
      # CLI flag: -dataobj.builder.logsobj-builder.target-page-size
      [target_page_size: <int> | default = 1MiB]

      # The maximum row count for pages to use for the data object builder. A
      # value of 0 means no limit.
      # CLI flag: -dataobj.builder.logsobj-builder.max-page-rows
      [max_page_rows: <int> | default = 10000]

      # The target maximum size of the encoded object and all of its encoded
      # sections (after compression), to limit memory usage of a builder.
      # CLI flag: -dataobj.builder.logsobj-builder.target-builder-memory-limit
      [target_object_size: <int> | default = 512MiB]

      # The target maximum amount of uncompressed data to hold in sections, for
      # sections that support being limited by size. Uncompressed size is used
      # for consistent I/O and planning.
      # CLI flag: -dataobj.builder.logsobj-builder.target-section-size
      [target_section_size: <int> | default = 512MiB]

      # The size of logs to buffer in memory before adding into columnar
      # builders, used to reduce CPU load of sorting.
      # CLI flag: -dataobj.builder.logsobj-builder.buffer-size
      [buffer_size: <int> | default = 128MiB]

      # The maximum number of dataobj section stripes to merge into a section at
      # once. Must be greater than 1.
      # CLI flag: -dataobj.builder.logsobj-builder.section-stripe-merge-limit
      [section_stripe_merge_limit: <int> | default = 2]

      # Expected compression ratio for log data, used to estimate compressed
      # output size from uncompressed buffered records. Only takes effect with
      # ordered append. Set to either 0 or 1 to disable.
      # CLI flag: -dataobj.builder.logsobj-builder.estimated-compression-ratio
      [estimated_compression_ratio: <int> | default = 8]

    indexobj_builder:
      # The target maximum amount of uncompressed data to hold in data pages
      # (for columnar sections). Uncompressed size is used for consistent I/O
      # and planning.
      # CLI flag: -dataobj.builder.indexobj-builder.target-page-size
      [target_page_size: <int> | default = 128KiB]

      # The maximum row count for pages to use for the data object builder. A
      # value of 0 means no limit.
      # CLI flag: -dataobj.builder.indexobj-builder.max-page-rows
      [max_page_rows: <int> | default = 10000]

      # The target maximum size of the encoded object and all of its encoded
      # sections (after compression), to limit memory usage of a builder.
      # CLI flag: -dataobj.builder.indexobj-builder.target-builder-memory-limit
      [target_object_size: <int> | default = 512MiB]

      # The target maximum amount of uncompressed data to hold in sections, for
      # sections that support being limited by size. Uncompressed size is used
      # for consistent I/O and planning.
      # CLI flag: -dataobj.builder.indexobj-builder.target-section-size
      [target_section_size: <int> | default = 512MiB]

      # The size of logs to buffer in memory before adding into columnar
      # builders, used to reduce CPU load of sorting.
      # CLI flag: -dataobj.builder.indexobj-builder.buffer-size
      [buffer_size: <int> | default = 128MiB]

      # The maximum number of dataobj section stripes to merge into a section at
      # once. Must be greater than 1.
      # CLI flag: -dataobj.builder.indexobj-builder.section-stripe-merge-limit
      [section_stripe_merge_limit: <int> | default = 2]

      # Expected compression ratio for log data, used to estimate compressed
      # output size from uncompressed buffered records. Only takes effect with
      # ordered append. Set to either 0 or 1 to disable.
      # CLI flag: -dataobj.builder.indexobj-builder.estimated-compression-ratio
      [estimated_compression_ratio: <int> | default = 1]

    # The maximum amount of time to wait in seconds before flushing an object
    # that is no longer receiving new writes.
    # CLI flag: -dataobj.builder.idle-flush-timeout
    [idle_flush_timeout: <duration> | default = 1h]

    # The maximum amount of time to accumulate data in a builder before flushing
    # it. Defaults to 1 hour.
    # CLI flag: -dataobj.builder.max-builder-age
    [max_builder_age: <duration> | default = 1h]

    # The maximum number of data objects that one flush builds, uploads and
    # indexes at the same time. Each extra object in progress adds the memory to
    # sort, upload and index it. If one object fails, the flush cancels the
    # others. The builder commits the Kafka offset only after every object
    # succeeds.
    # CLI flag: -dataobj.builder.flush-concurrency
    [flush_concurrency: <int> | default = 1]

    # The name of the Kafka topic.
    # CLI flag: -dataobj.builder.topic
    [topic: <string> | default = ""]

    # The Kafka partition to consume. A negative value derives it from the
    # instance hostname instead.
    # CLI flag: -dataobj.builder.partition-id
    [partition_id: <int> | default = -1]

    # How often the builder that consumes partition 0 reads the offsets of every
    # partition to refresh the loki_dataobj_builder_required_replicas metric.
    # CLI flag: -dataobj.builder.required-replicas-refresh-interval
    [required_replicas_refresh_interval: <duration> | default = 30s]

  uploader:
    # The size of the SHA prefix to use for generating object storage keys for
    # data objects.
    # CLI flag: -dataobj.uploader.sha-prefix-size
    [sha_prefix_size: <int> | default = 2]

  metastore:
    # Experimental: A prefix to use for storing indexes in object storage. Used
    # for testing only.
    # CLI flag: -dataobj-metastore.index-storage-prefix
    [index_storage_prefix: <string> | default = "index/v0"]

  # Caches the metadata of the data objects the querier reads. The cache is
  # enabled when a backend is configured. Entries are keyed by object path, so
  # the cache cannot differentiate entries between different buckets and
  # clusters. Multiple installations must not share the same cache backend.
  # The CLI flags prefix for this block configuration is: dataobj.metadata-cache
  [metadata_cache: <cache_config>]

  compaction:
    # Experimental: Enable dataobj compaction modules (planner and worker
    # targets when selected via -target).
    # CLI flag: -dataobj.compaction.enabled
    [enabled: <boolean> | default = false]

    # Experimental: Per-tenant-cycle cap on concurrent IndexMerge tasks
    # dispatched by the coordinator. 0 means unlimited (one goroutine per task
    # with no admission throttle).
    # CLI flag: -dataobj.compaction.max-running-compaction-tasks
    [max_running_compaction_tasks: <int> | default = 16]

    # Experimental: Per-tenant-cycle cap on concurrent LogMerge tasks dispatched
    # by the coordinator. 0 means unlimited.
    # CLI flag: -dataobj.compaction.logs.max-running-compaction-tasks
    [logs_max_running_compaction_tasks: <int> | default = 16]

    # Experimental: Coordinator main-loop cadence.
    # CLI flag: -dataobj.compaction.polling-interval
    [polling_interval: <duration> | default = 5m]

    # Experimental: Minimum wait a per-tenant worker applies between compaction
    # phases, and the starting point of the exponential backoff idle or failing
    # tenants grow toward max-backoff.
    # CLI flag: -dataobj.compaction.min-backoff
    [min_backoff: <duration> | default = 1m]

    # Experimental: Maximum wait a per-tenant worker backs off to after
    # consecutive no-work (converged or empty) or failing phases, so an idle
    # worker stops hammering object storage.
    # CLI flag: -dataobj.compaction.max-backoff
    [max_backoff: <duration> | default = 15m]

    # Experimental: Number of prior metastore windows to compact in addition to
    # the current window. Must be at least 1, which compacts the current and
    # immediately prior window.
    # CLI flag: -dataobj.compaction.window-lookback
    [window_lookback: <int> | default = 1]

    # Experimental: Maximum runs per IndexMerge task (K). Memory grows linearly
    # with K.
    # CLI flag: -dataobj.compaction.max-runs-per-task
    [max_runs_per_task: <int> | default = 8]

    # Experimental: Maximum runs per LogMerge task (K for log compaction). Log
    # compaction starts only when a size level holds K runs, so a larger K
    # leaves more overlapping runs per window. Separate from max-runs-per-task
    # to scale independently. Must be at least 2.
    # CLI flag: -dataobj.compaction.logs.max-runs-per-task
    [logs_max_runs_per_task: <int> | default = 8]

    # Experimental: Minimum total compactable data (sum of all runs'
    # uncompressed size) that justifies log compaction. Windows below this floor
    # are skipped.
    # CLI flag: -dataobj.compaction.logs.min-compaction-size
    [logs_min_compaction_size: <int> | default = 4MiB]

    # Experimental: Maximum uncompressed input size of one LogMerge task. The
    # planner splits a larger merge into tasks by stream sort prefix and never
    # splits one sort prefix. A task exceeds the maximum in two cases: a sort
    # prefix above it gets a task of its own, and the last task of a merge can
    # take in a leftover of up to 10% of it. 0 disables the split.
    # CLI flag: -dataobj.compaction.logs.max-task-size
    [logs_max_task_size: <int> | default = 0B]

    # Experimental: Coordinator-side timeout around the inline ToC
    # ReplaceIndexPointers call. Not a task TTL.
    # CLI flag: -dataobj.compaction.toc-consolidate-timeout
    [toc_consolidate_timeout: <duration> | default = 30s]

    # Experimental: Skip the post-compaction ToC ReplaceIndexPointers swap.
    # Planning, IndexMerge task execution, and per-output audit logging still
    # run, but the ToC is never mutated.
    # CLI flag: -dataobj.compaction.dry-run
    [dry_run: <boolean> | default = false]

    # Experimental: Plan version hashed into IndexMerge output paths. Bump to
    # invalidate previously-written outputs after a planner-algorithm change.
    # CLI flag: -dataobj.compaction.plan-version
    [plan_version: <int> | default = 1]

    scheduler:
      # Experimental: host:port the embedded compaction scheduler advertises to
      # compaction workers. Empty string keeps the scheduler in-process-only.
      # CLI flag: -dataobj.compaction.scheduler.advertise-addr
      [advertise_addr: <string> | default = ""]

      # Experimental: HTTP path the embedded compaction scheduler listens on for
      # worker frame traffic.
      # CLI flag: -dataobj.compaction.scheduler.endpoint
      [endpoint: <string> | default = "/api/v2/compaction-frame"]

    worker:
      # Experimental: Number of task-execution threads. 0 uses GOMAXPROCS.
      # CLI flag: -dataobj.compaction.worker.worker-threads
      [worker_threads: <int> | default = 0]

      # Experimental: DNS-SRV address used to discover compaction schedulers.
      # Required when -target=dataobj-compaction-worker. Example:
      # dnssrv+_compaction-frame._tcp.compactor-scheduler.svc.cluster.local
      # CLI flag: -dataobj.compaction.worker.scheduler-lookup-address
      [scheduler_lookup_address: <string> | default = ""]

      # Experimental: Interval at which to re-run the DNS-SRV lookup.
      # CLI flag: -dataobj.compaction.worker.scheduler-lookup-interval
      [scheduler_lookup_interval: <duration> | default = 10s]

      # Experimental: host:port the embedded compaction worker advertises to
      # schedulers. Required when -target=dataobj-compaction-worker.
      # CLI flag: -dataobj.compaction.worker.advertise-addr
      [advertise_addr: <string> | default = ""]

      # Experimental: HTTP path the embedded compaction worker registers its
      # frame handler on.
      # CLI flag: -dataobj.compaction.worker.endpoint
      [endpoint: <string> | default = "/api/v2/compaction-frame"]

    indexobj_builder:
      # The target maximum amount of uncompressed data to hold in data pages
      # (for columnar sections). Uncompressed size is used for consistent I/O
      # and planning.
      # CLI flag: -dataobj.compaction.indexobj-builder.target-page-size
      [target_page_size: <int> | default = 128KiB]

      # The maximum row count for pages to use for the data object builder. A
      # value of 0 means no limit.
      # CLI flag: -dataobj.compaction.indexobj-builder.max-page-rows
      [max_page_rows: <int> | default = 10000]

      # The target maximum size of the encoded object and all of its encoded
      # sections (after compression), to limit memory usage of a builder.
      # CLI flag: -dataobj.compaction.indexobj-builder.target-builder-memory-limit
      [target_object_size: <int> | default = 512MiB]

      # The target maximum amount of uncompressed data to hold in sections, for
      # sections that support being limited by size. Uncompressed size is used
      # for consistent I/O and planning.
      # CLI flag: -dataobj.compaction.indexobj-builder.target-section-size
      [target_section_size: <int> | default = 512MiB]

      # The size of logs to buffer in memory before adding into columnar
      # builders, used to reduce CPU load of sorting.
      # CLI flag: -dataobj.compaction.indexobj-builder.buffer-size
      [buffer_size: <int> | default = 128MiB]

      # The maximum number of dataobj section stripes to merge into a section at
      # once. Must be greater than 1.
      # CLI flag: -dataobj.compaction.indexobj-builder.section-stripe-merge-limit
      [section_stripe_merge_limit: <int> | default = 2]

      # Expected compression ratio for log data, used to estimate compressed
      # output size from uncompressed buffered records. Only takes effect with
      # ordered append. Set to either 0 or 1 to disable.
      # CLI flag: -dataobj.compaction.indexobj-builder.estimated-compression-ratio
      [estimated_compression_ratio: <int> | default = 8]

    logsobj_builder:
      # The target maximum amount of uncompressed data to hold in data pages
      # (for columnar sections). Uncompressed size is used for consistent I/O
      # and planning.
      # CLI flag: -dataobj.compaction.logsobj-builder.target-page-size
      [target_page_size: <int> | default = 1MiB]

      # The maximum row count for pages to use for the data object builder. A
      # value of 0 means no limit.
      # CLI flag: -dataobj.compaction.logsobj-builder.max-page-rows
      [max_page_rows: <int> | default = 10000]

      # The target maximum size of the encoded object and all of its encoded
      # sections (after compression), to limit memory usage of a builder.
      # CLI flag: -dataobj.compaction.logsobj-builder.target-builder-memory-limit
      [target_object_size: <int> | default = 512MiB]

      # The target maximum amount of uncompressed data to hold in sections, for
      # sections that support being limited by size. Uncompressed size is used
      # for consistent I/O and planning.
      # CLI flag: -dataobj.compaction.logsobj-builder.target-section-size
      [target_section_size: <int> | default = 512MiB]

      # The size of logs to buffer in memory before adding into columnar
      # builders, used to reduce CPU load of sorting.
      # CLI flag: -dataobj.compaction.logsobj-builder.buffer-size
      [buffer_size: <int> | default = 128MiB]

      # The maximum number of dataobj section stripes to merge into a section at
      # once. Must be greater than 1.
      # CLI flag: -dataobj.compaction.logsobj-builder.section-stripe-merge-limit
      [section_stripe_merge_limit: <int> | default = 2]

      # Expected compression ratio for log data, used to estimate compressed
      # output size from uncompressed buffered records. Only takes effect with
      # ordered append. Set to either 0 or 1 to disable.
      # CLI flag: -dataobj.compaction.logsobj-builder.estimated-compression-ratio
      [estimated_compression_ratio: <int> | default = 8]

  # The prefix to use for the storage bucket.
  # CLI flag: -dataobj-storage-bucket-prefix
  [storage_bucket_prefix: <string> | default = "dataobj/"]

  # Enable data objects.
  # CLI flag: -dataobj.enabled
  [enabled: <boolean> | default = false]

  # Delay after which data objects hold all the data.
  # CLI flag: -dataobj.storage-lag
  [storage_lag: <duration> | default = 3h]

ingest_limits:
  # Enable the ingest limits service.
  # CLI flag: -ingest-limits.enabled
  [enabled: <boolean> | default = false]

  # The duration for which which streams are considered active. Streams that
  # have not been updated within this window are considered inactive and not
  # counted towards limits.
  # CLI flag: -ingest-limits.active-window
  [active_window: <duration> | default = 2h]

  # The time window for rate calculation. This should match the window used in
  # Prometheus rate() queries for consistency.
  # CLI flag: -ingest-limits.rate-window
  [rate_window: <duration> | default = 5m]

  # The size of the buckets used to calculate stream rates. Smaller buckets
  # provide more precise rates but require more memory.
  # CLI flag: -ingest-limits.bucket-size
  [bucket_size: <duration> | default = 1m]

  # The interval at which old streams are evicted.
  # CLI flag: -ingest-limits.eviction-interval
  [eviction_interval: <duration> | default = 10m]

  # Enable durability for stream sharding. Rate buckets are written to the
  # stream metadata topic and restored from it, which removes the warm-up period
  # after a restart or a partition rebalance during which streams are not
  # sharded. It adds one record per stream per bucket size.
  # CLI flag: -ingest-limits.stream-sharding-durability-enabled
  [stream_sharding_durability_enabled: <boolean> | default = false]

  # The number of partitions for the Kafka topic used to read and write stream
  # metadata. It is fixed, not a maximum.
  # CLI flag: -ingest-limits.num-partitions
  [num_partitions: <int> | default = 64]

  lifecycler:
    ring:
      kvstore:
        # Backend storage to use for the ring. Supported values are: consul,
        # etcd, inmemory, memberlist, multi.
        # CLI flag: -ingest-limits.store
        [store: <string> | default = "consul"]

        # The prefix for the keys in the store. Should end with a /.
        # CLI flag: -ingest-limits.prefix
        [prefix: <string> | default = "collectors/"]

        # Configuration for a Consul client. Only applies if the selected
        # kvstore is consul.
        # The CLI flags prefix for this block configuration is: ingest-limits
        [consul: <consul>]

        # Configuration for an ETCD v3 client. Only applies if the selected
        # kvstore is etcd.
        # The CLI flags prefix for this block configuration is: ingest-limits
        [etcd: <etcd>]

        multi:
          # Primary backend storage used by multi-client.
          # CLI flag: -ingest-limits.multi.primary
          [primary: <string> | default = ""]

          # Secondary backend storage used by multi-client.
          # CLI flag: -ingest-limits.multi.secondary
          [secondary: <string> | default = ""]

          # Mirror writes to the secondary store.
          # CLI flag: -ingest-limits.multi.mirror-enabled
          [mirror_enabled: <boolean> | default = false]

          # Timeout for storing a value to the secondary store.
          # CLI flag: -ingest-limits.multi.mirror-timeout
          [mirror_timeout: <duration> | default = 2s]

      # The heartbeat timeout after which ingesters are skipped for
      # reads/writes.
      # CLI flag: -ingest-limits.ring.heartbeat-timeout
      [heartbeat_timeout: <duration> | default = 1m]

      # The number of ingesters to write to and read from.
      # CLI flag: -ingest-limits.distributor.replication-factor
      [replication_factor: <int> | default = 3]

      # True to enable the zone-awareness and replicate ingested samples across
      # different availability zones.
      # CLI flag: -ingest-limits.distributor.zone-awareness-enabled
      [zone_awareness_enabled: <boolean> | default = false]

      # Comma-separated list of zones to exclude from the ring. Instances in
      # excluded zones will be filtered out from the ring.
      # CLI flag: -ingest-limits.distributor.excluded-zones
      [excluded_zones: <string> | default = ""]

    # Number of tokens for each ingester.
    # CLI flag: -ingest-limits.num-tokens
    [num_tokens: <int> | default = 128]

    # Period at which to heartbeat to consul.
    # CLI flag: -ingest-limits.heartbeat-period
    [heartbeat_period: <duration> | default = 5s]

    # Heartbeat timeout after which instance is assumed to be unhealthy.
    # CLI flag: -ingest-limits.heartbeat-timeout
    [heartbeat_timeout: <duration> | default = 1m]

    # Observe tokens after generating to resolve collisions. Useful when using
    # gossiping ring.
    # CLI flag: -ingest-limits.observe-period
    [observe_period: <duration> | default = 0s]

    # Period to wait for a claim from another member; will join automatically
    # after this.
    # CLI flag: -ingest-limits.join-after
    [join_after: <duration> | default = 0s]

    # Minimum duration to wait after the internal readiness checks have passed
    # but before succeeding the readiness endpoint. This is used to slowdown
    # deployment controllers (eg. Kubernetes) after an instance is ready and
    # before they proceed with a rolling update, to give the rest of the cluster
    # instances enough time to receive ring updates.
    # CLI flag: -ingest-limits.min-ready-duration
    [min_ready_duration: <duration> | default = 15s]

    # Name of network interface to read address from.
    # CLI flag: -ingest-limits.lifecycler.interface
    [interface_names: <list of strings> | default = [<private network interfaces>]]

    # Enable IPv6 support. Required to make use of IP addresses from IPv6
    # interfaces.
    # CLI flag: -ingest-limits.enable-inet6
    [enable_inet6: <boolean> | default = false]

    # Duration to sleep for before exiting, to ensure metrics are scraped.
    # CLI flag: -ingest-limits.final-sleep
    [final_sleep: <duration> | default = 0s]

    # File path where tokens are stored. If empty, tokens are not stored at
    # shutdown and restored at startup.
    # CLI flag: -ingest-limits.tokens-file-path
    [tokens_file_path: <string> | default = ""]

    # The availability zone where this instance is running.
    # CLI flag: -ingest-limits.availability-zone
    [availability_zone: <string> | default = ""]

    # Unregister from the ring upon clean shutdown. It can be useful to disable
    # for rolling restarts with consistent naming in conjunction with
    # -distributor.extend-writes=false.
    # CLI flag: -ingest-limits.unregister-on-shutdown
    [unregister_on_shutdown: <boolean> | default = true]

    # When enabled the readiness probe succeeds only after all instances are
    # ACTIVE and healthy in the ring, otherwise only the instance itself is
    # checked. This option should be disabled if in your cluster multiple
    # instances can be rolled out simultaneously, otherwise rolling updates may
    # be slowed down.
    # CLI flag: -ingest-limits.readiness-check-ring-health
    [readiness_check_ring_health: <boolean> | default = true]

    # IP address to advertise in the ring.
    # CLI flag: -ingest-limits.lifecycler.addr
    [address: <string> | default = ""]

    # port to advertise in consul (defaults to server.grpc-listen-port).
    # CLI flag: -ingest-limits.lifecycler.port
    [port: <int> | default = 0]

    # ID to register in the ring.
    # CLI flag: -ingest-limits.lifecycler.ID
    [id: <string> | default = "<hostname>"]

  # The consumer group for the Kafka topic used to read stream metadata records.
  # CLI flag: -ingest-limits.consumer-group
  [consumer_group: <string> | default = "ingest-limits"]

  # The topic for the Kafka topic used to read and write stream metadata
  # records.
  # CLI flag: -ingest-limits.topic
  [topic: <string> | default = ""]

ingest_limits_frontend:
  client_config:
    # Configures client gRPC connections to limits service.
    # The CLI flags prefix for this block configuration is:
    # ingest-limits-frontend.limits-client
    [grpc_client_config: <grpc_client>]

    # Configures client gRPC connections pool to limits service.
    pool_config:
      # How frequently to clean up clients for ingest-limits that have gone
      # away.
      # CLI flag: -ingest-limits-frontend.client-cleanup-period
      [client_cleanup_period: <duration> | default = 15s]

      # Run a health check on each ingest-limits client during periodic cleanup.
      # CLI flag: -ingest-limits-frontend.health-check-ingest-limits
      [health_check_ingest_limits: <boolean> | default = true]

      # Timeout for the health check.
      # CLI flag: -ingest-limits-frontend.remote-timeout
      [remote_timeout: <duration> | default = 1s]

  lifecycler:
    ring:
      kvstore:
        # Backend storage to use for the ring. Supported values are: consul,
        # etcd, inmemory, memberlist, multi.
        # CLI flag: -ingest-limits-frontend.store
        [store: <string> | default = "consul"]

        # The prefix for the keys in the store. Should end with a /.
        # CLI flag: -ingest-limits-frontend.prefix
        [prefix: <string> | default = "collectors/"]

        # Configuration for a Consul client. Only applies if the selected
        # kvstore is consul.
        # The CLI flags prefix for this block configuration is:
        # ingest-limits-frontend
        [consul: <consul>]

        # Configuration for an ETCD v3 client. Only applies if the selected
        # kvstore is etcd.
        # The CLI flags prefix for this block configuration is:
        # ingest-limits-frontend
        [etcd: <etcd>]

        multi:
          # Primary backend storage used by multi-client.
          # CLI flag: -ingest-limits-frontend.multi.primary
          [primary: <string> | default = ""]

          # Secondary backend storage used by multi-client.
          # CLI flag: -ingest-limits-frontend.multi.secondary
          [secondary: <string> | default = ""]

          # Mirror writes to the secondary store.
          # CLI flag: -ingest-limits-frontend.multi.mirror-enabled
          [mirror_enabled: <boolean> | default = false]

          # Timeout for storing a value to the secondary store.
          # CLI flag: -ingest-limits-frontend.multi.mirror-timeout
          [mirror_timeout: <duration> | default = 2s]

      # The heartbeat timeout after which ingesters are skipped for
      # reads/writes.
      # CLI flag: -ingest-limits-frontend.ring.heartbeat-timeout
      [heartbeat_timeout: <duration> | default = 1m]

      # The number of ingesters to write to and read from.
      # CLI flag: -ingest-limits-frontend.distributor.replication-factor
      [replication_factor: <int> | default = 3]

      # True to enable the zone-awareness and replicate ingested samples across
      # different availability zones.
      # CLI flag: -ingest-limits-frontend.distributor.zone-awareness-enabled
      [zone_awareness_enabled: <boolean> | default = false]

      # Comma-separated list of zones to exclude from the ring. Instances in
      # excluded zones will be filtered out from the ring.
      # CLI flag: -ingest-limits-frontend.distributor.excluded-zones
      [excluded_zones: <string> | default = ""]

    # Number of tokens for each ingester.
    # CLI flag: -ingest-limits-frontend.num-tokens
    [num_tokens: <int> | default = 128]

    # Period at which to heartbeat to consul.
    # CLI flag: -ingest-limits-frontend.heartbeat-period
    [heartbeat_period: <duration> | default = 5s]

    # Heartbeat timeout after which instance is assumed to be unhealthy.
    # CLI flag: -ingest-limits-frontend.heartbeat-timeout
    [heartbeat_timeout: <duration> | default = 1m]

    # Observe tokens after generating to resolve collisions. Useful when using
    # gossiping ring.
    # CLI flag: -ingest-limits-frontend.observe-period
    [observe_period: <duration> | default = 0s]

    # Period to wait for a claim from another member; will join automatically
    # after this.
    # CLI flag: -ingest-limits-frontend.join-after
    [join_after: <duration> | default = 0s]

    # Minimum duration to wait after the internal readiness checks have passed
    # but before succeeding the readiness endpoint. This is used to slowdown
    # deployment controllers (eg. Kubernetes) after an instance is ready and
    # before they proceed with a rolling update, to give the rest of the cluster
    # instances enough time to receive ring updates.
    # CLI flag: -ingest-limits-frontend.min-ready-duration
    [min_ready_duration: <duration> | default = 15s]

    # Name of network interface to read address from.
    # CLI flag: -ingest-limits-frontend.lifecycler.interface
    [interface_names: <list of strings> | default = [<private network interfaces>]]

    # Enable IPv6 support. Required to make use of IP addresses from IPv6
    # interfaces.
    # CLI flag: -ingest-limits-frontend.enable-inet6
    [enable_inet6: <boolean> | default = false]

    # Duration to sleep for before exiting, to ensure metrics are scraped.
    # CLI flag: -ingest-limits-frontend.final-sleep
    [final_sleep: <duration> | default = 0s]

    # File path where tokens are stored. If empty, tokens are not stored at
    # shutdown and restored at startup.
    # CLI flag: -ingest-limits-frontend.tokens-file-path
    [tokens_file_path: <string> | default = ""]

    # The availability zone where this instance is running.
    # CLI flag: -ingest-limits-frontend.availability-zone
    [availability_zone: <string> | default = ""]

    # Unregister from the ring upon clean shutdown. It can be useful to disable
    # for rolling restarts with consistent naming in conjunction with
    # -distributor.extend-writes=false.
    # CLI flag: -ingest-limits-frontend.unregister-on-shutdown
    [unregister_on_shutdown: <boolean> | default = true]

    # When enabled the readiness probe succeeds only after all instances are
    # ACTIVE and healthy in the ring, otherwise only the instance itself is
    # checked. This option should be disabled if in your cluster multiple
    # instances can be rolled out simultaneously, otherwise rolling updates may
    # be slowed down.
    # CLI flag: -ingest-limits-frontend.readiness-check-ring-health
    [readiness_check_ring_health: <boolean> | default = true]

    # IP address to advertise in the ring.
    # CLI flag: -ingest-limits-frontend.lifecycler.addr
    [address: <string> | default = ""]

    # port to advertise in consul (defaults to server.grpc-listen-port).
    # CLI flag: -ingest-limits-frontend.lifecycler.port
    [port: <int> | default = 0]

    # ID to register in the ring.
    # CLI flag: -ingest-limits-frontend.lifecycler.ID
    [id: <string> | default = "<hostname>"]

  # The number of partitions to use for the ring.
  # CLI flag: -ingest-limits-frontend.num-partitions
  [num_partitions: <int> | default = 64]

  # Enable the assigned partitions cache.
  # CLI flag: -ingest-limits-frontend.assigned-partitions-cache-enabled
  [assigned_partitions_cache_enabled: <boolean> | default = true]

  # The TTL for the assigned partitions cache.
  # CLI flag: -ingest-limits-frontend.assigned-partitions-cache-ttl
  [assigned_partitions_cache_ttl: <duration> | default = 1m]

  # [Experimental]: Enable the accepted streams cache.
  # CLI flag: -ingest-limits-frontend.accepted-streams-cache-enabled
  [accepted_streams_cache_enabled: <boolean> | default = false]

  # The TTL for the accepted streams cache.
  # CLI flag: -ingest-limits-frontend.accepted-streams-cache-ttl
  [accepted_streams_cache_ttl: <duration> | default = 1m]

  # The jitter to add to the accepted streams cache.
  # CLI flag: -ingest-limits-frontend.accepted-streams-cache-ttl-jitter
  [accepted_streams_cache_ttl_jitter: <duration> | default = 15s]

ingest_limits_frontend_client:
  # Configures client gRPC connections to limits service.
  # The CLI flags prefix for this block configuration is:
  # ingest-limits-frontend-client
  [grpc_client_config: <grpc_client>]

  # Configures client gRPC connections pool to limits service.
  pool_config:
    # How frequently to clean up clients for ingest-limits-frontend that have
    # gone away.
    # CLI flag: -ingest-limits-frontend-client.client-cleanup-period
    [client_cleanup_period: <duration> | default = 15s]

    # Run a health check on each ingest-limits-frontend client during periodic
    # cleanup.
    # CLI flag: -ingest-limits-frontend-client.health-check-ingest-limits
    [health_check_ingest_limits: <boolean> | default = true]

    # Timeout for the health check.
    # CLI flag: -ingest-limits-frontend-client.remote-timeout
    [remote_timeout: <duration> | default = 1s]

  # [Experimental]: Enable shuffle sharding.
  # CLI flag: -ingest-limits-frontend-client.shuffle-shard-enabled
  [shuffle_shard_enabled: <boolean> | default = false]

  # [Experimental]: The number of shards per tenant.
  # CLI flag: -ingest-limits-frontend-client.shuffle-shard-size
  [shuffle_shard_size: <int> | default = 1]

# Configuration for 'runtime config' module, responsible for reloading runtime
# configuration file.
[runtime_config: <runtime_config>]

# These are values which allow you to control aspects of Loki's operation, most
# commonly used for controlling types of higher verbosity logging, the values
# here can be overridden in the `configs` section of the `runtime_config` file.
[operational_config: <operational_config>]

# Configuration for tracing.
[tracing: <tracing>]

# Configuration for analytics.
[analytics: <analytics>]

# Configuration for profiling options.
[profiling: <profiling>]

# List of limit fields to publish from the tenant limits endpoint. If empty, all
# fields are returned. Use YAML field names (e.g., 'retention_period',
# 'max_query_series').
# CLI flag: -limits.tenant-limits-allow-publish
[tenant_limits_allow_publish: <list of strings> | default = [discover_log_levels discover_service_name log_level_fields max_entries_limit_per_query max_line_size_truncate max_query_bytes_read max_query_length max_query_lookback max_query_range max_query_series metric_aggregation_enabled otlp_config pattern_persistence_enabled query_timeout retention_period retention_stream volume_enabled volume_max_series]]

# List of /config fields to expose via /loki/api/v1/exposed-config, addressed by
# dot-separated path (e.g.
# 'distributor.otlp_config.default_resource_attributes_as_index_labels'). Empty
# by default, meaning nothing is exposed.
# CLI flag: -config.exposed-fields
[exposed_fields: <list of strings> | default = []]

# Common configuration to be shared between multiple modules. If a more specific
# configuration is given in other sections, the related configuration within
# this section will be ignored.
[common: <common>]

# How long to wait between SIGTERM and shutdown. After receiving SIGTERM, Loki
# will report 503 Service Unavailable status via /ready endpoint.
# CLI flag: -shutdown-delay
[shutdown_delay: <duration> | default = 0s]

# Namespace of the metrics that in previous releases had cortex as namespace.
# This setting is deprecated and will be removed in the next minor release.
# CLI flag: -metrics-namespace
[metrics_namespace: <string> | default = "loki"]
```

### alibabacloud_storage_config

The `alibabacloud_storage_config` block configures the connection to Alibaba Cloud Storage object storage backend. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `common.storage`
- `ruler.storage`

&nbsp;

```yaml
# Name of OSS bucket.
# CLI flag: -<prefix>.oss.bucketname
[bucket: <string> | default = ""]

# oss Endpoint to connect to.
# CLI flag: -<prefix>.oss.endpoint
[endpoint: <string> | default = ""]

# Alibabacloud Region to use.
# CLI flag: -<prefix>.oss.region
[region: <string> | default = ""]

# alibabacloud Access Key ID
# CLI flag: -<prefix>.oss.access-key-id
[access_key_id: <string> | default = ""]

# alibabacloud Secret Access Key
# CLI flag: -<prefix>.oss.secret-access-key
[secret_access_key: <string> | default = ""]

# Specify the RAM role name of the ECS instance. ECS RAM role authentication is
# used only when neither access_key_id nor secret_access_key is configured and
# requires signature_version=v4. If not set, the role name will be automatically
# retrieved from the ECS instance metadata.
# CLI flag: -<prefix>.oss.ram-role-name
[ram_role_name: <string> | default = ""]

# Connection timeout in seconds
# CLI flag: -<prefix>.oss.conn-timeout-sec
[conn_timeout_sec: <int> | default = 30]

# Read/Write timeout in seconds
# CLI flag: -<prefix>.oss.read-write-timeout-sec
[read_write_timeout_sec: <int> | default = 60]

# The signature version to use for authenticating against OSS. Supported values
# are: v1, v4. ECS RAM role authentication requires signature_version=v4.
# CLI flag: -<prefix>.oss.signature-version
[signature_version: <string> | default = "v1"]
```

### analytics

Configuration for `analytics`.

```yaml
# Enable anonymous usage reporting.
# CLI flag: -reporting.enabled
[reporting_enabled: <boolean> | default = true]

# URL to which reports are sent
# CLI flag: -reporting.usage-stats-url
[usage_stats_url: <string> | default = "https://stats.grafana.org/loki-usage-report"]

# URL to the proxy server
# CLI flag: -reporting.proxy-url
[proxy_url: <string> | default = ""]

# The TLS configuration.
# The CLI flags prefix for this block configuration is: reporting.tls-config
[tls_config: <tls_config>]
```

### attributes_config

Define actions for matching OpenTelemetry (OTEL) attributes.

```yaml
# Configures action to take on matching attributes. It allows one of
# [structured_metadata, drop] for all attribute types. It additionally allows
# index_label action for resource attributes
[action: <string> | default = ""]

# List of attributes to configure how to store them or drop them altogether
[attributes: <list of strings>]

# Regex to choose attributes to configure how to store them or drop them
# altogether
[regex: <Regexp>]
```

### azure_storage_config

The `azure_storage_config` block configures the connection to Azure object storage backend. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `common.storage`
- `ruler.storage`

&nbsp;

```yaml
# Azure Cloud environment. Supported values are: AzureGlobal, AzureChinaCloud,
# AzureGermanCloud, AzureUSGovernment.
# CLI flag: -<prefix>.azure.environment
[environment: <string> | default = "AzureGlobal"]

# Azure storage account name.
# CLI flag: -<prefix>.azure.account-name
[account_name: <string> | default = ""]

# Azure storage account key.
# CLI flag: -<prefix>.azure.account-key
[account_key: <string> | default = ""]

# If `connection-string` is set, the values of `account-name` and
# `endpoint-suffix` values will not be used. Use this method over `account-key`
# if you need to authenticate via a SAS token. Or if you use the Azurite
# emulator.
# CLI flag: -<prefix>.azure.connection-string
[connection_string: <string> | default = ""]

# Name of the storage account blob container used to store chunks. This
# container must be created before running cortex.
# CLI flag: -<prefix>.azure.container-name
[container_name: <string> | default = "loki"]

# Azure active directory endpoint override. Use when the Azure SDK does not
# support your environment.
# CLI flag: -<prefix>.azure.active-directory-endpoint
[active_directory_endpoint: <string> | default = ""]

# Azure storage endpoint suffix without schema. The storage account name will be
# prefixed to this value to create the FQDN.
# CLI flag: -<prefix>.azure.endpoint-suffix
[endpoint_suffix: <string> | default = ""]

# Use Managed Identity to authenticate to the Azure storage account.
# CLI flag: -<prefix>.azure.use-managed-identity
[use_managed_identity: <boolean> | default = false]

# Use Federated Token to authenticate to the Azure storage account.
# CLI flag: -<prefix>.azure.use-federated-token
[use_federated_token: <boolean> | default = false]

# User assigned identity ID to authenticate to the Azure storage account.
# CLI flag: -<prefix>.azure.user-assigned-id
[user_assigned_id: <string> | default = ""]

# Use Service Principal to authenticate through Azure OAuth.
# CLI flag: -<prefix>.azure.use-service-principal
[use_service_principal: <boolean> | default = false]

# Azure Service Principal ID(GUID).
# CLI flag: -<prefix>.azure.client-id
[client_id: <string> | default = ""]

# Azure Service Principal secret key.
# CLI flag: -<prefix>.azure.client-secret
[client_secret: <string> | default = ""]

# Azure Tenant ID is used to authenticate through Azure OAuth.
# CLI flag: -<prefix>.azure.tenant-id
[tenant_id: <string> | default = ""]

# Chunk delimiter for blob ID to be used
# CLI flag: -<prefix>.azure.chunk-delimiter
[chunk_delimiter: <string> | default = "-"]

# Preallocated buffer size for downloads.
# CLI flag: -<prefix>.azure.download-buffer-size
[download_buffer_size: <int> | default = 512000]

# Preallocated buffer size for uploads.
# CLI flag: -<prefix>.azure.upload-buffer-size
[upload_buffer_size: <int> | default = 256000]

# Number of buffers used to used to upload a chunk.
# CLI flag: -<prefix>.azure.download-buffer-count
[upload_buffer_count: <int> | default = 1]

# Timeout for requests made against azure blob storage.
# CLI flag: -<prefix>.azure.request-timeout
[request_timeout: <duration> | default = 30s]

# Number of retries for a request which times out.
# CLI flag: -<prefix>.azure.max-retries
[max_retries: <int> | default = 5]

# Minimum time to wait before retrying a request.
# CLI flag: -<prefix>.azure.min-retry-delay
[min_retry_delay: <duration> | default = 10ms]

# Maximum time to wait before retrying a request.
# CLI flag: -<prefix>.azure.max-retry-delay
[max_retry_delay: <duration> | default = 500ms]

http_config:
  # Skip TLS certificate verification for Azure blob storage connections.
  # CLI flag: -<prefix>.azure.http.insecure-skip-verify
  [insecure_skip_verify: <boolean> | default = false]

  # Path to a CA certificate file to trust for Azure blob storage TLS
  # connections.
  # CLI flag: -<prefix>.azure.http.tls-ca-path
  [tls_ca_path: <string> | default = ""]

  # Path to the client certificate for mutual TLS with Azure blob storage.
  # CLI flag: -<prefix>.azure.http.tls-cert-path
  [tls_cert_path: <string> | default = ""]

  # Path to the client key for mutual TLS with Azure blob storage.
  # CLI flag: -<prefix>.azure.http.tls-key-path
  [tls_key_path: <string> | default = ""]

  # Override the server name used in the TLS handshake with Azure blob storage.
  # CLI flag: -<prefix>.azure.http.tls-server-name
  [tls_server_name: <string> | default = ""]
```

### bloom_build

Deprecated: The `bloom_build` block configures the Loki bloom planner and builder servers, responsible for building bloom filters.

```yaml
# Flag to enable or disable the usage of the bloom-planner and bloom-builder
# components.
# CLI flag: -bloom-build.enabled
[enabled: <boolean> | default = false]

planner:
  # Interval at which to re-run the bloom creation planning.
  # CLI flag: -bloom-build.planner.interval
  [planning_interval: <duration> | default = 8h]

  # Newest day-table offset (from today, inclusive) to build blooms for. 0 start
  # building from today, 1 from yesterday and so on. Increase to lower cost by
  # not re-writing data to object storage too frequently since recent data
  # changes more often at the cost of not having blooms available as quickly.
  # CLI flag: -bloom-build.planner.min-table-offset
  [min_table_offset: <int> | default = 0]

  # Oldest day-table offset (from today, inclusive) to build blooms for. 1 till
  # yesterday, 2 till day before yesterday and so on. This can be used to lower
  # cost by not trying to build blooms for older data which doesn't change. This
  # can be optimized by aligning it with the maximum
  # `reject_old_samples_max_age` setting of any tenant.
  # CLI flag: -bloom-build.planner.max-table-offset
  [max_table_offset: <int> | default = 1]

  retention:
    # Enable bloom retention.
    # CLI flag: -bloom-build.planner.retention.enabled
    [enabled: <boolean> | default = false]

  queue:
    # Maximum number of tasks to queue per tenant.
    # CLI flag: -bloom-build.planner.queue.max-tasks-per-tenant
    [max_queued_tasks_per_tenant: <int> | default = 30000]

    # Whether to store tasks on disk.
    # CLI flag: -bloom-build.planner.queue.store-tasks-on-disk
    [store_tasks_on_disk: <boolean> | default = false]

    # Directory to store tasks on disk.
    # CLI flag: -bloom-build.planner.queue.tasks-disk-directory
    [tasks_disk_directory: <string> | default = "/tmp/bloom-planner-queue"]

    # Whether to clean the tasks directory on startup.
    # CLI flag: -bloom-build.planner.queue.clean-tasks-directory
    [clean_tasks_directory: <boolean> | default = false]

builder:
  # The grpc_client block configures the gRPC client used to communicate between
  # a client and server component in Loki.
  # The CLI flags prefix for this block configuration is:
  # bloom-build.builder.grpc
  [grpc_config: <grpc_client>]

  # Hostname (and port) of the bloom planner
  # CLI flag: -bloom-build.builder.planner-address
  [planner_address: <string> | default = ""]

  backoff_config:
    # Minimum delay when backing off.
    # CLI flag: -bloom-build.builder.backoff.backoff-min-period
    [min_period: <duration> | default = 100ms]

    # Maximum delay when backing off.
    # CLI flag: -bloom-build.builder.backoff.backoff-max-period
    [max_period: <duration> | default = 10s]

    # Number of times to backoff and retry before failing.
    # CLI flag: -bloom-build.builder.backoff.backoff-retries
    [max_retries: <int> | default = 10]
```

### bloom_gateway

Deprecated: The `bloom_gateway` block configures the Loki bloom gateway server, responsible for serving queries for filtering chunks based on filter expressions.

```yaml
# Flag to enable or disable the bloom gateway component globally.
# CLI flag: -bloom-gateway.enabled
[enabled: <boolean> | default = false]

client:
  # Configures the behavior of the connection pool.
  pool_config:
    # How frequently to update the list of servers.
    # CLI flag: -bloom-gateway-client.pool.check-interval
    [check_interval: <duration> | default = 15s]

  # The grpc_client block configures the gRPC client used to communicate between
  # a client and server component in Loki.
  # The CLI flags prefix for this block configuration is:
  # bloom-gateway-client.grpc
  [grpc_client_config: <grpc_client>]

  # Comma separated addresses list in DNS Service Discovery format:
  # https://grafana.com/docs/mimir/latest/configure/about-dns-service-discovery/#supported-discovery-modes
  # CLI flag: -bloom-gateway-client.addresses
  [addresses: <string> | default = ""]

# Number of workers to use for filtering chunks concurrently. Usually set to 1x
# number of CPU cores.
# CLI flag: -bloom-gateway.worker-concurrency
[worker_concurrency: <int> | default = 4]

# Number of blocks processed concurrently on a single worker. Usually set to 2x
# number of CPU cores.
# CLI flag: -bloom-gateway.block-query-concurrency
[block_query_concurrency: <int> | default = 8]

# Maximum number of outstanding tasks per tenant.
# CLI flag: -bloom-gateway.max-outstanding-per-tenant
[max_outstanding_per_tenant: <int> | default = 1024]

# How many tasks are multiplexed at once.
# CLI flag: -bloom-gateway.num-multiplex-tasks
[num_multiplex_tasks: <int> | default = 512]
```

### bos_storage_config

The `bos_storage_config` block configures the connection to Baidu Object Storage (BOS) object storage backend. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `common.storage`
- `ruler.storage`

&nbsp;

```yaml
# Name of BOS bucket.
# CLI flag: -<prefix>.bos.bucket-name
[bucket_name: <string> | default = ""]

# BOS endpoint to connect to.
# CLI flag: -<prefix>.bos.endpoint
[endpoint: <string> | default = "bj.bcebos.com"]

# Baidu Cloud Engine (BCE) Access Key ID.
# CLI flag: -<prefix>.bos.access-key-id
[access_key_id: <string> | default = ""]

# Baidu Cloud Engine (BCE) Secret Access Key.
# CLI flag: -<prefix>.bos.secret-access-key
[secret_access_key: <string> | default = ""]
```

### cache_config

The `cache_config` block configures the cache backend for a specific Loki component. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `bloom.metas-cache`
- `dataobj.metadata-cache`
- `frontend`
- `frontend.index-stats-results-cache`
- `frontend.instant-metric-results-cache`
- `frontend.label-results-cache`
- `frontend.series-results-cache`
- `frontend.volume-results-cache`
- `store.chunks-cache`
- `store.chunks-cache-l2`
- `tsdb.shipper.postings-cache`

&nbsp;

```yaml
# The default validity of entries for caches unless overridden.
# CLI flag: -<prefix>.default-validity
[default_validity: <duration> | default = 1h]

background:
  # At what concurrency to write back to cache.
  # CLI flag: -<prefix>.background.write-back-concurrency
  [writeback_goroutines: <int> | default = 1]

  # How many key batches to buffer for background write-back. Default is large
  # to prefer size based limiting.
  # CLI flag: -<prefix>.background.write-back-buffer
  [writeback_buffer: <int> | default = 500000]

  # Size limit in bytes for background write-back.
  # CLI flag: -<prefix>.background.write-back-size-limit
  [writeback_size_limit: <int> | default = 500MB]

memcached:
  # How long keys stay in the memcache.
  # CLI flag: -<prefix>.memcached.expiration
  [expiration: <duration> | default = 0s]

  # How many keys to fetch in each batch.
  # CLI flag: -<prefix>.memcached.batchsize
  [batch_size: <int> | default = 4]

  # Maximum active requests to memcache.
  # CLI flag: -<prefix>.memcached.parallelism
  [parallelism: <int> | default = 5]

memcached_client:
  # Hostname for memcached service to use. If empty and if addresses is unset,
  # no memcached will be used.
  # CLI flag: -<prefix>.memcached.hostname
  [host: <string> | default = ""]

  # SRV service used to discover memcache servers.
  # CLI flag: -<prefix>.memcached.service
  [service: <string> | default = "memcached"]

  # Comma separated addresses list in DNS Service Discovery format:
  # https://grafana.com/docs/mimir/latest/configure/about-dns-service-discovery/#supported-discovery-modes
  # CLI flag: -<prefix>.memcached.addresses
  [addresses: <string> | default = ""]

  # Maximum time to wait before giving up on memcached requests.
  # CLI flag: -<prefix>.memcached.timeout
  [timeout: <duration> | default = 100ms]

  # Maximum number of idle connections in pool.
  # CLI flag: -<prefix>.memcached.max-idle-conns
  [max_idle_conns: <int> | default = 16]

  # The maximum size of an item stored in memcached. Bigger items are not
  # stored. If set to 0, no maximum size is enforced.
  # CLI flag: -<prefix>.memcached.max-item-size
  [max_item_size: <int> | default = 0]

  # Period with which to poll DNS for memcache servers.
  # CLI flag: -<prefix>.memcached.update-interval
  [update_interval: <duration> | default = 1m]

  # Use consistent hashing to distribute to memcache servers.
  # CLI flag: -<prefix>.memcached.consistent-hash
  [consistent_hash: <boolean> | default = true]

  # Trip circuit-breaker after this number of consecutive dial failures (if zero
  # then circuit-breaker is disabled).
  # CLI flag: -<prefix>.memcached.circuit-breaker-consecutive-failures
  [circuit_breaker_consecutive_failures: <int> | default = 10]

  # Duration circuit-breaker remains open after tripping (if zero then 60
  # seconds is used).
  # CLI flag: -<prefix>.memcached.circuit-breaker-timeout
  [circuit_breaker_timeout: <duration> | default = 10s]

  # Reset circuit-breaker counts after this long (if zero then never reset).
  # CLI flag: -<prefix>.memcached.circuit-breaker-interval
  [circuit_breaker_interval: <duration> | default = 10s]

  # Enable connecting to Memcached with TLS.
  # CLI flag: -<prefix>.memcached.tls-enabled
  [tls_enabled: <boolean> | default = false]

  # The TLS configuration.
  # The CLI flags prefix for this block configuration is:
  # tsdb.shipper.postings-cache.memcached
  [<tls_config>]

redis:
  # Redis Server or Cluster configuration endpoint to use for caching. A
  # comma-separated list of endpoints for Redis Cluster or Redis Sentinel. If
  # empty, no redis will be used.
  # CLI flag: -<prefix>.redis.endpoint
  [endpoint: <string> | default = ""]

  # Redis Sentinel master name. An empty string for Redis Server or Redis
  # Cluster.
  # CLI flag: -<prefix>.redis.master-name
  [master_name: <string> | default = ""]

  # Maximum time to wait before giving up on redis requests.
  # CLI flag: -<prefix>.redis.timeout
  [timeout: <duration> | default = 500ms]

  # How long keys stay in the redis.
  # CLI flag: -<prefix>.redis.expiration
  [expiration: <duration> | default = 0s]

  # Database index.
  # CLI flag: -<prefix>.redis.db
  [db: <int> | default = 0]

  # Maximum number of connections in the pool.
  # CLI flag: -<prefix>.redis.pool-size
  [pool_size: <int> | default = 0]

  # Username to use when connecting to redis.
  # CLI flag: -<prefix>.redis.username
  [username: <string> | default = ""]

  # Password to use when connecting to redis.
  # CLI flag: -<prefix>.redis.password
  [password: <string> | default = ""]

  # Enable connecting to redis with TLS.
  # CLI flag: -<prefix>.redis.tls-enabled
  [tls_enabled: <boolean> | default = false]

  # Skip validating server certificate.
  # CLI flag: -<prefix>.redis.tls-insecure-skip-verify
  [tls_insecure_skip_verify: <boolean> | default = false]

  # Close connections after remaining idle for this duration. If the value is
  # zero, then idle connections are not closed.
  # CLI flag: -<prefix>.redis.idle-timeout
  [idle_timeout: <duration> | default = 0s]

  # Close connections older than this duration. If the value is zero, then the
  # pool does not close connections based on age.
  # CLI flag: -<prefix>.redis.max-connection-age
  [max_connection_age: <duration> | default = 0s]

  # By default, the Redis client only reads from the master node. Enabling this
  # option can lower pressure on the master node by randomly routing read-only
  # commands to the master and any available replicas.
  # CLI flag: -<prefix>.redis.route-randomly
  [route_randomly: <boolean> | default = false]

embedded_cache:
  # Whether embedded cache is enabled.
  # CLI flag: -<prefix>.embedded-cache.enabled
  [enabled: <boolean> | default = false]

  # Maximum memory size of the cache in MB.
  # CLI flag: -<prefix>.embedded-cache.max-size-mb
  [max_size_mb: <int> | default = 100]

  # Maximum number of entries in the cache.
  # CLI flag: -<prefix>.embedded-cache.max-size-items
  [max_size_items: <int> | default = 0]

  # The time to live for items in the cache before they get purged.
  # CLI flag: -<prefix>.embedded-cache.ttl
  [ttl: <duration> | default = 1h]
```

### chunk_store_config

The `chunk_store_config` block configures how chunks will be cached and how long to wait before saving them to the backing store.

```yaml
# The cache_config block configures the cache backend for a specific Loki
# component.
# The CLI flags prefix for this block configuration is: store.chunks-cache
[chunk_cache_config: <cache_config>]

# The cache_config block configures the cache backend for a specific Loki
# component.
# The CLI flags prefix for this block configuration is: store.chunks-cache-l2
[chunk_cache_config_l2: <cache_config>]

# Chunks fetched from queriers before this duration will not be written to the
# cache. A value of 0 will write all chunks to the cache
# CLI flag: -store.skip-query-writeback-older-than
[skip_query_writeback_cache_older_than: <duration> | default = 0s]

# Chunks will be handed off to the L2 cache after this duration. 0 to disable L2
# cache.
# CLI flag: -store.chunks-cache-l2.handoff
[l2_chunk_cache_handoff: <duration> | default = 0s]
```

### common

Common configuration to be shared between multiple modules. If a more specific configuration is given in other sections, the related configuration within this section will be ignored.

```yaml
# prefix for the path
# CLI flag: -common.path-prefix
[path_prefix: <string> | default = ""]

storage:
  # The s3_storage_config block configures the connection to Amazon S3 object
  # storage backend.
  # The CLI flags prefix for this block configuration is: common.storage
  [s3: <s3_storage_config>]

  # The gcs_storage_config block configures the connection to Google Cloud
  # Storage object storage backend.
  # The CLI flags prefix for this block configuration is: common.storage
  [gcs: <gcs_storage_config>]

  # The azure_storage_config block configures the connection to Azure object
  # storage backend.
  # The CLI flags prefix for this block configuration is: common.storage
  [azure: <azure_storage_config>]

  # The alibabacloud_storage_config block configures the connection to Alibaba
  # Cloud Storage object storage backend.
  # The CLI flags prefix for this block configuration is: common.storage
  [alibabacloud: <alibabacloud_storage_config>]

  # The bos_storage_config block configures the connection to Baidu Object
  # Storage (BOS) object storage backend.
  # The CLI flags prefix for this block configuration is: common.storage
  [bos: <bos_storage_config>]

  # The swift_storage_config block configures the connection to OpenStack Object
  # Storage (Swift) object storage backend.
  # The CLI flags prefix for this block configuration is: common.storage
  [swift: <swift_storage_config>]

  filesystem:
    # Directory to store chunks in.
    # CLI flag: -common.storage.filesystem.chunk-directory
    [chunks_directory: <string> | default = ""]

    # Directory to store rules in.
    # CLI flag: -common.storage.filesystem.rules-directory
    [rules_directory: <string> | default = ""]

  hedging:
    # If set to a non-zero value a second request will be issued at the provided
    # duration. Default is 0 (disabled)
    # CLI flag: -common.storage.hedge-requests-at
    [at: <duration> | default = 0s]

    # The maximum of hedge requests allowed.
    # CLI flag: -common.storage.hedge-requests-up-to
    [up_to: <int> | default = 2]

    # The maximum of hedge requests allowed per seconds.
    # CLI flag: -common.storage.hedge-max-per-second
    [max_per_second: <int> | default = 5]

  # The cos_storage_config block configures the connection to IBM Cloud Object
  # Storage (COS) backend.
  # The CLI flags prefix for this block configuration is: common.storage
  [cos: <cos_storage_config>]

  congestion_control:
    # Use storage congestion control (default: disabled).
    # CLI flag: -common.storage.congestion-control.enabled
    [enabled: <boolean> | default = false]

    controller:
      # Congestion control strategy to use (default: none, options: 'aimd').
      # CLI flag: -common.storage.congestion-control.strategy
      [strategy: <string> | default = ""]

      aimd:
        # AIMD starting throughput window size: how many requests can be sent
        # per second (default: 2000).
        # CLI flag: -common.storage.congestion-control.strategy.aimd.start
        [start: <int> | default = 2000]

        # AIMD maximum throughput window size: upper limit of requests sent per
        # second (default: 10000).
        # CLI flag: -common.storage.congestion-control.strategy.aimd.upper-bound
        [upper_bound: <int> | default = 10000]

        # AIMD backoff factor when upstream service is throttled to decrease
        # number of requests sent per second (default: 0.5).
        # CLI flag: -common.storage.congestion-control.strategy.aimd.backoff-factor
        [backoff_factor: <float> | default = 0.5]

    retry:
      # Congestion control retry strategy to use (default: none, options:
      # 'limited').
      # CLI flag: -common.storage.congestion-control.retry.strategy
      [strategy: <string> | default = ""]

      # Maximum number of retries allowed.
      # CLI flag: -common.storage.congestion-control.retry.strategy.limited.limit
      [limit: <int> | default = 2]

    hedging:
      config:
        [at: <duration>]

        [up_to: <int>]

        [max_per_second: <int>]

      # Congestion control hedge strategy to use (default: none, options:
      # 'limited').
      # CLI flag: -common.storage.congestion-control.hedge.strategy
      [strategy: <string> | default = ""]

  # The thanos_object_store_config block configures the connection to object
  # storage backend using thanos-io/objstore clients. This will become the
  # default way of configuring object store clients in future releases.
  # Currently this is opt-in and takes effect only when `-use-thanos-objstore`
  # is set to true.
  # The CLI flags prefix for this block configuration is:
  # common.storage.object-store
  [object_store: <thanos_object_store_config>]

[persist_tokens: <boolean>]

[replication_factor: <int>]

ring:
  kvstore:
    # Backend storage to use for the ring. Supported values are: consul, etcd,
    # inmemory, memberlist, multi.
    # CLI flag: -common.storage.ring.store
    [store: <string> | default = "consul"]

    # The prefix for the keys in the store. Should end with a /.
    # CLI flag: -common.storage.ring.prefix
    [prefix: <string> | default = "collectors/"]

    # Configuration for a Consul client. Only applies if the selected kvstore is
    # consul.
    # The CLI flags prefix for this block configuration is: common.storage.ring
    [consul: <consul>]

    # Configuration for an ETCD v3 client. Only applies if the selected kvstore
    # is etcd.
    # The CLI flags prefix for this block configuration is: common.storage.ring
    [etcd: <etcd>]

    multi:
      # Primary backend storage used by multi-client.
      # CLI flag: -common.storage.ring.multi.primary
      [primary: <string> | default = ""]

      # Secondary backend storage used by multi-client.
      # CLI flag: -common.storage.ring.multi.secondary
      [secondary: <string> | default = ""]

      # Mirror writes to the secondary store.
      # CLI flag: -common.storage.ring.multi.mirror-enabled
      [mirror_enabled: <boolean> | default = false]

      # Timeout for storing a value to the secondary store.
      # CLI flag: -common.storage.ring.multi.mirror-timeout
      [mirror_timeout: <duration> | default = 2s]

  # Period at which to heartbeat to the ring.
  # CLI flag: -common.storage.ring.heartbeat-period
  [heartbeat_period: <duration> | default = 15s]

  # The heartbeat timeout after which compactors are considered unhealthy within
  # the ring. 0 = never (timeout disabled).
  # CLI flag: -common.storage.ring.heartbeat-timeout
  [heartbeat_timeout: <duration> | default = 1m]

  # File path where tokens are stored. If empty, tokens are not stored at
  # shutdown and restored at startup.
  # CLI flag: -common.storage.ring.tokens-file-path
  [tokens_file_path: <string> | default = ""]

  # True to enable zone-awareness and replicate blocks across different
  # availability zones.
  # CLI flag: -common.storage.ring.zone-awareness-enabled
  [zone_awareness_enabled: <boolean> | default = false]

  # Number of tokens to own in the ring.
  # CLI flag: -common.storage.ring.num-tokens
  [num_tokens: <int> | default = 128]

  # Factor for data replication.
  # CLI flag: -common.storage.ring.replication-factor
  [replication_factor: <int> | default = 3]

  # Instance ID to register in the ring.
  # CLI flag: -common.storage.ring.instance-id
  [instance_id: <string> | default = "<hostname>"]

  # Name of network interface to read address from.
  # CLI flag: -common.storage.ring.instance-interface-names
  [instance_interface_names: <list of strings> | default = [<private network interfaces>]]

  # Port to advertise in the ring (defaults to server.grpc-listen-port).
  # CLI flag: -common.storage.ring.instance-port
  [instance_port: <int> | default = 0]

  # IP address to advertise in the ring.
  # CLI flag: -common.storage.ring.instance-addr
  [instance_addr: <string> | default = ""]

  # The availability zone where this instance is running. Required if
  # zone-awareness is enabled.
  # CLI flag: -common.storage.ring.instance-availability-zone
  [instance_availability_zone: <string> | default = ""]

  # Enable using a IPv6 instance address.
  # CLI flag: -common.storage.ring.instance-enable-ipv6
  [instance_enable_ipv6: <boolean> | default = false]

[instance_interface_names: <list of strings> | default = [<private network interfaces>]]

[instance_addr: <string> | default = ""]

# the http address of the compactor in the form http://host:port
# CLI flag: -common.compactor-address
[compactor_address: <string> | default = ""]

# the grpc address of the compactor in the form host:port
# CLI flag: -common.compactor-grpc-address
[compactor_grpc_address: <string> | default = ""]

# Experimental: path to use for temporary data, where scratch data is supported.
# CLI flag: -common.scratch-path
[scratch_path: <string> | default = ""]
```

### compactor

The `compactor` block configures the compactor component, which compacts index shards for performance.

```yaml
# Directory where files can be downloaded for compaction.
# CLI flag: -compactor.working-directory
[working_directory: <string> | default = "/var/loki/compactor"]

# Interval at which to re-run the compaction operation.
# CLI flag: -compactor.compaction-interval
[compaction_interval: <duration> | default = 10m]

# Interval at which to apply/enforce retention. 0 means run at same interval as
# compaction. If non-zero, it should always be a multiple of compaction
# interval.
# CLI flag: -compactor.apply-retention-interval
[apply_retention_interval: <duration> | default = 0s]

# Activate custom (per-stream,per-tenant) retention.
# CLI flag: -compactor.retention-enabled
[retention_enabled: <boolean> | default = false]

# Delay after which chunks will be fully deleted during retention.
# CLI flag: -compactor.retention-delete-delay
[retention_delete_delay: <duration> | default = 2h]

# The total amount of worker to use to delete chunks.
# CLI flag: -compactor.retention-delete-worker-count
[retention_delete_worker_count: <int> | default = 150]

# The maximum amount of time to spend running retention and deletion on any
# given table in the index.
# CLI flag: -compactor.retention-table-timeout
[retention_table_timeout: <duration> | default = 0s]

retention_backoff_config:
  # Minimum delay when backing off.
  # CLI flag: -compactor.retention-backoff-config.backoff-min-period
  [min_period: <duration> | default = 100ms]

  # Maximum delay when backing off.
  # CLI flag: -compactor.retention-backoff-config.backoff-max-period
  [max_period: <duration> | default = 10s]

  # Number of times to backoff and retry before failing.
  # CLI flag: -compactor.retention-backoff-config.backoff-retries
  [max_retries: <int> | default = 10]

# Store used for managing delete requests.
# CLI flag: -compactor.delete-request-store
[delete_request_store: <string> | default = ""]

# Path prefix for storing delete requests.
# CLI flag: -compactor.delete-request-store.key-prefix
[delete_request_store_key_prefix: <string> | default = "index/"]

# Type of DB to use for storing delete requests. Supported types: boltdb, sqlite
# CLI flag: -compactor.delete-request-store.db-type
[delete_request_store_db_type: <string> | default = "boltdb"]

# Type of DB to use as backup for storing delete requests. Backup DB should
# ideally be used while migrating from one DB type to another. Supported
# type(s): boltdb
# CLI flag: -compactor.delete-request-store.backup-db-type
[backup_delete_request_store_db_type: <string> | default = ""]

# The max number of delete requests to run per compaction cycle.
# CLI flag: -compactor.delete-batch-size
[delete_batch_size: <int> | default = 70]

# Allow cancellation of delete request until duration after they are created.
# Data would be deleted only after delete requests have been older than this
# duration. Ideally this should be set to at least 24h.
# CLI flag: -compactor.delete-request-cancel-period
[delete_request_cancel_period: <duration> | default = 24h]

# Constrain the size of any single delete request with line filters. When a
# delete request > delete_max_interval is input, the request is sharded into
# smaller requests of no more than delete_max_interval
# CLI flag: -compactor.delete-max-interval
[delete_max_interval: <duration> | default = 24h]

# Do not fail processing of a delete request with a line filter when a chunk it
# needs to rebuild is indexed but missing from the object storage. When enabled,
# the missing chunk is skipped and its index entry is left as is for diagnosing
# the issue. Chunks skipped this way are counted by the
# loki_compactor_deletion_missing_chunks_total metric and logged with their
# chunk IDs. This setting has no effect when horizontal_scaling_mode is not
# disabled. CAUTION: enable this only as a temporary escape hatch to unblock
# delete requests, and only after verifying against the object storage that the
# chunks really are missing. If a chunk gets skipped while it is in fact still
# present, the data it holds is left undeleted even though the delete request is
# reported as processed.
# CLI flag: -compactor.deletion-ignore-missing-chunks
[deletion_ignore_missing_chunks: <boolean> | default = false]

# Maximum number of tables to compact in parallel. While increasing this value,
# please make sure compactor has enough disk space allocated to be able to store
# and compact as many tables.
# CLI flag: -compactor.max-compaction-parallelism
[max_compaction_parallelism: <int> | default = 1]

# Number of upload/remove operations to execute in parallel when finalizing a
# compaction. NOTE: This setting is per compaction operation, which can be
# executed in parallel. The upper bound on the number of concurrent uploads is
# upload_parallelism * max_compaction_parallelism.
# CLI flag: -compactor.upload-parallelism
[upload_parallelism: <int> | default = 10]

# The hash ring configuration used by compactors to elect a single instance for
# running compactions. The CLI flags prefix for this block config is:
# compactor.ring
compactor_ring:
  kvstore:
    # Backend storage to use for the ring. Supported values are: consul, etcd,
    # inmemory, memberlist, multi.
    # CLI flag: -compactor.ring.store
    [store: <string> | default = "consul"]

    # The prefix for the keys in the store. Should end with a /.
    # CLI flag: -compactor.ring.prefix
    [prefix: <string> | default = "collectors/"]

    # Configuration for a Consul client. Only applies if the selected kvstore is
    # consul.
    # The CLI flags prefix for this block configuration is: compactor.ring
    [consul: <consul>]

    # Configuration for an ETCD v3 client. Only applies if the selected kvstore
    # is etcd.
    # The CLI flags prefix for this block configuration is: compactor.ring
    [etcd: <etcd>]

    multi:
      # Primary backend storage used by multi-client.
      # CLI flag: -compactor.ring.multi.primary
      [primary: <string> | default = ""]

      # Secondary backend storage used by multi-client.
      # CLI flag: -compactor.ring.multi.secondary
      [secondary: <string> | default = ""]

      # Mirror writes to the secondary store.
      # CLI flag: -compactor.ring.multi.mirror-enabled
      [mirror_enabled: <boolean> | default = false]

      # Timeout for storing a value to the secondary store.
      # CLI flag: -compactor.ring.multi.mirror-timeout
      [mirror_timeout: <duration> | default = 2s]

  # Period at which to heartbeat to the ring.
  # CLI flag: -compactor.ring.heartbeat-period
  [heartbeat_period: <duration> | default = 15s]

  # The heartbeat timeout after which compactors are considered unhealthy within
  # the ring. 0 = never (timeout disabled).
  # CLI flag: -compactor.ring.heartbeat-timeout
  [heartbeat_timeout: <duration> | default = 1m]

  # File path where tokens are stored. If empty, tokens are not stored at
  # shutdown and restored at startup.
  # CLI flag: -compactor.ring.tokens-file-path
  [tokens_file_path: <string> | default = ""]

  # True to enable zone-awareness and replicate blocks across different
  # availability zones.
  # CLI flag: -compactor.ring.zone-awareness-enabled
  [zone_awareness_enabled: <boolean> | default = false]

  # Instance ID to register in the ring.
  # CLI flag: -compactor.ring.instance-id
  [instance_id: <string> | default = "<hostname>"]

  # Name of network interface to read address from.
  # CLI flag: -compactor.ring.instance-interface-names
  [instance_interface_names: <list of strings> | default = [<private network interfaces>]]

  # Port to advertise in the ring (defaults to server.grpc-listen-port).
  # CLI flag: -compactor.ring.instance-port
  [instance_port: <int> | default = 0]

  # IP address to advertise in the ring.
  # CLI flag: -compactor.ring.instance-addr
  [instance_addr: <string> | default = ""]

  # The availability zone where this instance is running. Required if
  # zone-awareness is enabled.
  # CLI flag: -compactor.ring.instance-availability-zone
  [instance_availability_zone: <string> | default = ""]

  # Enable using a IPv6 instance address.
  # CLI flag: -compactor.ring.instance-enable-ipv6
  [instance_enable_ipv6: <boolean> | default = false]

# Number of tables that compactor will try to compact. Newer tables are chosen
# when this is less than the number of tables available.
# CLI flag: -compactor.tables-to-compact
[tables_to_compact: <int> | default = 0]

# Do not compact N latest tables. Together with -compactor.run-once and
# -compactor.tables-to-compact, this is useful when clearing compactor backlogs.
# CLI flag: -compactor.skip-latest-n-tables
[skip_latest_n_tables: <int> | default = 0]

# Experimental: Configuration to turn on and run horizontally scalable
# compactor. Supported modes - [disabled]: Keeps the horizontal scaling mode
# disabled. Locally runs all the functions of the compactor.[main]: Runs all
# functions of the compactor. Distributes work to workers where
# possible.[worker]: Runs the compactor in worker mode, only working on jobs
# built by the main compactor.
# CLI flag: -compactor.horizontal-scaling-mode
[horizontal_scaling_mode: <string> | default = "disabled"]

worker_config:
  # Number of sub-workers to run for concurrent processing of jobs.
  # CLI flag: -compactor.worker.num-sub-workers
  [num_sub_workers: <int> | default = 4]

jobs_config:
  deletion:
    # Object storage path prefix for storing deletion manifests.
    # CLI flag: -compactor.jobs.deletion.deletion-manifest-store-prefix
    [deletion_manifest_store_prefix: <string> | default = "__deletion_manifest__/"]

    # Maximum number of chunks to process concurrently in each worker.
    # CLI flag: -compactor.jobs.deletion.chunk-processing-concurrency
    [chunk_processing_concurrency: <int> | default = 3]

    # Maximum time to wait for a job before considering it failed and retrying.
    # CLI flag: -compactor.jobs.deletion.timeout
    [timeout: <duration> | default = 15m]

    # Maximum number of times to retry a failed or timed out job.
    # CLI flag: -compactor.jobs.deletion.max-retries
    [max_retries: <int> | default = 3]

# Object storage path prefix for storing deletion markers. The prefix must end
# with a forward slash(/). Leave empty to continue to store deletion markers on
# the local disk.
# CLI flag: -compactor.deletion-marker-object-store-prefix
[deletion_marker_object_store_prefix: <string> | default = ""]
```

### consul

Configuration for a Consul client. Only applies if the selected kvstore is `consul`. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `common.storage.ring`
- `compactor.ring`
- `distributor.ring`
- `index-gateway.ring`
- `ingest-limits`
- `ingest-limits-frontend`
- `ingester.partition-ring`
- `pattern-ingester`
- `query-scheduler.ring`
- `ruler.ring`

&nbsp;

```yaml
# Hostname and port of Consul.
# CLI flag: -<prefix>.consul.hostname
[host: <string> | default = "localhost:8500"]

# ACL Token used to interact with Consul.
# CLI flag: -<prefix>.consul.acl-token
[acl_token: <string> | default = ""]

# HTTP timeout when talking to Consul
# CLI flag: -<prefix>.consul.client-timeout
[http_client_timeout: <duration> | default = 20s]

# Enable consistent reads to Consul.
# CLI flag: -<prefix>.consul.consistent-reads
[consistent_reads: <boolean> | default = false]

# Rate limit when watching key or prefix in Consul, in requests per second. 0
# disables the rate limit.
# CLI flag: -<prefix>.consul.watch-rate-limit
[watch_rate_limit: <float> | default = 1]

# Burst size used in rate limit. Values less than 1 are treated as 1.
# CLI flag: -<prefix>.consul.watch-burst-size
[watch_burst_size: <int> | default = 1]

# Maximum duration to wait before retrying a Compare And Swap (CAS) operation.
# CLI flag: -<prefix>.consul.cas-retry-delay
[cas_retry_delay: <duration> | default = 1s]
```

### cos_storage_config

The `cos_storage_config` block configures the connection to IBM Cloud Object Storage (COS) backend. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `common.storage`
- `ruler.storage`

&nbsp;

```yaml
# Set this to `true` to force the request to use path-style addressing.
# CLI flag: -<prefix>.cos.force-path-style
[forcepathstyle: <boolean> | default = false]

# Comma separated list of bucket names to evenly distribute chunks over.
# CLI flag: -<prefix>.cos.buckets
[bucketnames: <string> | default = ""]

# COS Endpoint to connect to.
# CLI flag: -<prefix>.cos.endpoint
[endpoint: <string> | default = ""]

# COS region to use.
# CLI flag: -<prefix>.cos.region
[region: <string> | default = ""]

# COS HMAC Access Key ID.
# CLI flag: -<prefix>.cos.access-key-id
[access_key_id: <string> | default = ""]

# COS HMAC Secret Access Key.
# CLI flag: -<prefix>.cos.secret-access-key
[secret_access_key: <string> | default = ""]

http_config:
  # The maximum amount of time an idle connection will be held open.
  # CLI flag: -<prefix>.cos.http.idle-conn-timeout
  [idle_conn_timeout: <duration> | default = 1m30s]

  # If non-zero, specifies the amount of time to wait for a server's response
  # headers after fully writing the request.
  # CLI flag: -<prefix>.cos.http.response-header-timeout
  [response_header_timeout: <duration> | default = 0s]

# Configures back off when cos get Object.
backoff_config:
  # Minimum backoff time when cos get Object.
  # CLI flag: -<prefix>.cos.min-backoff
  [min_period: <duration> | default = 100ms]

  # Maximum backoff time when cos get Object.
  # CLI flag: -<prefix>.cos.max-backoff
  [max_period: <duration> | default = 3s]

  # Maximum number of times to retry when cos get Object.
  # CLI flag: -<prefix>.cos.max-retries
  [max_retries: <int> | default = 5]

# IAM API key to access COS.
# CLI flag: -<prefix>.cos.api-key
[api_key: <string> | default = ""]

# COS service instance id to use.
# CLI flag: -<prefix>.cos.service-instance-id
[service_instance_id: <string> | default = ""]

# IAM Auth Endpoint for authentication.
# CLI flag: -<prefix>.cos.auth-endpoint
[auth_endpoint: <string> | default = "https://iam.cloud.ibm.com/identity/token"]

# Compute resource token file path.
# CLI flag: -<prefix>.cos.cr-token-file-path
[cr_token_file_path: <string> | default = ""]

# Name of the trusted profile.
# CLI flag: -<prefix>.cos.trusted-profile-name
[trusted_profile_name: <string> | default = ""]

# ID of the trusted profile.
# CLI flag: -<prefix>.cos.trusted-profile-id
[trusted_profile_id: <string> | default = ""]
```

### distributor

Configures the `distributor`.

```yaml
ring:
  kvstore:
    # Backend storage to use for the ring. Supported values are: consul, etcd,
    # inmemory, memberlist, multi.
    # CLI flag: -distributor.ring.store
    [store: <string> | default = "consul"]

    # The prefix for the keys in the store. Should end with a /.
    # CLI flag: -distributor.ring.prefix
    [prefix: <string> | default = "collectors/"]

    # Configuration for a Consul client. Only applies if the selected kvstore is
    # consul.
    # The CLI flags prefix for this block configuration is: distributor.ring
    [consul: <consul>]

    # Configuration for an ETCD v3 client. Only applies if the selected kvstore
    # is etcd.
    # The CLI flags prefix for this block configuration is: distributor.ring
    [etcd: <etcd>]

    multi:
      # Primary backend storage used by multi-client.
      # CLI flag: -distributor.ring.multi.primary
      [primary: <string> | default = ""]

      # Secondary backend storage used by multi-client.
      # CLI flag: -distributor.ring.multi.secondary
      [secondary: <string> | default = ""]

      # Mirror writes to the secondary store.
      # CLI flag: -distributor.ring.multi.mirror-enabled
      [mirror_enabled: <boolean> | default = false]

      # Timeout for storing a value to the secondary store.
      # CLI flag: -distributor.ring.multi.mirror-timeout
      [mirror_timeout: <duration> | default = 2s]

  # Period at which to heartbeat to the ring.
  # CLI flag: -distributor.ring.heartbeat-period
  [heartbeat_period: <duration> | default = 5s]

  # The heartbeat timeout after which distributors are considered unhealthy
  # within the ring. 0 = never (timeout disabled).
  # CLI flag: -distributor.ring.heartbeat-timeout
  [heartbeat_timeout: <duration> | default = 1m]

  # Name of network interface to read address from.
  # CLI flag: -distributor.ring.instance-interface-names
  [instance_interface_names: <list of strings> | default = [<private network interfaces>]]

# Number of workers to push batches to ingesters.
# CLI flag: -distributor.push-worker-count
[push_worker_count: <int> | default = 256]

# The maximum number of inflight bytes at a time. 0 means disabled.
# CLI flag: -distributor.max-inflight-bytes
[max_inflight_bytes: <int> | default = 0]

rate_store:
  # The max number of concurrent requests to make to ingester stream apis
  # CLI flag: -distributor.rate-store.max-request-parallelism
  [max_request_parallelism: <int> | default = 200]

  # The interval on which distributors will update current stream rates from
  # ingesters
  # CLI flag: -distributor.rate-store.stream-rate-update-interval
  [stream_rate_update_interval: <duration> | default = 1s]

  # Timeout for communication between distributors and any given ingester when
  # updating rates
  # CLI flag: -distributor.rate-store.ingester-request-timeout
  [ingester_request_timeout: <duration> | default = 500ms]

  # If enabled, detailed logs and spans will be emitted.
  # CLI flag: -distributor.rate-store.debug
  [debug: <boolean> | default = false]

# Customize the logging of write failures.
write_failures_logging:
  # Log volume allowed (per second). Default: 1KB.
  # CLI flag: -distributor.write-failures-logging.rate
  [rate: <int> | default = 1KB]

  # Whether a insight=true key should be logged or not. Default: false.
  # CLI flag: -distributor.write-failures-logging.add-insights-label
  [add_insights_label: <boolean> | default = false]

# Customize the logging of OTLP attribute expansion.
otlp_attribute_logging:
  # Number of attribute expansion reports to emit per second, per tenant. Each
  # report is one summary line plus one line per attribute.
  # CLI flag: -distributor.otlp-attribute-logging.rate
  [rate: <float> | default = 1]

  # Maximum number of attributes to log on their own line for a reported
  # request. The remaining attributes are summarised on a single overflow line.
  # CLI flag: -distributor.otlp-attribute-logging.max-attributes
  [max_attributes: <int> | default = 20]

otlp_config:
  # Controls when OTLP resource and scope attributes are expanded: during
  # parsing when false, or when needed by downstream components when true.
  # CLI flag: -distributor.otlp.defer-attribute-expansion
  [defer_attribute_expansion: <boolean> | default = false]

  # List of default otlp resource attributes to be picked as index labels
  # CLI flag: -distributor.otlp.default_resource_attributes_as_index_labels
  [default_resource_attributes_as_index_labels: <list of strings> | default = [service.name service.namespace service.instance.id deployment.environment deployment.environment.name cloud.region cloud.availability_zone k8s.cluster.name k8s.namespace.name k8s.pod.name k8s.container.name container.name k8s.replicaset.name k8s.deployment.name k8s.statefulset.name k8s.daemonset.name k8s.cronjob.name k8s.job.name]]

# Default policy stream mappings that are merged with per-tenant mappings.
[default_policy_stream_mappings: <map of string to list of PriorityStreams>]

# Enable writes to Kafka during Push requests.
# CLI flag: -distributor.kafka-writes-enabled
[kafka_writes_enabled: <boolean> | default = false]

# Enable writes to Ingesters during Push requests. Defaults to true.
# CLI flag: -distributor.ingester-writes-enabled
[ingester_writes_enabled: <boolean> | default = true]

# Enable checking limits against the ingest-limits service. Defaults to false.
# CLI flag: -distributor.ingest-limits-enabled
[ingest_limits_enabled: <boolean> | default = false]

# Enable dry-run mode where limits are checked the ingest-limits service, but
# not enforced. Defaults to false.
# CLI flag: -distributor.ingest-limits-dry-run-enabled
[ingest_limits_dry_run_enabled: <boolean> | default = false]

circuit_breaker:
  # Enable circuit breakers.
  # CLI flag: -distributor.circuit-breaker.enabled
  [enabled: <boolean> | default = false]

  # The open period.
  # CLI flag: -distributor.circuit-breaker.open-period
  [open_period: <duration> | default = 1s]

  # The minimum number of successive failures required to open the circuit
  # breaker.
  # CLI flag: -distributor.circuit-breaker.min-failures
  [min_failures: <int> | default = 1]

  # The number of permitted trial requests in the half-open state. All requests
  # must succeed to close the circuit breaker, any failure re-opens it.
  # CLI flag: -distributor.circuit-breaker.permitted-trials
  [permitted_trials: <int> | default = 1]
```

### etcd

Configuration for an ETCD v3 client. Only applies if the selected kvstore is `etcd`. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `common.storage.ring`
- `compactor.ring`
- `distributor.ring`
- `index-gateway.ring`
- `ingest-limits`
- `ingest-limits-frontend`
- `ingester.partition-ring`
- `pattern-ingester`
- `query-scheduler.ring`
- `ruler.ring`

&nbsp;

```yaml
# The etcd endpoints to connect to.
# CLI flag: -<prefix>.etcd.endpoints
[endpoints: <list of strings> | default = []]

# The dial timeout for the etcd connection.
# CLI flag: -<prefix>.etcd.dial-timeout
[dial_timeout: <duration> | default = 10s]

# The maximum number of retries to do for failed ops.
# CLI flag: -<prefix>.etcd.max-retries
[max_retries: <int> | default = 10]

# Enable TLS.
# CLI flag: -<prefix>.etcd.tls-enabled
[tls_enabled: <boolean> | default = false]

# The TLS configuration.
# The CLI flags prefix for this block configuration is: ruler.ring.etcd
[<tls_config>]

# Etcd username.
# CLI flag: -<prefix>.etcd.username
[username: <string> | default = ""]

# Etcd password.
# CLI flag: -<prefix>.etcd.password
[password: <string> | default = ""]
```

### frontend

The `frontend` block configures the Loki query-frontend.

```yaml
# Log queries that are slower than the specified duration. Set to 0 to disable.
# Set to < 0 to enable on all queries.
# CLI flag: -frontend.log-queries-longer-than
[log_queries_longer_than: <duration> | default = 0s]

# Comma-separated list of request header names to include in query logs. Applies
# to both query stats and slow queries logs.
# CLI flag: -frontend.log-query-request-headers
[log_query_request_headers: <string> | default = ""]

# Max body size for downstream prometheus.
# CLI flag: -frontend.max-body-size
[max_body_size: <int> | default = 10485760]

# True to enable query statistics tracking. When enabled, a message with some
# statistics is logged for every query.
# CLI flag: -frontend.query-stats-enabled
[query_stats_enabled: <boolean> | default = false]

# Maximum number of outstanding requests per tenant per frontend; requests
# beyond this error with HTTP 429.
# CLI flag: -querier.max-outstanding-requests-per-tenant
[max_outstanding_per_tenant: <int> | default = 2048]

# In the event a tenant is repeatedly sending queries that lead the querier to
# crash or be killed due to an out-of-memory error, the crashed querier will be
# disconnected from the query frontend and a new querier will be immediately
# assigned to the tenant’s shard. This invalidates the assumption that shuffle
# sharding can be used to reduce the impact on tenants. This option mitigates
# the impact by configuring a delay between when a querier disconnects because
# of a crash and when the crashed querier is actually removed from the tenant's
# shard.
# CLI flag: -query-frontend.querier-forget-delay
[querier_forget_delay: <duration> | default = 0s]

# DNS hostname used for finding query-schedulers.
# CLI flag: -frontend.scheduler-address
[scheduler_address: <string> | default = ""]

# How often to resolve the scheduler-address, in order to look for new
# query-scheduler instances. Also used to determine how often to poll the
# scheduler-ring for addresses if the scheduler-ring is configured.
# CLI flag: -frontend.scheduler-dns-lookup-period
[scheduler_dns_lookup_period: <duration> | default = 10s]

# Number of concurrent workers forwarding queries to single query-scheduler.
# CLI flag: -frontend.scheduler-worker-concurrency
[scheduler_worker_concurrency: <int> | default = 5]

# The grpc_client block configures the gRPC client used to communicate between a
# client and server component in Loki.
# The CLI flags prefix for this block configuration is:
# frontend.grpc-client-config
[grpc_client_config: <grpc_client>]

# Time to wait for inflight requests to finish before forcefully shutting down.
# This needs to be aligned with the query timeout and the graceful termination
# period of the process orchestrator.
# CLI flag: -frontend.graceful-shutdown-timeout
[graceful_shutdown_timeout: <duration> | default = 5m]

# Name of network interface to read address from. This address is sent to
# query-scheduler and querier, which uses it to send the query response back to
# query-frontend.
# CLI flag: -frontend.instance-interface-names
[instance_interface_names: <list of strings> | default = [<private network interfaces>]]

# Enable using a IPv6 instance address (default false).
# CLI flag: -frontend.instance-enable-ipv6
[instance_enable_ipv6: <boolean> | default = false]

# Defines the encoding for requests to and responses from the scheduler and
# querier. Can be 'json' or 'protobuf' (defaults to 'protobuf').
# CLI flag: -frontend.encoding
[encoding: <string> | default = "protobuf"]

# Compress HTTP responses.
# CLI flag: -querier.compress-http-responses
[compress_responses: <boolean> | default = true]

# URL of downstream Loki.
# CLI flag: -frontend.downstream-url
[downstream_url: <string> | default = ""]

# URL of querier for tail proxy.
# CLI flag: -frontend.tail-proxy-url
[tail_proxy_url: <string> | default = ""]

# The TLS configuration.
# The CLI flags prefix for this block configuration is: frontend.tail-tls-config
[tail_tls_config: <tls_config>]

# Support 'application/vnd.apache.parquet' content type in HTTP responses.
# CLI flag: -frontend.support-parquet-encoding
[support_parquet_encoding: <boolean> | default = false]
```

### frontend_worker

The `frontend_worker` configures the worker - running within the Loki querier - picking up and executing queries enqueued by the query-frontend.

```yaml
# Address of query frontend service, in host:port format. If
# -querier.scheduler-address is set as well, querier will use scheduler instead.
# Only one of -querier.frontend-address or -querier.scheduler-address can be
# set. If neither is set, queries are only received via HTTP endpoint.
# CLI flag: -querier.frontend-address
[frontend_address: <string> | default = ""]

# Hostname (and port) of scheduler that querier will periodically resolve,
# connect to and receive queries from. Only one of -querier.frontend-address or
# -querier.scheduler-address can be set. If neither is set, queries are only
# received via HTTP endpoint.
# CLI flag: -querier.scheduler-address
[scheduler_address: <string> | default = ""]

# How often to query DNS for query-frontend or query-scheduler address. Also
# used to determine how often to poll the scheduler-ring for addresses if the
# scheduler-ring is configured.
# CLI flag: -querier.dns-lookup-period
[dns_lookup_duration: <duration> | default = 3s]

# Querier ID, sent to frontend service to identify requests from the same
# querier. Defaults to hostname.
# CLI flag: -querier.id
[id: <string> | default = ""]

# Configures the querier gRPC client used to communicate with the
# query-frontend. This can't be used in conjunction with 'grpc_client_config'.
# The CLI flags prefix for this block configuration is:
# querier.frontend-grpc-client
[query_frontend_grpc_client: <grpc_client>]

# Configures the querier gRPC client used to communicate with the query-frontend
# and with the query-scheduler. This can't be used in conjunction with
# 'query_frontend_grpc_client' or 'query_scheduler_grpc_client'.
# The CLI flags prefix for this block configuration is: querier.frontend-client
[grpc_client_config: <grpc_client>]

# Configures the querier gRPC client used to communicate with the
# query-scheduler. This can't be used in conjunction with 'grpc_client_config'.
# The CLI flags prefix for this block configuration is:
# querier.scheduler-grpc-client
[query_scheduler_grpc_client: <grpc_client>]
```

### gcs_storage_config

The `gcs_storage_config` block configures the connection to Google Cloud Storage object storage backend. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `common.storage`
- `ruler.storage`

&nbsp;

```yaml
# Name of GCS bucket. Please refer to
# https://cloud.google.com/docs/authentication/production for more information
# about how to configure authentication.
# CLI flag: -<prefix>.gcs.bucketname
[bucket_name: <string> | default = ""]

# Custom GCS endpoint URL.
# CLI flag: -<prefix>.gcs.endpoint
[endpoint: <string> | default = ""]

# Service account key content in JSON format, refer to
# https://cloud.google.com/iam/docs/creating-managing-service-account-keys for
# creation.
# CLI flag: -<prefix>.gcs.service-account
[service_account: <string> | default = ""]

# The size of the buffer that GCS client for each PUT request. 0 to disable
# buffering.
# CLI flag: -<prefix>.gcs.chunk-buffer-size
[chunk_buffer_size: <int> | default = 0]

# The duration after which the requests to GCS should be timed out.
# CLI flag: -<prefix>.gcs.request-timeout
[request_timeout: <duration> | default = 0s]

# Enable OpenCensus (OC) instrumentation for all requests.
# CLI flag: -<prefix>.gcs.enable-opencensus
[enable_opencensus: <boolean> | default = true]

# Enable HTTP2 connections.
# CLI flag: -<prefix>.gcs.enable-http2
[enable_http2: <boolean> | default = true]

# Enable automatic retries of failed idempotent requests.
# CLI flag: -<prefix>.gcs.enable-retries
[enable_retries: <boolean> | default = true]
```

### grpc_client

The `grpc_client` block configures the gRPC client used to communicate between a client and server component in Loki. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `bloom-build.builder.grpc`
- `bloom-gateway-client.grpc`
- `compactor.grpc-client`
- `frontend.grpc-client-config`
- `ingest-limits-frontend-client`
- `ingest-limits-frontend.limits-client`
- `ingester.client`
- `pattern-ingester.client`
- `querier.frontend-client`
- `querier.frontend-grpc-client`
- `querier.scheduler-grpc-client`
- `query-scheduler.grpc-client-config`
- `ruler.client`
- `ruler.evaluation.query-frontend`
- `tsdb.shipper.index-gateway-client.grpc`

&nbsp;

```yaml
# gRPC client max receive message size (bytes).
# CLI flag: -<prefix>.grpc-max-recv-msg-size
[max_recv_msg_size: <int> | default = 104857600]

# gRPC client max send message size (bytes).
# CLI flag: -<prefix>.grpc-max-send-msg-size
[max_send_msg_size: <int> | default = 104857600]

# Use compression when sending messages. Supported values are: 'gzip', 'snappy'
# and '' (disable compression)
# CLI flag: -<prefix>.grpc-compression
[grpc_compression: <string> | default = ""]

# Rate limit for gRPC client; 0 means disabled.
# CLI flag: -<prefix>.grpc-client-rate-limit
[rate_limit: <float> | default = 0]

# Rate limit burst for gRPC client.
# CLI flag: -<prefix>.grpc-client-rate-limit-burst
[rate_limit_burst: <int> | default = 0]

# Enable backoff and retry when we hit rate limits.
# CLI flag: -<prefix>.backoff-on-ratelimits
[backoff_on_ratelimits: <boolean> | default = false]

backoff_config:
  # Minimum delay when backing off.
  # CLI flag: -<prefix>.backoff-min-period
  [min_period: <duration> | default = 100ms]

  # Maximum delay when backing off.
  # CLI flag: -<prefix>.backoff-max-period
  [max_period: <duration> | default = 10s]

  # Number of times to backoff and retry before failing.
  # CLI flag: -<prefix>.backoff-retries
  [max_retries: <int> | default = 10]

# Initial stream window size. Values less than the default are not supported and
# are ignored. Setting this to a value other than the default disables the BDP
# estimator.
# CLI flag: -<prefix>.initial-stream-window-size
[initial_stream_window_size: <int> | default = 63KiB1023B]

# Initial connection window size. Values less than the default are not supported
# and are ignored. Setting this to a value other than the default disables the
# BDP estimator.
# CLI flag: -<prefix>.initial-connection-window-size
[initial_connection_window_size: <int> | default = 63KiB1023B]

# Enable TLS in the gRPC client. This flag needs to be enabled when any other
# TLS flag is set. If set to false, insecure connection to gRPC server will be
# used.
# CLI flag: -<prefix>.tls-enabled
[tls_enabled: <boolean> | default = false]

# The TLS configuration.
# The CLI flags prefix for this block configuration is:
# tsdb.shipper.index-gateway-client.grpc
[<tls_config>]

# The maximum amount of time to establish a connection. A value of 0 means
# default gRPC client connect timeout and backoff.
# CLI flag: -<prefix>.connect-timeout
[connect_timeout: <duration> | default = 5s]

# Initial backoff delay after first connection failure. Only relevant if
# ConnectTimeout > 0.
# CLI flag: -<prefix>.connect-backoff-base-delay
[connect_backoff_base_delay: <duration> | default = 1s]

# Maximum backoff delay when establishing a connection. Only relevant if
# ConnectTimeout > 0.
# CLI flag: -<prefix>.connect-backoff-max-delay
[connect_backoff_max_delay: <duration> | default = 5s]

# After a duration of this time if the client doesn't see any activity it pings
# the server to see if the transport is still alive. This also determines the
# socket's TCP_USER_TIMEOUT together with keepalive-timeout.
# CLI flag: -<prefix>.keepalive-time
[keepalive_time: <duration> | default = 20s]

# After having pinged for keepalive check, the client waits for a duration of
# this time and if no activity is seen even after that the connection is closed.
# CLI flag: -<prefix>.keepalive-timeout
[keepalive_timeout: <duration> | default = 10s]

cluster_validation:
  # Primary cluster validation label.
  # CLI flag: -<prefix>.cluster-validation.label
  [label: <string> | default = ""]
```

### index_gateway

The `index_gateway` block configures the Loki index gateway server, responsible for serving index queries without the need to constantly interact with the object store.

```yaml
# Defines in which mode the index gateway server will operate (default to
# 'simple'). It supports two modes:
# - 'simple': an index gateway server instance is responsible for handling,
# storing and returning requests for all indices for all tenants.
# - 'ring': an index gateway server instance is responsible for a subset of
# tenants instead of all tenants.
# CLI flag: -index-gateway.mode
[mode: <string> | default = "simple"]

# Defines the ring to be used by the index gateway servers and clients in case
# the servers are configured to run in 'ring' mode. In case this isn't
# configured, this block supports inheriting configuration from the common ring
# section.
ring:
  kvstore:
    # Backend storage to use for the ring. Supported values are: consul, etcd,
    # inmemory, memberlist, multi.
    # CLI flag: -index-gateway.ring.store
    [store: <string> | default = "consul"]

    # The prefix for the keys in the store. Should end with a /.
    # CLI flag: -index-gateway.ring.prefix
    [prefix: <string> | default = "collectors/"]

    # Configuration for a Consul client. Only applies if the selected kvstore is
    # consul.
    # The CLI flags prefix for this block configuration is: index-gateway.ring
    [consul: <consul>]

    # Configuration for an ETCD v3 client. Only applies if the selected kvstore
    # is etcd.
    # The CLI flags prefix for this block configuration is: index-gateway.ring
    [etcd: <etcd>]

    multi:
      # Primary backend storage used by multi-client.
      # CLI flag: -index-gateway.ring.multi.primary
      [primary: <string> | default = ""]

      # Secondary backend storage used by multi-client.
      # CLI flag: -index-gateway.ring.multi.secondary
      [secondary: <string> | default = ""]

      # Mirror writes to the secondary store.
      # CLI flag: -index-gateway.ring.multi.mirror-enabled
      [mirror_enabled: <boolean> | default = false]

      # Timeout for storing a value to the secondary store.
      # CLI flag: -index-gateway.ring.multi.mirror-timeout
      [mirror_timeout: <duration> | default = 2s]

  # Period at which to heartbeat to the ring.
  # CLI flag: -index-gateway.ring.heartbeat-period
  [heartbeat_period: <duration> | default = 15s]

  # The heartbeat timeout after which compactors are considered unhealthy within
  # the ring. 0 = never (timeout disabled).
  # CLI flag: -index-gateway.ring.heartbeat-timeout
  [heartbeat_timeout: <duration> | default = 1m]

  # File path where tokens are stored. If empty, tokens are not stored at
  # shutdown and restored at startup.
  # CLI flag: -index-gateway.ring.tokens-file-path
  [tokens_file_path: <string> | default = ""]

  # True to enable zone-awareness and replicate blocks across different
  # availability zones.
  # CLI flag: -index-gateway.ring.zone-awareness-enabled
  [zone_awareness_enabled: <boolean> | default = false]

  # Deprecated: How many index gateway instances are assigned to each tenant.
  # Use -index-gateway.shard-size instead. The shard size is also a per-tenant
  # setting.
  # CLI flag: -replication-factor
  [replication_factor: <int> | default = 3]

  # Instance ID to register in the ring.
  # CLI flag: -index-gateway.ring.instance-id
  [instance_id: <string> | default = "<hostname>"]

  # Name of network interface to read address from.
  # CLI flag: -index-gateway.ring.instance-interface-names
  [instance_interface_names: <list of strings> | default = [<private network interfaces>]]

  # Port to advertise in the ring (defaults to server.grpc-listen-port).
  # CLI flag: -index-gateway.ring.instance-port
  [instance_port: <int> | default = 0]

  # IP address to advertise in the ring.
  # CLI flag: -index-gateway.ring.instance-addr
  [instance_addr: <string> | default = ""]

  # The availability zone where this instance is running. Required if
  # zone-awareness is enabled.
  # CLI flag: -index-gateway.ring.instance-availability-zone
  [instance_availability_zone: <string> | default = ""]

  # Enable using a IPv6 instance address.
  # CLI flag: -index-gateway.ring.instance-enable-ipv6
  [instance_enable_ipv6: <boolean> | default = false]

# Experimental: Maximum number of index gateway RPCs that can execute
# concurrently. When the limit is reached, additional requests wait up to
# -index-gateway.max-concurrent-queue-timeout for a free slot. If no slot
# becomes free in that time, the index gateway rejects the request with an HTTP
# 503 status. Clients retry the request against another index gateway replica. 0
# disables admission control. A recommended starting value when enabling this
# setting is 200.
# CLI flag: -index-gateway.max-concurrent
[max_concurrent: <int> | default = 0]

# Experimental: Maximum time a request waits for a free slot when the index
# gateway is already executing -index-gateway.max-concurrent requests. If no
# slot becomes free in that time, the index gateway rejects the request. Bursts
# shorter than this timeout are absorbed without errors. Clients retry rejected
# requests against other replicas. When every replica is saturated, a request
# can wait up to this long per replica, so prefer a low value. 0 means requests
# wait indefinitely, bounded only by the request's own timeout. Only used when
# -index-gateway.max-concurrent is greater than 0.
# CLI flag: -index-gateway.max-concurrent-queue-timeout
[max_concurrent_queue_timeout: <duration> | default = 5s]
```

### ingester

The `ingester` block configures the ingester and how the ingester will register itself to a key value store.

```yaml
# Configures how the lifecycle of the ingester will operate and where it will
# register for discovery.
lifecycler:
  ring:
    kvstore:
      # Backend storage to use for the ring. Supported values are: consul, etcd,
      # inmemory, memberlist, multi.
      # CLI flag: -ring.store
      [store: <string> | default = "consul"]

      # The prefix for the keys in the store. Should end with a /.
      # CLI flag: -ring.prefix
      [prefix: <string> | default = "collectors/"]

      # Configuration for a Consul client. Only applies if the selected kvstore
      # is consul.
      [consul: <consul>]

      # Configuration for an ETCD v3 client. Only applies if the selected
      # kvstore is etcd.
      [etcd: <etcd>]

      multi:
        # Primary backend storage used by multi-client.
        # CLI flag: -multi.primary
        [primary: <string> | default = ""]

        # Secondary backend storage used by multi-client.
        # CLI flag: -multi.secondary
        [secondary: <string> | default = ""]

        # Mirror writes to the secondary store.
        # CLI flag: -multi.mirror-enabled
        [mirror_enabled: <boolean> | default = false]

        # Timeout for storing a value to the secondary store.
        # CLI flag: -multi.mirror-timeout
        [mirror_timeout: <duration> | default = 2s]

    # The heartbeat timeout after which ingesters are skipped for reads/writes.
    # CLI flag: -ring.heartbeat-timeout
    [heartbeat_timeout: <duration> | default = 1m]

    # The number of ingesters to write to and read from.
    # CLI flag: -distributor.replication-factor
    [replication_factor: <int> | default = 3]

    # True to enable the zone-awareness and replicate ingested samples across
    # different availability zones.
    # CLI flag: -distributor.zone-awareness-enabled
    [zone_awareness_enabled: <boolean> | default = false]

    # Comma-separated list of zones to exclude from the ring. Instances in
    # excluded zones will be filtered out from the ring.
    # CLI flag: -distributor.excluded-zones
    [excluded_zones: <string> | default = ""]

  # Number of tokens for each ingester.
  # CLI flag: -ingester.num-tokens
  [num_tokens: <int> | default = 128]

  # Period at which to heartbeat to consul.
  # CLI flag: -ingester.heartbeat-period
  [heartbeat_period: <duration> | default = 5s]

  # Heartbeat timeout after which instance is assumed to be unhealthy.
  # CLI flag: -ingester.heartbeat-timeout
  [heartbeat_timeout: <duration> | default = 1m]

  # Observe tokens after generating to resolve collisions. Useful when using
  # gossiping ring.
  # CLI flag: -ingester.observe-period
  [observe_period: <duration> | default = 0s]

  # Period to wait for a claim from another member; will join automatically
  # after this.
  # CLI flag: -ingester.join-after
  [join_after: <duration> | default = 0s]

  # Minimum duration to wait after the internal readiness checks have passed but
  # before succeeding the readiness endpoint. This is used to slowdown
  # deployment controllers (eg. Kubernetes) after an instance is ready and
  # before they proceed with a rolling update, to give the rest of the cluster
  # instances enough time to receive ring updates.
  # CLI flag: -ingester.min-ready-duration
  [min_ready_duration: <duration> | default = 15s]

  # Name of network interface to read address from.
  # CLI flag: -ingester.lifecycler.interface
  [interface_names: <list of strings> | default = [<private network interfaces>]]

  # Enable IPv6 support. Required to make use of IP addresses from IPv6
  # interfaces.
  # CLI flag: -ingester.enable-inet6
  [enable_inet6: <boolean> | default = false]

  # Duration to sleep for before exiting, to ensure metrics are scraped.
  # CLI flag: -ingester.final-sleep
  [final_sleep: <duration> | default = 0s]

  # File path where tokens are stored. If empty, tokens are not stored at
  # shutdown and restored at startup.
  # CLI flag: -ingester.tokens-file-path
  [tokens_file_path: <string> | default = ""]

  # The availability zone where this instance is running.
  # CLI flag: -ingester.availability-zone
  [availability_zone: <string> | default = ""]

  # Unregister from the ring upon clean shutdown. It can be useful to disable
  # for rolling restarts with consistent naming in conjunction with
  # -distributor.extend-writes=false.
  # CLI flag: -ingester.unregister-on-shutdown
  [unregister_on_shutdown: <boolean> | default = true]

  # When enabled the readiness probe succeeds only after all instances are
  # ACTIVE and healthy in the ring, otherwise only the instance itself is
  # checked. This option should be disabled if in your cluster multiple
  # instances can be rolled out simultaneously, otherwise rolling updates may be
  # slowed down.
  # CLI flag: -ingester.readiness-check-ring-health
  [readiness_check_ring_health: <boolean> | default = true]

  # IP address to advertise in the ring.
  # CLI flag: -ingester.lifecycler.addr
  [address: <string> | default = ""]

  # port to advertise in consul (defaults to server.grpc-listen-port).
  # CLI flag: -ingester.lifecycler.port
  [port: <int> | default = 0]

  # ID to register in the ring.
  # CLI flag: -ingester.lifecycler.ID
  [id: <string> | default = "<hostname>"]

# How many flushes can happen concurrently from each stream.
# CLI flag: -ingester.concurrent-flushes
[concurrent_flushes: <int> | default = 32]

# How often should the ingester see if there are any blocks to flush. The first
# flush check is delayed by a random time up to 0.8x the flush check period.
# Additionally, there is +/- 1% jitter added to the interval.
# CLI flag: -ingester.flush-check-period
[flush_check_period: <duration> | default = 30s]

flush_op_backoff:
  # Minimum backoff period when a flush fails. Each concurrent flush has its own
  # backoff, see `ingester.concurrent-flushes`.
  # CLI flag: -ingester.flush-op-backoff-min-period
  [min_period: <duration> | default = 10s]

  # Maximum backoff period when a flush fails. Each concurrent flush has its own
  # backoff, see `ingester.concurrent-flushes`.
  # CLI flag: -ingester.flush-op-backoff-max-period
  [max_period: <duration> | default = 1m]

  # Maximum retries for failed flushes.
  # CLI flag: -ingester.flush-op-backoff-retries
  [max_retries: <int> | default = 10]

# The timeout for an individual flush. Will be retried up to
# `flush-op-backoff-retries` times.
# CLI flag: -ingester.flush-op-timeout
[flush_op_timeout: <duration> | default = 10m]

# How long chunks should be retained in-memory after they've been flushed.
# CLI flag: -ingester.chunks-retain-period
[chunk_retain_period: <duration> | default = 0s]

# How long chunks should sit in-memory with no updates before being flushed if
# they don't hit the max block size. This means that half-empty chunks will
# still be flushed after a certain period as long as they receive no further
# activity.
# CLI flag: -ingester.chunks-idle-period
[chunk_idle_period: <duration> | default = 30m]

# The targeted _uncompressed_ size in bytes of a chunk block When this threshold
# is exceeded the head block will be cut and compressed inside the chunk.
# CLI flag: -ingester.chunks-block-size
[chunk_block_size: <int> | default = 262144]

# A target _compressed_ size in bytes for chunks. This is a desired size not an
# exact size, chunks may be slightly bigger or significantly smaller if they get
# flushed for other reasons (e.g. chunk_idle_period). A value of 0 creates
# chunks with a fixed 10 blocks, a non zero value will create chunks with a
# variable number of blocks to meet the target size.
# CLI flag: -ingester.chunk-target-size
[chunk_target_size: <int> | default = 1572864]

# The algorithm to use for compressing chunk. (none, gzip, lz4-64k, snappy,
# lz4-256k, lz4-1M, lz4, flate, zstd)
# CLI flag: -ingester.chunk-encoding
[chunk_encoding: <string> | default = "gzip"]

# The maximum duration of a timeseries chunk in memory. If a timeseries runs for
# longer than this, the current chunk will be flushed to the store and a new
# chunk created.
# CLI flag: -ingester.max-chunk-age
[max_chunk_age: <duration> | default = 2h]

# Forget about ingesters having heartbeat timestamps older than
# `ring.kvstore.heartbeat_timeout`. This is equivalent to clicking on the
# `/ring` `forget` button in the UI: the ingester is removed from the ring. This
# is a useful setting when you are sure that an unhealthy node won't return. An
# example is when not using stateful sets or the equivalent. Use
# `memberlist.rejoin_interval` > 0 to handle network partition cases when using
# a memberlist.
# CLI flag: -ingester.autoforget-unhealthy
[autoforget_unhealthy: <boolean> | default = false]

# Parameters used to synchronize ingesters to cut chunks at the same moment.
# Sync period is used to roll over incoming entry to a new chunk. If chunk's
# utilization isn't high enough (eg. less than 50% when sync_min_utilization is
# set to 0.5), then this chunk rollover doesn't happen.
# CLI flag: -ingester.sync-period
[sync_period: <duration> | default = 1h]

# Minimum utilization of chunk when doing synchronization.
# CLI flag: -ingester.sync-min-utilization
[sync_min_utilization: <float> | default = 0.1]

# The maximum number of errors a stream will report to the user when a push
# fails. 0 to make unlimited.
# CLI flag: -ingester.max-ignored-stream-errors
[max_returned_stream_errors: <int> | default = 10]

# How far back should an ingester be allowed to query the store for data, for
# use only with tsdb index and filesystem object store. -1 for infinite.
# CLI flag: -ingester.query-store-max-look-back-period
[query_store_max_look_back_period: <duration> | default = 0s]

# The ingester WAL (Write Ahead Log) records incoming logs and stores them on
# the local file systems in order to guarantee persistence of acknowledged data
# in the event of a process crash.
wal:
  # Enable writing of ingested data into WAL.
  # CLI flag: -ingester.wal-enabled
  [enabled: <boolean> | default = true]

  # Directory where the WAL data is stored and/or recovered from.
  # CLI flag: -ingester.wal-dir
  [dir: <string> | default = "wal"]

  # Interval at which checkpoints should be created.
  # CLI flag: -ingester.checkpoint-duration
  [checkpoint_duration: <duration> | default = 5m]

  # When WAL is enabled, should chunks be flushed to long-term storage on
  # shutdown.
  # CLI flag: -ingester.flush-on-shutdown
  [flush_on_shutdown: <boolean> | default = false]

  # Maximum memory size the WAL may use during replay. After hitting this, it
  # will flush data to storage before continuing. A unit suffix (KB, MB, GB) may
  # be applied.
  # CLI flag: -ingester.wal-replay-memory-ceiling
  [replay_memory_ceiling: <int> | default = 4GB]

  # Threshold for disk usage (0.0 to 1.0) at which the WAL will throttle
  # incoming writes. Set to 0 to disable throttling.
  # CLI flag: -ingester.wal-disk-full-threshold
  [disk_full_threshold: <float> | default = 0.9]

# Shard factor used in the ingesters for the in process reverse index. This MUST
# be evenly divisible by ALL schema shard factors or Loki will not start.
# CLI flag: -ingester.index-shards
[index_shards: <int> | default = 32]

# Maximum number of dropped streams to keep in memory during tailing.
# CLI flag: -ingester.tailer.max-dropped-streams
[max_dropped_streams: <int> | default = 10]

# Path where the shutdown marker file is stored. If not set and
# common.path_prefix is set then common.path_prefix will be used.
# CLI flag: -ingester.shutdown-marker-path
[shutdown_marker_path: <string> | default = ""]

# Interval at which the ingester ownedStreamService checks for changes in the
# ring to recalculate owned streams.
# CLI flag: -ingester.owned-streams-check-interval
[owned_streams_check_interval: <duration> | default = 30s]

# Deprecated: use the per-tenant limit delegate_stream_limits_enabled instead.
# It provides the default for that limit, so the ingester skips stream count
# limit checks for all tenants that do not set the per-tenant limit, delegating
# them entirely to the ingest-limits service. Requires ingest-limits service to
# be enabled.
# CLI flag: -ingester.delegate-stream-limits-enabled
[delegate_stream_limits_enabled: <boolean> | default = false]

kafka_ingestion:
  # Whether the kafka ingester is enabled.
  # CLI flag: -ingester.kafka-ingestion-enabled
  [enabled: <boolean> | default = false]

  partition_ring:
    # The key-value store used to share the hash ring across multiple instances.
    # This option needs be set on ingesters, distributors, queriers, and rulers
    # when running in microservices mode.
    kvstore:
      # Backend storage to use for the ring. Supported values are: consul, etcd,
      # inmemory, memberlist, multi.
      # CLI flag: -ingester.partition-ring.store
      [store: <string> | default = "memberlist"]

      # The prefix for the keys in the store. Should end with a /.
      # CLI flag: -ingester.partition-ring.prefix
      [prefix: <string> | default = "collectors/"]

      # Configuration for a Consul client. Only applies if the selected kvstore
      # is consul.
      # The CLI flags prefix for this block configuration is:
      # ingester.partition-ring
      [consul: <consul>]

      # Configuration for an ETCD v3 client. Only applies if the selected
      # kvstore is etcd.
      # The CLI flags prefix for this block configuration is:
      # ingester.partition-ring
      [etcd: <etcd>]

      multi:
        # Primary backend storage used by multi-client.
        # CLI flag: -ingester.partition-ring.multi.primary
        [primary: <string> | default = ""]

        # Secondary backend storage used by multi-client.
        # CLI flag: -ingester.partition-ring.multi.secondary
        [secondary: <string> | default = ""]

        # Mirror writes to the secondary store.
        # CLI flag: -ingester.partition-ring.multi.mirror-enabled
        [mirror_enabled: <boolean> | default = false]

        # Timeout for storing a value to the secondary store.
        # CLI flag: -ingester.partition-ring.multi.mirror-timeout
        [mirror_timeout: <duration> | default = 2s]

    # Minimum number of owners to wait before a PENDING partition gets switched
    # to ACTIVE.
    # CLI flag: -ingester.partition-ring.min-partition-owners-count
    [min_partition_owners_count: <int> | default = 1]

    # How long the minimum number of owners are enforced before a PENDING
    # partition gets switched to ACTIVE.
    # CLI flag: -ingester.partition-ring.min-partition-owners-duration
    [min_partition_owners_duration: <duration> | default = 10s]

    # How long to wait before an INACTIVE partition is eligible for deletion.
    # The partition is deleted only if it has been in INACTIVE state for at
    # least the configured duration and it has no owners registered. A value of
    # 0 disables partitions deletion.
    # CLI flag: -ingester.partition-ring.delete-inactive-partition-after
    [delete_inactive_partition_after: <duration> | default = 13h]

    # Experimental: The size of the cache used for shuffle sharding. If zero or
    # negative, an unbounded cache is used. If positive, an LRU cache with the
    # specified size is used.
    # CLI flag: -ingester.partition-ring.shuffle-shard-cache-size
    [shuffle_shard_cache_size: <int> | default = 0]
```

### ingester_client

The `ingester_client` block configures how the distributor will connect to ingesters. Only appropriate when running all components, the distributor, or the querier.

```yaml
# Configures how connections are pooled.
pool_config:
  # How frequently to clean up clients for ingesters that have gone away.
  # CLI flag: -distributor.client-cleanup-period
  [client_cleanup_period: <duration> | default = 15s]

  # Run a health check on each ingester client during periodic cleanup.
  # CLI flag: -distributor.health-check-ingesters
  [health_check_ingesters: <boolean> | default = true]

  # How quickly a dead client will be removed after it has been detected to
  # disappear. Set this to a value to allow time for a secondary health check to
  # recover the missing client.
  # CLI flag: -ingester.client.healthcheck-timeout
  [remote_timeout: <duration> | default = 1s]

# The remote request timeout on the client side.
# CLI flag: -ingester.client.timeout
[remote_timeout: <duration> | default = 5s]

# Configures how the gRPC connection to ingesters work as a client.
# The CLI flags prefix for this block configuration is: ingester.client
[grpc_client_config: <grpc_client>]
```

### limits_config

The `limits_config` block configures global and per-tenant limits in Loki. The values here can be overridden in the `overrides` section of the runtime_config file

```yaml
# Whether the ingestion rate limit should be applied individually to each
# distributor instance (local), or evenly shared across the cluster (global).
# The ingestion rate strategy cannot be overridden on a per-tenant basis.
# - local: enforces the limit on a per distributor basis. The actual effective
# rate limit will be N times higher, where N is the number of distributor
# replicas.
# - global: enforces the limit globally, configuring a per-distributor local
# rate limiter as 'ingestion_rate / N', where N is the number of distributor
# replicas (it's automatically adjusted if the number of replicas change). The
# global strategy requires the distributors to form their own ring, which is
# used to keep track of the current number of healthy distributor replicas.
# CLI flag: -distributor.ingestion-rate-limit-strategy
[ingestion_rate_strategy: <string> | default = "global"]

# Per-user ingestion rate limit in sample size per second. Sample size includes
# size of the logs line and the size of structured metadata labels. Units in MB.
# CLI flag: -distributor.ingestion-rate-limit-mb
[ingestion_rate_mb: <float> | default = 4]

# Per-user allowed ingestion burst size (in sample size). Units in MB. The burst
# size refers to the per-distributor local rate limiter even in the case of the
# 'global' strategy, and should be set at least to the maximum logs size
# expected in a single push request.
# CLI flag: -distributor.ingestion-burst-size-mb
[ingestion_burst_size_mb: <float> | default = 6]

# Maximum length accepted for label names.
# CLI flag: -validation.max-length-label-name
[max_label_name_length: <int> | default = 1024]

# Maximum length accepted for label value. This setting also applies to the
# metric name.
# CLI flag: -validation.max-length-label-value
[max_label_value_length: <int> | default = 2048]

# Maximum number of label names per series.
# CLI flag: -validation.max-label-names-per-series
[max_label_names_per_series: <int> | default = 15]

# Whether or not old samples will be rejected.
# CLI flag: -validation.reject-old-samples
[reject_old_samples: <boolean> | default = true]

# Maximum accepted sample age before rejecting.
# CLI flag: -validation.reject-old-samples.max-age
[reject_old_samples_max_age: <duration> | default = 1w]

# Duration which table will be created/deleted before/after it's needed; we
# won't accept sample from before this time.
# CLI flag: -validation.create-grace-period
[creation_grace_period: <duration> | default = 10m]

# Maximum line size on ingestion path. Example: 256kb. Any log line exceeding
# this limit will be discarded unless `distributor.max-line-size-truncate` is
# set, in which case it is truncated rather than discarded completely. There is
# no limit when set to 0.
# CLI flag: -distributor.max-line-size
[max_line_size: <int> | default = 256KB]

# Whether to truncate lines that exceed max_line_size.
# CLI flag: -distributor.max-line-size-truncate
[max_line_size_truncate: <boolean> | default = false]

# Identifier that is added at the end of a truncated log line.
# CLI flag: -distributor.max-line-size-truncate-identifier
[max_line_size_truncate_identifier: <string> | default = ""]

# The maximum size of a Push request.
# CLI flag: -distributor.max-push-size
[max_push_size: <int> | default = 2GB]

# Alter the log line timestamp during ingestion when the timestamp is the same
# as the previous entry for the same stream. When enabled, if a log line in a
# push request has the same timestamp as the previous line for the same stream,
# one nanosecond is added to the log line. This will preserve the received order
# of log lines with the exact same timestamp when they are queried, by slightly
# altering their stored timestamp. NOTE: This is imperfect, because Loki accepts
# out of order writes, and another push request for the same stream could
# contain duplicate timestamps to existing entries and they will not be
# incremented.
# CLI flag: -validation.increment-duplicate-timestamps
[increment_duplicate_timestamp: <boolean> | default = false]

# Simulated latency to add to push requests. Used for testing. Set to 0s to
# disable.
# CLI flag: -limits.simulated-push-latency
[simulated_push_latency: <duration> | default = 0s]

# Experimental: Detect fields from stream labels, structured metadata, or
# json/logfmt formatted log line and put them into structured metadata of the
# log entry.
discover_generic_fields:
  [fields: <map of string to list of strings>]

# If no service_name label exists, Loki maps a single label from the configured
# list to service_name. If none of the configured labels exist in the stream,
# label is set to unknown_service. Empty list disables setting the label.
# CLI flag: -validation.discover-service-name
[discover_service_name: <list of strings> | default = [service app application app_name name app_kubernetes_io_name container container_name k8s_container_name component workload job k8s_job_name]]

# Discover the log level of each entry during ingestion and add it to Structured
# Metadata as detected_level, unless it is already present. The level is read
# from the first stream label or Structured Metadata field whose name is in
# log_level_fields (default: level, LEVEL, Level, log.level, severity, SEVERITY,
# Severity, SeverityText, lvl, LVL, Lvl, severity_text, Severity_Text,
# SEVERITY_TEXT). Otherwise it is derived from the OTLP severity number, or from
# the log line: a JSON or logfmt field named in log_level_fields, then keywords.
# Recognized values (case insensitive) are normalized to 'trace', 'debug',
# 'info', 'warn', 'error', 'critical' or 'fatal'.
# CLI flag: -validation.discover-log-levels
[discover_log_levels: <boolean> | default = true]

# Field name to use for log levels. If not set, log level would be detected
# based on pre-defined labels as mentioned above.
# CLI flag: -validation.log-level-fields
[log_level_fields: <list of strings> | default = [level LEVEL Level log.level severity SEVERITY Severity SeverityText lvl LVL Lvl severity_text Severity_Text SEVERITY_TEXT]]

# Maximum depth to search for log level fields in JSON logs. A value of 0 or
# less means unlimited depth. Default is 2 which searches the first 2 levels of
# the JSON object.
# CLI flag: -validation.log-level-from-json-max-depth
[log_level_from_json_max_depth: <int> | default = 2]

# When true an ingester takes into account only the streams that it owns
# according to the ring while applying the stream limit.
# CLI flag: -ingester.use-owned-stream-count
[use_owned_stream_count: <boolean> | default = false]

# When enabled, the ingester skips stream count limit checks for the tenant,
# delegating them entirely to the ingest-limits service. Requires ingest-limits
# service to be enabled. When this limit is not set, the deprecated
# ingester-wide setting -ingester.delegate-stream-limits-enabled provides its
# default.
# CLI flag: -limits.delegate-stream-limits-enabled
[delegate_stream_limits_enabled: <boolean> | default = false]

# Maximum number of active streams per user, per ingester. 0 to disable.
# CLI flag: -ingester.max-streams-per-user
[max_streams_per_user: <int> | default = 0]

# Maximum number of active streams per user, across the cluster. 0 to disable.
# When the global limit is enabled, each ingester is configured with a dynamic
# local limit based on the replication factor and the current number of healthy
# ingesters, and is kept updated whenever the number of ingesters change.
# CLI flag: -ingester.max-global-streams-per-user
[max_global_streams_per_user: <int> | default = 5000]

# Maximum byte rate per second per stream, also expressible in human readable
# forms (1MB, 256KB, etc).
# CLI flag: -ingester.per-stream-rate-limit
[per_stream_rate_limit: <int> | default = 3MB]

# Maximum burst bytes per stream, also expressible in human readable forms (1MB,
# 256KB, etc). This is how far above the rate limit a stream can 'burst' before
# the stream is limited.
# CLI flag: -ingester.per-stream-rate-limit-burst
[per_stream_rate_limit_burst: <int> | default = 15MB]

# Maximum number of chunks that can be fetched in a single query.
# CLI flag: -store.query-chunk-limit
[max_chunks_per_query: <int> | default = 2000000]

# Limit the maximum of unique series that is returned by a metric query. When
# the limit is reached an error is returned.
# CLI flag: -querier.max-query-series
[max_query_series: <int> | default = 500]

# Limit how far back in time series data and metadata can be queried, up until
# lookback duration ago. This limit is enforced in the query frontend, the
# querier and the ruler. If the requested time range is outside the allowed
# range, the request will not fail, but will be modified to only query data
# within the allowed time range. The default value of 0 does not set a limit.
# CLI flag: -querier.max-query-lookback
[max_query_lookback: <duration> | default = 0s]

# The limit to length of chunk store queries. 0 to disable.
# CLI flag: -store.max-query-length
[max_query_length: <duration> | default = 30d1h]

# Limit the length of the [range] inside a range query. Default is 0 or
# unlimited
# CLI flag: -querier.max-query-range
[max_query_range: <duration> | default = 0s]

# Maximum number of queries that will be scheduled in parallel by the frontend.
# CLI flag: -querier.max-query-parallelism
[max_query_parallelism: <int> | default = 32]

# Maximum number of queries will be scheduled in parallel by the frontend for
# TSDB schemas.
# CLI flag: -querier.tsdb-max-query-parallelism
[tsdb_max_query_parallelism: <int> | default = 128]

# Target maximum number of bytes assigned to a single sharded query that reads
# chunks. Also expressible in human readable forms (1GB, etc). Note: This is a
# _target_ and not an absolute limit. The actual limit can be higher, but the
# query planner will try to build shards up to this limit. The bounded sharding
# strategy also uses this setting for queries that read data objects.
# CLI flag: -querier.tsdb-max-bytes-per-shard
[tsdb_max_bytes_per_shard: <int> | default = 600MB]

# sharding strategy to use in query planning. Suggested to use bounded once all
# nodes can recognize it.
# CLI flag: -limits.tsdb-sharding-strategy
[tsdb_sharding_strategy: <string> | default = "power_of_two"]

# Precompute chunks for TSDB queries. This can improve query performance at the
# cost of increased memory usage by computing chunks once during planning,
# reducing index calls.
# CLI flag: -querier.tsdb-precompute-chunks
[tsdb_precompute_chunks: <boolean> | default = false]

# Cardinality limit for index queries.
# CLI flag: -store.cardinality-limit
[cardinality_limit: <int> | default = 100000]

# Maximum number of stream matchers per query.
# CLI flag: -querier.max-streams-matcher-per-query
[max_streams_matchers_per_query: <int> | default = 1000]

# Maximum number of concurrent tail requests.
# CLI flag: -querier.max-concurrent-tail-requests
[max_concurrent_tail_requests: <int> | default = 10]

# Maximum number of log entries that will be returned for a query.
# CLI flag: -validation.max-entries-limit
[max_entries_limit_per_query: <int> | default = 5000]

# Most recent allowed cacheable result per-tenant, to prevent caching very
# recent results that might still be in flux.
# CLI flag: -frontend.max-cache-freshness
[max_cache_freshness_per_query: <duration> | default = 10m]

# Do not cache metadata request if the end time is within the
# frontend.max-metadata-cache-freshness window. Set this to 0 to apply no such
# limits. Defaults to 24h.
# CLI flag: -frontend.max-metadata-cache-freshness
[max_metadata_cache_freshness: <duration> | default = 1d]

# Do not cache requests with an end time that falls within Now minus this
# duration. 0 disables this feature (default).
# CLI flag: -frontend.max-stats-cache-freshness
[max_stats_cache_freshness: <duration> | default = 10m]

# Maximum number of queriers that can handle requests for a single tenant. If
# set to 0 or value higher than number of available queriers, *all* queriers
# will handle requests for the tenant. Each frontend (or query-scheduler, if
# used) will select the same set of queriers for the same tenant (given that all
# queriers are connected to all frontends / query-schedulers). This option only
# works with queriers connecting to the query-frontend / query-scheduler, not
# when using downstream URL.
# CLI flag: -frontend.max-queriers-per-tenant
[max_queriers_per_tenant: <int> | default = 0]

# How much of the available query capacity ("querier" components) can be used by
# a single tenant. Allowed values are 0.0 to 1.0. For example, setting this to
# 0.5 would allow a tenant to use half of the available queriers for processing
# the query workload. If set to 0, query capacity is determined by
# frontend.max-queriers-per-tenant. When both frontend.max-queriers-per-tenant
# and frontend.max-query-capacity are configured, smaller value of the resulting
# querier replica count is considered: min(frontend.max-queriers-per-tenant,
# ceil(querier_replicas * frontend.max-query-capacity)). *All* queriers will
# handle requests for the tenant if neither limits are applied. This option only
# works with queriers connecting to the query-frontend / query-scheduler, not
# when using downstream URL. Use this feature in a multi-tenant setup where you
# need to limit query capacity for certain tenants.
# CLI flag: -frontend.max-query-capacity
[max_query_capacity: <float> | default = 0]

# Number of days of index to be kept always downloaded for queries. Applies only
# to per user index in boltdb-shipper index store. 0 to disable.
# CLI flag: -store.query-ready-index-num-days
[query_ready_index_num_days: <int> | default = 0]

# Timeout when querying backends (ingesters or storage) during the execution of
# a query request. When a specific per-tenant timeout is used, the global
# timeout is ignored.
# CLI flag: -querier.query-timeout
[query_timeout: <duration> | default = 1m]

# When enabled, Loki evaluates eligible metric queries in stream-first order,
# which reads samples one stream at a time instead of in timestamp order. The
# query-frontend and the querier read this setting.
# CLI flag: -querier.stream-first-execution-enabled
[stream_first_execution_enabled: <boolean> | default = false]

# Time, inclusive, from which stream-first metric queries read data objects. The
# query-frontend and the querier read this setting. 0 disables it.
# CLI flag: -querier.dataobj-query-start-time
[dataobj_query_start_time: <time> | default = 0]

# Target maximum number of bytes assigned to a single sharded query that reads
# data objects. Also expressible in human readable forms (1GB, etc). Note: This
# is a _target_ and not an absolute limit. The query-frontend reads this
# setting. The query-frontend uses it only with the power-of-two sharding
# strategy. The bounded strategy uses -querier.tsdb-max-bytes-per-shard.
# CLI flag: -querier.dataobj-max-bytes-per-shard
[dataobj_max_bytes_per_shard: <int> | default = 6000MB]

# Split queries by a time interval and execute in parallel. The value 0 disables
# splitting by time. This also determines how cache keys are chosen when result
# caching is enabled.
# CLI flag: -querier.split-queries-by-interval
[split_queries_by_interval: <duration> | default = 1h]

# Split metadata queries by a time interval and execute in parallel. The value 0
# disables splitting metadata queries by time. This also determines how cache
# keys are chosen when label/series result caching is enabled.
# CLI flag: -querier.split-metadata-queries-by-interval
[split_metadata_queries_by_interval: <duration> | default = 1d]

# Experimental. Split interval to use for the portion of metadata request that
# falls within `recent_metadata_query_window`. Rest of the request which is
# outside the window still uses `split_metadata_queries_by_interval`. If set to
# 0, the entire request defaults to using a split interval of
# `split_metadata_queries_by_interval.`.
# CLI flag: -experimental.querier.split-recent-metadata-queries-by-interval
[split_recent_metadata_queries_by_interval: <duration> | default = 1h]

# Experimental. Metadata query window inside which
# `split_recent_metadata_queries_by_interval` gets applied, portion of the
# metadata request that falls in this window is split using
# `split_recent_metadata_queries_by_interval`. The value 0 disables using a
# different split interval for recent metadata queries.
# 
# This is added to improve cacheability of recent metadata queries. Query split
# interval also determines the interval used in cache key. The default split
# interval of 24h is useful for caching long queries, each cache key holding 1
# day's results. But metadata queries are often shorter than 24h, to cache them
# effectively we need a smaller split interval. `recent_metadata_query_window`
# along with `split_recent_metadata_queries_by_interval` help configure a
# shorter split interval for recent metadata queries.
# CLI flag: -experimental.querier.recent-metadata-query-window
[recent_metadata_query_window: <duration> | default = 0s]

# Split instant metric queries by a time interval and execute in parallel. The
# value 0 disables splitting instant metric queries by time. This also
# determines how cache keys are chosen when instant metric query result caching
# is enabled.
# CLI flag: -querier.split-instant-metric-queries-by-interval
[split_instant_metric_queries_by_interval: <duration> | default = 1h]

# Interval to use for time-based splitting when a request is within the
# `query_ingesters_within` window; defaults to `split-queries-by-interval` by
# setting to 0.
# CLI flag: -querier.split-ingester-queries-by-interval
[split_ingester_queries_by_interval: <duration> | default = 0s]

# Limit queries that can be sharded. Queries within the time range of now and
# now minus this sharding lookback are not sharded. The default value of 0s
# disables the lookback, causing sharding of all queries at all times.
# CLI flag: -frontend.min-sharding-lookback
[min_sharding_lookback: <duration> | default = 0s]

# Max number of bytes a query can fetch. Enforced in log and metric queries only
# when TSDB is used. This limit is not enforced on log queries without filters.
# The default value of 0 disables this limit.
# CLI flag: -frontend.max-query-bytes-read
[max_query_bytes_read: <int> | default = 0B]

# Max number of bytes a query can fetch after splitting and sharding. Enforced
# in log and metric queries only when TSDB is used. This limit is not enforced
# on log queries without filters. The default value of 0 disables this limit.
# CLI flag: -frontend.max-querier-bytes-read
[max_querier_bytes_read: <int> | default = 150GB]

# Enable log-volume endpoints.
# CLI flag: -limits.volume-enabled
[volume_enabled: <boolean> | default = true]

# The maximum number of aggregated series in a log-volume response
# CLI flag: -limits.volume-max-series
[volume_max_series: <int> | default = 1000]

# Maximum number of rules per rule group per-tenant. 0 to disable.
# CLI flag: -ruler.max-rules-per-rule-group
[ruler_max_rules_per_rule_group: <int> | default = 0]

# Maximum number of rule groups per-tenant. 0 to disable.
# CLI flag: -ruler.max-rule-groups-per-tenant
[ruler_max_rule_groups_per_tenant: <int> | default = 0]

# The default tenant's shard size when shuffle-sharding is enabled in the ruler.
# When this setting is specified in the per-tenant overrides, a value of 0
# disables shuffle sharding for the tenant.
# CLI flag: -ruler.tenant-shard-size
[ruler_tenant_shard_size: <int> | default = 0]

# Disable recording rules remote-write.
[ruler_remote_write_disabled: <boolean>]

# Configures global and per-tenant limits for remote write clients. A map with
# remote client id as key.
[ruler_remote_write_config: <map of string to RemoteWriteConfig>]

# Timeout for a remote rule evaluation. Defaults to the value of
# 'querier.query-timeout'.
[ruler_remote_evaluation_timeout: <duration>]

# Maximum size (in bytes) of the allowable response size from a remote rule
# evaluation. Set to 0 to allow any response size (default).
[ruler_remote_evaluation_max_response_size: <int>]

# Deletion mode. Can be one of 'disabled', 'filter-only', or
# 'filter-and-delete'. When set to 'filter-only' or 'filter-and-delete', and if
# retention_enabled is true, then the log entry deletion API endpoints are
# available.
# CLI flag: -compactor.deletion-mode
[deletion_mode: <string> | default = "filter-and-delete"]

# Retention period to apply to stored data, only applies if retention_enabled is
# true in the compactor config. As of version 2.8.0, a zero value of 0 or 0s
# disables retention. In previous releases, Loki did not properly honor a zero
# value to disable retention and a really large value should be used instead.
# CLI flag: -store.retention
[retention_period: <duration> | default = 0s]

# Per-stream retention to apply, if the retention is enabled on the compactor
# side.
# Example:
#  retention_stream:
#  - selector: '{namespace="dev"}'
#  priority: 1
#  period: 24h
# - selector: '{container="nginx"}'
#  priority: 1
#  period: 744h
# Selector is a Prometheus labels matchers that will apply the 'period'
# retention only if the stream is matching. In case multiple streams are
# matching, the highest priority will be picked. If no rule is matched the
# 'retention_period' is used.
[retention_stream: <list of StreamRetentions>]

# Define streams sharding behavior.
shard_streams:
  # Automatically shard streams to keep them under the per-stream rate limit.
  # Sharding is dictated by the desired rate.
  # CLI flag: -shard-streams.enabled
  [enabled: <boolean> | default = true]

  # Automatically shard streams by adding a __time_shard__ label, with values
  # calculated from the log timestamps divided by MaxChunkAge/2. This allows the
  # out-of-order ingestion of very old logs. If both flags are enabled,
  # time-based sharding will happen before rate-based sharding.
  # CLI flag: -shard-streams.time-sharding-enabled
  [time_sharding_enabled: <boolean> | default = false]

  # Logs with timestamps that are newer than this value will not be
  # time-sharded.
  # CLI flag: -shard-streams.time-sharding-ignore-recent
  [time_sharding_ignore_recent: <duration> | default = 40m]

  # Whether to log sharding streams behavior or not. Not recommended for
  # production environments.
  # CLI flag: -shard-streams.logging-enabled
  [logging_enabled: <boolean> | default = false]

  # Threshold used to cut a new shard. Default (1536KB) means if a rate is above
  # 1536KB/s, it will be sharded into two streams.
  # CLI flag: -shard-streams.desired-rate
  [desired_rate: <int> | default = 1536KB]

  # Maximum number of shards that my be produced by rateStore. The default of 0
  # means unlimited.
  # CLI flag: -shard-streams.max-shard-count
  [max_shard_count: <int> | default = 0]

  # Experimental. Whether the ingest-limits service is asked for a shard count
  # for this tenant, and whether its answer is used. One of 'disabled' (default,
  # unchanged behavior), 'shadow' (ask the limits service and compare its answer
  # against the local rate store's; the local rate store still decides how
  # streams are sharded) or 'live' (shard streams with the count the limits
  # service returns, falling back to the local rate store for streams it does
  # not answer for). Both 'shadow' and 'live' require the ingest-limits service
  # to be enabled.
  # CLI flag: -shard-streams.limits-service-stream-sharding-mode
  [limits_service_stream_sharding_mode: <string> | default = "disabled"]

  # Experimental. The window the ingest-limits service averages this tenant's
  # stream rates over when deciding shard counts. A shorter window reacts to
  # shorter bursts, closer to the distributor's local rate store, which measures
  # a one second window. 0 (default) uses the ingest-limits service's own
  # rate_window. Clamped to the service's [bucket_size, rate_window], so raise
  # the service-wide rate_window to allow a longer window here. The local rate
  # store ignores this.
  # CLI flag: -shard-streams.limits-service-stream-sharding-rate-window
  [limits_service_stream_sharding_rate_window: <duration> | default = 0s]

[blocked_queries: <blocked_query...>]

# Define a list of required selector labels.
[required_labels: <list of strings>]

# Minimum number of label matchers a query should contain.
[minimum_labels_number: <int>]

# The shard size defines how many index gateways should be used by a tenant for
# querying. If the global shard factor is 0, the global shard factor is set to
# the deprecated -replication-factor for backwards compatibility reasons.
# CLI flag: -index-gateway.shard-size
[index_gateway_shard_size: <int> | default = 0]

# Experimental. Defines a fraction (between 0.0 and 1.0) of the total index
# gateways available for a each tenant. A value of 0.0 has the same effect as
# 1.0, meaning all available index gateways. This setting only applies to simple
# mode.
# CLI flag: -index-gateway.max-capacity
[index_gateway_max_capacity: <float> | default = 1]

# Deprecated: Whether to use the bloom gateway component in the read path to
# filter chunks.
# CLI flag: -bloom-gateway.enable-filtering
[bloom_gateway_enable_filtering: <boolean> | default = false]

# Deprecated: Maximum number of builders to use when building blooms. 0 allows
# unlimited builders.
# CLI flag: -bloom-build.max-builders
[bloom_build_max_builders: <int> | default = 0]

# Deprecated: Maximum number of retries for a failed task. If a task fails more
# than this number of times, it is considered failed and will not be retried. A
# value of 0 disables this limit.
# CLI flag: -bloom-build.task-max-retries
[bloom_build_task_max_retries: <int> | default = 3]

# Deprecated: Timeout for a builder to finish a task. If a builder does not
# respond within this time, it is considered failed and the task will be
# requeued. 0 disables the timeout.
# CLI flag: -bloom-build.builder-response-timeout
[bloom_build_builder_response_timeout: <duration> | default = 0s]

# Deprecated: Whether to create blooms for the tenant.
# CLI flag: -bloom-build.enable
[bloom_creation_enabled: <boolean> | default = false]

# Experimental. Whether dataobj index compaction runs for the tenant.
# CLI flag: -dataobj-compaction.index.enable
[dataobj_index_compaction_enabled: <boolean> | default = false]

# Experimental. Whether dataobj log compaction runs for the tenant. Log
# compaction implies index compaction; enabling log compaction without index
# compaction is a configuration error.
# CLI flag: -dataobj-compaction.log.enable
[dataobj_log_compaction_enabled: <boolean> | default = false]

# Deprecated: Bloom planning strategy to use in bloom creation. Can be one of:
# 'split_keyspace_by_factor', 'split_by_series_chunks_size'
# CLI flag: -bloom-build.planning-strategy
[bloom_planning_strategy: <string> | default = "split_keyspace_by_factor"]

# Deprecated: Only if `bloom-build.planning-strategy` is 'split'. Number of
# splits to create for the series keyspace when building blooms. The series
# keyspace is split into this many parts to parallelize bloom creation.
# CLI flag: -bloom-build.split-keyspace-by
[bloom_split_series_keyspace_by: <int> | default = 256]

# Deprecated: Target chunk size in bytes for bloom tasks. Default is 20GB.
# CLI flag: -bloom-build.split-target-series-chunk-size
[bloom_task_target_series_chunk_size: <int> | default = 20GB]

# Deprecated: Compression algorithm for bloom block pages.
# CLI flag: -bloom-build.block-encoding
[bloom_block_encoding: <string> | default = "none"]

# Deprecated: Prefetch blocks on bloom gateways as soon as they are built.
# CLI flag: -bloom-build.prefetch-blocks
[bloom_prefetch_blocks: <boolean> | default = false]

# Deprecated: The maximum bloom block size. A value of 0 sets an unlimited size.
# Default is 200MB. The actual block size might exceed this limit since blooms
# will be added to blocks until the block exceeds the maximum block size.
# CLI flag: -bloom-build.max-block-size
[bloom_max_block_size: <int> | default = 200MB]

# Deprecated: The maximum bloom size per log stream. A log stream whose
# generated bloom filter exceeds this size will be discarded. A value of 0 sets
# an unlimited size. Default is 128MB.
# CLI flag: -bloom-build.max-bloom-size
[bloom_max_bloom_size: <int> | default = 128MB]

# Allow user to send structured metadata in push payload.
# CLI flag: -validation.allow-structured-metadata
[allow_structured_metadata: <boolean> | default = true]

# Maximum size accepted for structured metadata per log line.
# CLI flag: -limits.max-structured-metadata-size
[max_structured_metadata_size: <int> | default = 64KB]

# Maximum number of structured metadata entries per log line.
# CLI flag: -limits.max-structured-metadata-entries-count
[max_structured_metadata_entries_count: <int> | default = 128]

# OTLP log ingestion configurations
otlp_config:
  # Configuration for resource attributes to store them as index labels or
  # Structured Metadata or drop them altogether
  resource_attributes:
    # Configure whether to ignore the default list of resource attributes set in
    # 'distributor.otlp.default_resource_attributes_as_index_labels' to be
    # stored as index labels and only use the given resource attributes config
    [ignore_defaults: <boolean> | default = false]

    [attributes_config: <list of attributes_configs>]

  # Configuration for scope attributes to store them as Structured Metadata or
  # drop them altogether
  [scope_attributes: <list of attributes_configs>]

  # Configuration for log attributes to store them as index labels or Structured
  # Metadata or drop them altogether
  [log_attributes: <list of attributes_configs>]

  # When true, the severity_text field from log records will be stored as an
  # index label. It is recommended not to use this option unless absolutely
  # necessary
  [severity_text_as_label: <boolean> | default = false]

# Block ingestion for policy until the configured date. The policy '*' is the
# global policy, which is applied to all streams not matching a policy and can
# be overridden by other policies. The time should be in RFC3339 format. The
# policy is based on the policy_stream_mapping configuration.
[block_ingestion_policy_until: <map of string to Time>]

# Block ingestion until the configured date. The time should be in RFC3339
# format.
# CLI flag: -limits.block-ingestion-until
[block_ingestion_until: <time> | default = 0]

# HTTP status code to return when ingestion is blocked. If 200, the ingestion
# will be blocked without returning an error to the client. By Default, a custom
# status code (260) is returned to the client along with an error message.
# CLI flag: -limits.block-ingestion-status-code
[block_ingestion_status_code: <int> | default = 260]

# List of labels that must be present in the stream. If any of the labels are
# missing, the stream will be discarded. This flag configures it globally for
# all tenants. Experimental.
# CLI flag: -validation.enforced-labels
[enforced_labels: <list of strings> | default = []]

# Map of policies to enforced labels. The policy '*' is the global policy, which
# is applied to all streams and can be extended by other policies. Example:
#  policy_enforced_labels: 
#   policy1: 
#     - label1 
#     - label2 
#   policy2: 
#     - label3 
#     - label4
#   '*':
#     - label5
[policy_enforced_labels: <map of string to list of strings>]

# Map of policies to stream selectors with a priority. Experimental.  Example:
#  policy_stream_mapping: 
#   finance: 
#     - selector: '{namespace="prod", container="billing"}' 
#       priority: 2 
#   ops: 
#     - selector: '{namespace="prod", container="ops"}' 
#       priority: 1 
#   staging: 
#     - selector: '{namespace="staging"}' 
#       priority: 1
[policy_stream_mapping: <map of string to list of PriorityStreams>]

# The number of partitions a tenant's data should be sharded to when using kafka
# ingestion. Tenants are sharded across partitions using shuffle-sharding. 0
# disables shuffle sharding and tenant is sharded across all partitions.
# CLI flag: -limits.ingestion-partition-tenant-shard-size
[ingestion_partitions_tenant_shard_size: <int> | default = 0]

# List of LogQL vector and range aggregations that should be sharded.
[shard_aggregations: <list of strings>]

# Default list of JSON fields to tokenize for pattern detection. It is recommend
# to append to or delete from the defaults rather than replacing them.
# CLI flag: -limits.pattern-ingester-tokenizable-json-fields
[pattern_ingester_tokenizable_json_fields_default: <string> | default = "log,message,msg,msg_,_msg,content"]

# List of additional JSON fields to tokenize for pattern detection.
# CLI flag: -limits.pattern-ingester-tokenizable-json-fields-append
[pattern_ingester_tokenizable_json_fields_append: <string> | default = ""]

# List of JSON fields to excluded from the defaults to tokenize for pattern
# detection.
# CLI flag: -limits.pattern-ingester-tokenizable-json-fields-delete
[pattern_ingester_tokenizable_json_fields_delete: <string> | default = ""]

# Enable metric aggregation. When enabled, pushed streams will be sampled for
# bytes and line counts. These metrics will be written back into Loki as a
# special __aggregated_metric__ stream.
# CLI flag: -limits.aggregation-enabled
[metric_aggregation_enabled: <boolean> | default = false]

# Enable persistence of patterns detected at ingest. When enabled, patterns for
# pushed streams will be written back into Loki as a special __pattern__ stream.
# CLI flag: -limits.pattern-persistence-enabled
[pattern_persistence_enabled: <boolean> | default = false]

# The time granularity for persisting patterns. Controls how many data points
# are written when patterns are flushed. Set to 0 to use the default from the
# pattern ingester configuration.
# CLI flag: -limits.pattern-persistence-granularity
[pattern_persistence_granularity: <duration> | default = 0s]

# Minimum pattern rate (samples per second) required for a pattern to be
# persisted. Patterns with lower rates will be filtered out during persistence.
# CLI flag: -limits.pattern-rate-threshold
[pattern_rate_threshold: <float> | default = 1]

# S3 server-side encryption type. Required to enable server-side encryption
# overrides for a specific tenant. If not set, the default S3 client settings
# are used.
[s3_sse_type: <string> | default = ""]

# S3 server-side encryption KMS Key ID. Ignored if the SSE type override is not
# set.
[s3_sse_kms_key_id: <string> | default = ""]

# S3 server-side encryption KMS encryption context. If unset and the key ID
# override is set, the encryption context will not be provided to S3. Ignored if
# the SSE type override is not set.
[s3_sse_kms_encryption_context: <string> | default = ""]
```

### local_storage_config

The `local_storage_config` block configures the usage of local file system as object storage backend.

```yaml
# Directory to store chunks in.
# CLI flag: -local.chunk-directory
[directory: <string> | default = ""]
```

### memberlist

Configuration for `memberlist` client. Only applies if the selected kvstore is memberlist.

When a memberlist config with atleast 1 join_members is defined, kvstore of type memberlist is automatically selected for all the components that require a ring unless otherwise specified in the component's configuration section.

```yaml
# Name of the node in memberlist cluster. Defaults to hostname.
# CLI flag: -memberlist.nodename
[node_name: <string> | default = ""]

# Add random suffix to the node name.
# CLI flag: -memberlist.randomize-node-name
[randomize_node_name: <boolean> | default = true]

# The timeout for establishing a connection with a remote node, and for
# read/write operations.
# CLI flag: -memberlist.stream-timeout
[stream_timeout: <duration> | default = 2s]

# Multiplication factor used when sending out messages (factor * log(N+1)).
# CLI flag: -memberlist.retransmit-factor
[retransmit_factor: <int> | default = 4]

# How often to use pull/push sync.
# CLI flag: -memberlist.pullpush-interval
[pull_push_interval: <duration> | default = 30s]

# How often to gossip.
# CLI flag: -memberlist.gossip-interval
[gossip_interval: <duration> | default = 200ms]

# How many nodes to gossip to.
# CLI flag: -memberlist.gossip-nodes
[gossip_nodes: <int> | default = 3]

# How long to keep gossiping to dead nodes, to give them chance to refute their
# death.
# CLI flag: -memberlist.gossip-to-dead-nodes-time
[gossip_to_dead_nodes_time: <duration> | default = 30s]

# How soon can dead node's name be reclaimed with new address. 0 to disable.
# CLI flag: -memberlist.dead-node-reclaim-time
[dead_node_reclaim_time: <duration> | default = 0s]

# Enable message compression. This can be used to reduce bandwidth usage at the
# cost of slightly more CPU utilization.
# CLI flag: -memberlist.compression-enabled
[compression_enabled: <boolean> | default = true]

# How frequently to notify watchers when a key changes. Can reduce CPU activity
# in large memberlist deployments. 0 to notify without delay.
# CLI flag: -memberlist.notify-interval
[notify_interval: <duration> | default = 0s]

# Size of the internal queue for messages received from other nodes. Increasing
# this value may help to avoid dropping messages when the node is processing a
# large number of messages from other nodes.
# CLI flag: -memberlist.received-messages-queue-size
[received_messages_queue_size: <int> | default = 1024]

# Size of the per-key internal queue for processing messages received from other
# nodes. Increasing this value may help to avoid dropping per-key updates when
# the node is processing many updates for the same key.
# CLI flag: -memberlist.processed-messages-queue-size
[processed_messages_queue_size: <int> | default = 1024]

# Compression algorithm used for outgoing messages when
# -memberlist.compression-enabled is true. Supported values: lzw, snappy.
# Ignored when -memberlist.compression-enabled is false.
# CLI flag: -memberlist.compression-algorithm
[compression_algorithm: <string> | default = "lzw"]

# Gossip address to advertise to other members in the cluster. Used for NAT
# traversal.
# CLI flag: -memberlist.advertise-addr
[advertise_addr: <string> | default = ""]

# Gossip port to advertise to other members in the cluster. Used for NAT
# traversal.
# CLI flag: -memberlist.advertise-port
[advertise_port: <int> | default = 7946]

# The cluster label is an optional string to include in outbound packets and
# gossip streams. Other members in the memberlist cluster will discard any
# message whose label doesn't match the configured one, unless the
# 'cluster-label-verification-disabled' configuration option is set to true.
# CLI flag: -memberlist.cluster-label
[cluster_label: <string> | default = ""]

# When true, memberlist doesn't verify that inbound packets and gossip streams
# have the cluster label matching the configured one. This verification should
# be disabled while rolling out the change to the configured cluster label in a
# live memberlist cluster.
# CLI flag: -memberlist.cluster-label-verification-disabled
[cluster_label_verification_disabled: <boolean> | default = false]

# Other cluster members to join. Can be specified multiple times or as a
# comma-separated list. It can be an IP, hostname or an entry specified in the
# DNS Service Discovery format.
# CLI flag: -memberlist.join
[join_members: <list of strings>]

# Min backoff duration to join other cluster members.
# CLI flag: -memberlist.min-join-backoff
[min_join_backoff: <duration> | default = 1s]

# Max backoff duration to join other cluster members.
# CLI flag: -memberlist.max-join-backoff
[max_join_backoff: <duration> | default = 1m]

# Max number of retries to join other cluster members.
# CLI flag: -memberlist.max-join-retries
[max_join_retries: <int> | default = 10]

# Abort if this node fails the fast memberlist cluster joining procedure at
# startup. When enabled, it's guaranteed that other services, depending on
# memberlist, have an updated view over the cluster state when they're started.
# CLI flag: -memberlist.abort-if-fast-join-fails
[abort_if_cluster_fast_join_fails: <boolean> | default = false]

# Minimum number of seed nodes that must be successfully joined during fast-join
# for it to succeed. Only applies when -memberlist.abort-if-fast-join-fails is
# enabled.
# CLI flag: -memberlist.abort-if-fast-join-fails-min-nodes
[abort_if_cluster_fast_join_fails_min_nodes: <int> | default = 1]

# Abort if this node fails to join memberlist cluster at startup. When enabled,
# it's not guaranteed that other services are started only after the cluster
# state has been successfully updated; use 'abort-if-fast-join-fails' instead.
# CLI flag: -memberlist.abort-if-join-fails
[abort_if_cluster_join_fails: <boolean> | default = false]

# If not 0, how often to rejoin the cluster. Occasional rejoin can help to fix
# the cluster split issue, and is harmless otherwise. For example when using
# only few components as a seed nodes (via -memberlist.join), then it's
# recommended to use rejoin. If -memberlist.join points to dynamic service that
# resolves to all gossiping nodes (eg. Kubernetes headless service), then rejoin
# is not needed.
# CLI flag: -memberlist.rejoin-interval
[rejoin_interval: <duration> | default = 0s]

# Seed nodes to use for periodic rejoin. Takes precedence over -memberlist.join
# for rejoining. If not specified, -memberlist.join is used. Can be specified
# multiple times or as a comma-separated list. Supports IP, hostname, or DNS
# Service Discovery format.
# CLI flag: -memberlist.rejoin-seed-nodes
[rejoin_seed_nodes: <list of strings>]

# How long to keep LEFT ingesters in the ring.
# CLI flag: -memberlist.left-ingesters-timeout
[left_ingesters_timeout: <duration> | default = 5m]

# How long to keep obsolete entries in the KV store.
# CLI flag: -memberlist.obsolete-entries-timeout
[obsolete_entries_timeout: <duration> | default = 30s]

# Timeout for leaving memberlist cluster.
# CLI flag: -memberlist.leave-timeout
[leave_timeout: <duration> | default = 20s]

# Timeout for broadcasting all remaining locally-generated updates to other
# nodes when shutting down. Only used if there are nodes left in the memberlist
# cluster, and only applies to locally-generated updates, not to broadcast
# messages that are result of incoming gossip updates. 0 = no timeout, wait
# until all locally-generated updates are sent.
# CLI flag: -memberlist.broadcast-timeout-for-local-updates-on-shutdown
[broadcast_timeout_for_local_updates_on_shutdown: <duration> | default = 10s]

# How much space to use for keeping received and sent messages in memory for
# troubleshooting (two buffers). 0 to disable.
# CLI flag: -memberlist.message-history-buffer-bytes
[message_history_buffer_bytes: <int> | default = 0]

# Size of the buffered channel for the WatchPrefix function.
# CLI flag: -memberlist.watch-prefix-buffer-size
[watch_prefix_buffer_size: <int> | default = 128]

# Minimum delay between CAS retries after a version mismatch. 0 disables the
# delay.
# CLI flag: -memberlist.cas-retry-min-backoff
[cas_retry_min_backoff: <duration> | default = 0s]

# Maximum delay between CAS retries after a version mismatch. Only takes effect
# if cas-retry-min-backoff is also set.
# CLI flag: -memberlist.cas-retry-max-backoff
[cas_retry_max_backoff: <duration> | default = 10s]

# Cache the serialized form of each key, so that push/pull sync doesn't
# re-encode entries that haven't changed since the last sync. Reduces CPU and
# lock contention in large clusters, at the cost of memory proportional to the
# size of the KV store.
# CLI flag: -memberlist.local-state-cache-enabled
[local_state_cache_enabled: <boolean> | default = false]

# IP address to listen on for gossip messages. Multiple addresses may be
# specified. Defaults to 0.0.0.0
# CLI flag: -memberlist.bind-addr
[bind_addr: <list of strings> | default = []]

# Port to listen on for gossip messages.
# CLI flag: -memberlist.bind-port
[bind_port: <int> | default = 7946]

# Timeout used when connecting to other nodes to send packet.
# CLI flag: -memberlist.packet-dial-timeout
[packet_dial_timeout: <duration> | default = 2s]

# Timeout for writing 'packet' data.
# CLI flag: -memberlist.packet-write-timeout
[packet_write_timeout: <duration> | default = 5s]

# Maximum number of concurrent writes to other nodes.
# CLI flag: -memberlist.max-concurrent-writes
[max_concurrent_writes: <int> | default = 3]

# Timeout for acquiring one of the concurrent write slots. After this time, the
# message will be dropped.
# CLI flag: -memberlist.acquire-writer-timeout
[acquire_writer_timeout: <duration> | default = 250ms]

# Enable TLS on the memberlist transport layer.
# CLI flag: -memberlist.tls-enabled
[tls_enabled: <boolean> | default = false]

# The TLS configuration.
# The CLI flags prefix for this block configuration is: memberlist
[<tls_config>]

zone_aware_routing:
  # Enable zone-aware routing for memberlist gossip.
  # CLI flag: -memberlist.zone-aware-routing.enabled
  [enabled: <boolean> | default = false]

  # Availability zone where this node is running.
  # CLI flag: -memberlist.zone-aware-routing.instance-availability-zone
  [instance_availability_zone: <string> | default = ""]

  # Role of this node in the cluster. Valid values: member, bridge.
  # CLI flag: -memberlist.zone-aware-routing.role
  [role: <string> | default = "member"]

propagation_delay_tracker:
  # Enable the propagation delay tracker to measure gossip propagation delay.
  # CLI flag: -memberlist.propagation-delay-tracker.enabled
  [enabled: <boolean> | default = false]

  # How often to publish beacons for propagation tracking.
  # CLI flag: -memberlist.propagation-delay-tracker.beacon-interval
  [beacon_interval: <duration> | default = 1m]

  # How long a beacon lives before being garbage collected.
  # CLI flag: -memberlist.propagation-delay-tracker.beacon-lifetime
  [beacon_lifetime: <duration> | default = 10m]

  # Log warning when beacon propagation delay exceeds this threshold. 0 disables
  # logging.
  # CLI flag: -memberlist.propagation-delay-tracker.log-beacons-latency-longer-than
  [log_beacons_latency_longer_than: <duration> | default = 0s]
```

### named_stores_config

Configures additional object stores for a given storage provider.
Supported stores: aws, azure, bos, filesystem, gcs, swift.
Example:
```yaml
    storage_config:
      named_stores:
        aws:
          store-1:
            endpoint: s3://foo-bucket
            region: us-west1
```
Named store from this example can be used by setting object_store to store-1 in period_config.

```yaml
[aws: <map of string to s3_storage_config>]

[azure: <map of string to azure_storage_config>]

[bos: <map of string to bos_storage_config>]

[filesystem: <map of string to local_storage_config>]

[gcs: <map of string to gcs_storage_config>]

[alibabacloud: <map of string to alibabacloud_storage_config>]

[swift: <map of string to swift_storage_config>]

[cos: <map of string to cos_storage_config>]
```

### operational_config

These are values which allow you to control aspects of Loki's operation, most commonly used for controlling types of higher verbosity logging, the values here can be overridden in the `configs` section of the `runtime_config` file.

```yaml
# Log every new stream created by a push request (very verbose, recommend to
# enable via runtime config only).
# CLI flag: -operation-config.log-stream-creation
[log_stream_creation: <boolean> | default = false]

# Log every push request (very verbose, recommend to enable via runtime config
# only).
# CLI flag: -operation-config.log-push-request
[log_push_request: <boolean> | default = false]

# Log a commutative hash of the labels for all streams in a push request. In
# some cases this can potentially be used as an identifier of the agent sending
# the stream. Calculating hashes is epensive so only enable as needed.
# CLI flag: -operation-config.log-hash-of-labels
[log_hash_of_labels: <boolean> | default = false]

# Log every stream in a push request (very verbose, recommend to enable via
# runtime config only).
# CLI flag: -operation-config.log-push-request-streams
[log_push_request_streams: <boolean> | default = false]

# Only show streams that match a provided IP address, LogPushRequestStreams must
# be enabled. Can be used multiple times to filter by multiple IPs.
# CLI flag: -operation-config.filter-push-request-streams-ips
[filter_push_request_streams_ips: <list of strings> | default = []]

# Log service name discovery (very verbose, recommend to enable via runtime
# config only).
# CLI flag: -operation-config.log-service-name-discovery
[log_service_name_discovery: <boolean> | default = false]

# Log metrics for duplicate lines received.
# CLI flag: -operation-config.log-duplicate-metrics
[log_duplicate_metrics: <boolean> | default = false]

# Log stream info for duplicate lines received
# CLI flag: -operation-config.log-duplicate-stream-info
[log_duplicate_stream_info: <boolean> | default = false]

# Log which OTLP resource and scope attributes are expanded into structured
# metadata, for a rate limited subset of push requests. Only attribute names and
# sizes are logged, never values.
# CLI flag: -operation-config.log-otlp-attribute-expansion
[log_otlp_attribute_expansion: <boolean> | default = false]

# Log push errors with a rate limited logger, will show client push errors
# without overly spamming logs.
# CLI flag: -operation-config.limited-log-push-errors
[limited_log_push_errors: <boolean> | default = true]
```

### period_config

The `period_config` block configures what index schemas should be used for from specific time periods.

```yaml
# The date of the first day that index buckets should be created. Use a date in
# the past if this is your only period_config, otherwise use a date when you
# want the schema to switch over. In YYYY-MM-DD format, for example: 2018-04-15.
[from: <daytime>]

# store and object_store below affect which <storage_config> key is used. Which
# index to use. Only tsdb is supported.
[store: <string> | default = ""]

# Which store to use for the chunks. Either aws (alias s3), azure, gcs,
# alibabacloud, bos, cos, swift, filesystem, or a named_store (refer to
# named_stores_config).
[object_store: <string> | default = ""]

# The schema version to use, current recommended schema is v13.
[schema: <string> | default = ""]

# Configures how the index is updated and stored.
index:
  # Path prefix for index tables. Prefix always needs to end with a path
  # delimiter '/', except when the prefix is empty.
  [path_prefix: <string> | default = "index/"]

  # Table prefix for all period tables.
  [prefix: <string> | default = ""]

  # Table period.
  [period: <duration>]
```

### profiling

Configuration for `profiling` options.

```yaml
# Sets the value for runtime.SetBlockProfilingRate
# CLI flag: -profiling.block-profile-rate
[block_profile_rate: <int> | default = 0]

# Sets the value for runtime.SetCPUProfileRate
# CLI flag: -profiling.cpu-profile-rate
[cpu_profile_rate: <int> | default = 0]

# Sets the value for runtime.SetMutexProfileFraction
# CLI flag: -profiling.mutex-profile-fraction
[mutex_profile_fraction: <int> | default = 0]
```

### querier

Configures the `querier`. Only appropriate when running all modules or just the querier.

```yaml
# Maximum duration for which the live tailing requests are served.
# CLI flag: -querier.tail-max-duration
[tail_max_duration: <duration> | default = 1h]

# Time to wait before sending more than the minimum successful query requests.
# CLI flag: -querier.extra-query-delay
[extra_query_delay: <duration> | default = 0s]

# Maximum lookback beyond which queries are not sent to ingester. 0 means all
# queries are sent to ingester.
# CLI flag: -querier.query-ingesters-within
[query_ingesters_within: <duration> | default = 3h]

engine:
  # The maximum amount of time to look back for log lines. Used only for instant
  # log queries.
  # CLI flag: -querier.engine.max-lookback-period
  [max_look_back_period: <duration> | default = 30s]

  # The maximum number of labels the heap of a topk query using a count min
  # sketch can track.
  # CLI flag: -querier.engine.max-count-min-sketch-heap-size
  [max_count_min_sketch_heap_size: <int> | default = 10000]

# The maximum number of queries that can be simultaneously processed by the
# querier.
# CLI flag: -querier.max-concurrent
[max_concurrent: <int> | default = 4]

# Only query the store, and not attempt any ingesters. This is useful for
# running a standalone querier pool operating only against stored data.
# CLI flag: -querier.query-store-only
[query_store_only: <boolean> | default = false]

# When true, queriers only query the ingesters, and not stored data. This is
# useful when the object store is unavailable.
# CLI flag: -querier.query-ingester-only
[query_ingester_only: <boolean> | default = false]

# When true, allow queries to span multiple tenants.
# CLI flag: -querier.multi-tenant-queries-enabled
[multi_tenant_queries_enabled: <boolean> | default = false]

# When true, querier limits sent via a header are enforced.
# CLI flag: -querier.per-request-limits-enabled
[per_request_limits_enabled: <boolean> | default = false]

# When true, querier directs ingester queries to the partition-ingesters instead
# of the normal ingesters.
# CLI flag: -querier.query-partition-ingesters
[query_partition_ingesters: <boolean> | default = false]
```

### query_range

The `query_range` block configures the query splitting and caching in the Loki query-frontend.

```yaml
# Mutate incoming queries to align their start and end with their step.
# CLI flag: -querier.align-querier-with-step
[align_queries_with_step: <boolean> | default = false]

results_cache:
  # The cache_config block configures the cache backend for a specific Loki
  # component.
  # The CLI flags prefix for this block configuration is: frontend
  [cache: <cache_config>]

  # Use compression in cache. The default is an empty value '', which disables
  # compression. Supported values are: 'snappy' and ''.
  # CLI flag: -frontend.compression
  [compression: <string> | default = ""]

# Cache query results.
# CLI flag: -querier.cache-results
[cache_results: <boolean> | default = false]

# Maximum number of retries for a single request; beyond this, the downstream
# error is returned.
# CLI flag: -querier.max-retries-per-request
[max_retries: <int> | default = 5]

# Perform query parallelisations based on storage sharding configuration and
# query ASTs. This feature is supported only by the chunks storage engine.
# CLI flag: -querier.parallelise-shardable-queries
[parallelise_shardable_queries: <boolean> | default = true]

# A comma-separated list of LogQL vector and range aggregations that should be
# sharded. Possible values 'quantile_over_time', 'last_over_time',
# 'first_over_time'.
# CLI flag: -querier.shard-aggregations
[shard_aggregations: <string> | default = ""]

# Cache index stats query results.
# CLI flag: -querier.cache-index-stats-results
[cache_index_stats_results: <boolean> | default = true]

# If a cache config is not specified and cache_index_stats_results is true, the
# config for the results cache is used.
index_stats_results_cache:
  # The cache_config block configures the cache backend for a specific Loki
  # component.
  # The CLI flags prefix for this block configuration is:
  # frontend.index-stats-results-cache
  [cache: <cache_config>]

  # Use compression in cache. The default is an empty value '', which disables
  # compression. Supported values are: 'snappy' and ''.
  # CLI flag: -frontend.index-stats-results-cache.compression
  [compression: <string> | default = ""]

# Cache volume query results.
# CLI flag: -querier.cache-volume-results
[cache_volume_results: <boolean> | default = true]

# If a cache config is not specified and cache_volume_results is true, the
# config for the results cache is used.
volume_results_cache:
  # The cache_config block configures the cache backend for a specific Loki
  # component.
  # The CLI flags prefix for this block configuration is:
  # frontend.volume-results-cache
  [cache: <cache_config>]

  # Use compression in cache. The default is an empty value '', which disables
  # compression. Supported values are: 'snappy' and ''.
  # CLI flag: -frontend.volume-results-cache.compression
  [compression: <string> | default = ""]

# Cache instant metric query results.
# CLI flag: -querier.cache-instant-metric-results
[cache_instant_metric_results: <boolean> | default = false]

# If a cache config is not specified and cache_instant_metric_results is true,
# the config for the results cache is used.
instant_metric_results_cache:
  # The cache_config block configures the cache backend for a specific Loki
  # component.
  # The CLI flags prefix for this block configuration is:
  # frontend.instant-metric-results-cache
  [cache: <cache_config>]

  # Use compression in cache. The default is an empty value '', which disables
  # compression. Supported values are: 'snappy' and ''.
  # CLI flag: -frontend.instant-metric-results-cache.compression
  [compression: <string> | default = ""]

# Whether to align the splits of instant metric query with splitByInterval and
# query's exec time. Useful when instant_metric_cache is enabled
# CLI flag: -querier.instant-metric-query-split-align
[instant_metric_query_split_align: <boolean> | default = false]

# Cache series query results.
# CLI flag: -querier.cache-series-results
[cache_series_results: <boolean> | default = true]

# If series_results_cache is not configured and cache_series_results is true,
# the config for the results cache is used.
series_results_cache:
  # The cache_config block configures the cache backend for a specific Loki
  # component.
  # The CLI flags prefix for this block configuration is:
  # frontend.series-results-cache
  [cache: <cache_config>]

  # Use compression in cache. The default is an empty value '', which disables
  # compression. Supported values are: 'snappy' and ''.
  # CLI flag: -frontend.series-results-cache.compression
  [compression: <string> | default = ""]

# Cache label query results.
# CLI flag: -querier.cache-label-results
[cache_label_results: <boolean> | default = true]

# If label_results_cache is not configured and cache_label_results is true, the
# config for the results cache is used.
label_results_cache:
  # The cache_config block configures the cache backend for a specific Loki
  # component.
  # The CLI flags prefix for this block configuration is:
  # frontend.label-results-cache
  [cache: <cache_config>]

  # Use compression in cache. The default is an empty value '', which disables
  # compression. Supported values are: 'snappy' and ''.
  # CLI flag: -frontend.label-results-cache.compression
  [compression: <string> | default = ""]
```

### query_scheduler

The `query_scheduler` block configures the Loki query scheduler. When configured it separates the tenant query queues from the query-frontend.

```yaml
# Maximum number of outstanding requests per tenant per query-scheduler.
# In-flight requests above this limit will fail with HTTP response status code
# 429.
# CLI flag: -query-scheduler.max-outstanding-requests-per-tenant
[max_outstanding_requests_per_tenant: <int> | default = 32000]

# Maximum number of levels of nesting of hierarchical queues. 0 means that
# hierarchical queues are disabled.
# CLI flag: -query-scheduler.max-queue-hierarchy-levels
[max_queue_hierarchy_levels: <int> | default = 3]

# If a querier disconnects without sending notification about graceful shutdown,
# the query-scheduler will keep the querier in the tenant's shard until the
# forget delay has passed. This feature is useful to reduce the blast radius
# when shuffle-sharding is enabled.
# CLI flag: -query-scheduler.querier-forget-delay
[querier_forget_delay: <duration> | default = 0s]

# This configures the gRPC client used to report errors back to the
# query-frontend.
# The CLI flags prefix for this block configuration is:
# query-scheduler.grpc-client-config
[grpc_client_config: <grpc_client>]

# Set to true to have the query schedulers create and place themselves in a
# ring. If no frontend_address or scheduler_address are present anywhere else in
# the configuration, Loki will toggle this value to true.
# CLI flag: -query-scheduler.use-scheduler-ring
[use_scheduler_ring: <boolean> | default = false]

# The hash ring configuration. This option is required only if
# use_scheduler_ring is true.
scheduler_ring:
  kvstore:
    # Backend storage to use for the ring. Supported values are: consul, etcd,
    # inmemory, memberlist, multi.
    # CLI flag: -query-scheduler.ring.store
    [store: <string> | default = "consul"]

    # The prefix for the keys in the store. Should end with a /.
    # CLI flag: -query-scheduler.ring.prefix
    [prefix: <string> | default = "collectors/"]

    # Configuration for a Consul client. Only applies if the selected kvstore is
    # consul.
    # The CLI flags prefix for this block configuration is: query-scheduler.ring
    [consul: <consul>]

    # Configuration for an ETCD v3 client. Only applies if the selected kvstore
    # is etcd.
    # The CLI flags prefix for this block configuration is: query-scheduler.ring
    [etcd: <etcd>]

    multi:
      # Primary backend storage used by multi-client.
      # CLI flag: -query-scheduler.ring.multi.primary
      [primary: <string> | default = ""]

      # Secondary backend storage used by multi-client.
      # CLI flag: -query-scheduler.ring.multi.secondary
      [secondary: <string> | default = ""]

      # Mirror writes to the secondary store.
      # CLI flag: -query-scheduler.ring.multi.mirror-enabled
      [mirror_enabled: <boolean> | default = false]

      # Timeout for storing a value to the secondary store.
      # CLI flag: -query-scheduler.ring.multi.mirror-timeout
      [mirror_timeout: <duration> | default = 2s]

  # Period at which to heartbeat to the ring.
  # CLI flag: -query-scheduler.ring.heartbeat-period
  [heartbeat_period: <duration> | default = 15s]

  # The heartbeat timeout after which compactors are considered unhealthy within
  # the ring. 0 = never (timeout disabled).
  # CLI flag: -query-scheduler.ring.heartbeat-timeout
  [heartbeat_timeout: <duration> | default = 1m]

  # File path where tokens are stored. If empty, tokens are not stored at
  # shutdown and restored at startup.
  # CLI flag: -query-scheduler.ring.tokens-file-path
  [tokens_file_path: <string> | default = ""]

  # True to enable zone-awareness and replicate blocks across different
  # availability zones.
  # CLI flag: -query-scheduler.ring.zone-awareness-enabled
  [zone_awareness_enabled: <boolean> | default = false]

  # Instance ID to register in the ring.
  # CLI flag: -query-scheduler.ring.instance-id
  [instance_id: <string> | default = "<hostname>"]

  # Name of network interface to read address from.
  # CLI flag: -query-scheduler.ring.instance-interface-names
  [instance_interface_names: <list of strings> | default = [<private network interfaces>]]

  # Port to advertise in the ring (defaults to server.grpc-listen-port).
  # CLI flag: -query-scheduler.ring.instance-port
  [instance_port: <int> | default = 0]

  # IP address to advertise in the ring.
  # CLI flag: -query-scheduler.ring.instance-addr
  [instance_addr: <string> | default = ""]

  # The availability zone where this instance is running. Required if
  # zone-awareness is enabled.
  # CLI flag: -query-scheduler.ring.instance-availability-zone
  [instance_availability_zone: <string> | default = ""]

  # Enable using a IPv6 instance address.
  # CLI flag: -query-scheduler.ring.instance-enable-ipv6
  [instance_enable_ipv6: <boolean> | default = false]
```

### ruler

The `ruler` block configures the Loki ruler.

```yaml
# Base URL of the Grafana instance.
# CLI flag: -ruler.external.url
[external_url: <url>]

# Datasource UID for the dashboard.
# CLI flag: -ruler.datasource-uid
[datasource_uid: <string> | default = ""]

# Labels to add to all alerts.
external_labels:

# The grpc_client block configures the gRPC client used to communicate between a
# client and server component in Loki.
# The CLI flags prefix for this block configuration is: ruler.client
[ruler_client: <grpc_client>]

# How frequently to evaluate rules.
# CLI flag: -ruler.evaluation-interval
[evaluation_interval: <duration> | default = 1m]

# How frequently to poll for rule changes.
# CLI flag: -ruler.poll-interval
[poll_interval: <duration> | default = 1m]

# Deprecated: Use -ruler-storage. CLI flags and their respective YAML config
# options instead.
storage:
  # Method to use for backend rule storage (configdb, azure, gcs, s3, swift,
  # local, bos, cos)
  # CLI flag: -ruler.storage.type
  [type: <string> | default = ""]

  # Configures backend rule storage for Azure.
  # The CLI flags prefix for this block configuration is: ruler.storage
  [azure: <azure_storage_config>]

  # Configures backend rule storage for AlibabaCloud Object Storage (OSS).
  # The CLI flags prefix for this block configuration is: ruler.storage
  [alibabacloud: <alibabacloud_storage_config>]

  # Configures backend rule storage for GCS.
  # The CLI flags prefix for this block configuration is: ruler.storage
  [gcs: <gcs_storage_config>]

  # Configures backend rule storage for S3.
  # The CLI flags prefix for this block configuration is: ruler.storage
  [s3: <s3_storage_config>]

  # Configures backend rule storage for Baidu Object Storage (BOS).
  # The CLI flags prefix for this block configuration is: ruler.storage
  [bos: <bos_storage_config>]

  # Configures backend rule storage for Swift.
  # The CLI flags prefix for this block configuration is: ruler.storage
  [swift: <swift_storage_config>]

  # Configures backend rule storage for IBM Cloud Object Storage (COS).
  # The CLI flags prefix for this block configuration is: ruler.storage
  [cos: <cos_storage_config>]

  # Configures backend rule storage for a local file system directory.
  local:
    # Directory to scan for rules
    # CLI flag: -ruler.storage.local.directory
    [directory: <string> | default = ""]

# File path to store temporary rule files.
# CLI flag: -ruler.rule-path
[rule_path: <string> | default = "/rules"]

# Comma-separated list of Alertmanager URLs to send notifications to. Each
# Alertmanager URL is treated as a separate group in the configuration. Multiple
# Alertmanagers in HA per group can be supported by using DNS resolution via
# '-ruler.alertmanager-discovery'.
# CLI flag: -ruler.alertmanager-url
[alertmanager_url: <string> | default = ""]

# Use DNS SRV records to discover Alertmanager hosts.
# CLI flag: -ruler.alertmanager-discovery
[enable_alertmanager_discovery: <boolean> | default = false]

# How long to wait between refreshing DNS resolutions of Alertmanager hosts.
# CLI flag: -ruler.alertmanager-refresh-interval
[alertmanager_refresh_interval: <duration> | default = 1m]

# Use Alertmanager APIv2. APIv1 was deprecated in Alertmanager 0.16.0 and is
# removed as of 0.27.0.
# CLI flag: -ruler.alertmanager-use-v2
[enable_alertmanager_v2: <boolean> | default = true]

# List of alert relabel configs.
[alert_relabel_configs: <relabel_config...>]

# Capacity of the queue for notifications to be sent to the Alertmanager.
# CLI flag: -ruler.notification-queue-capacity
[notification_queue_capacity: <int> | default = 10000]

# HTTP timeout duration when sending notifications to the Alertmanager.
# CLI flag: -ruler.notification-timeout
[notification_timeout: <duration> | default = 10s]

alertmanager_client:
  # The TLS configuration.
  # The CLI flags prefix for this block configuration is:
  # ruler.alertmanager-client
  [<tls_config>]

  # HTTP Basic authentication username. It overrides the username set in the URL
  # (if any).
  # CLI flag: -ruler.alertmanager-client.basic-auth-username
  [basic_auth_username: <string> | default = ""]

  # HTTP Basic authentication password. It overrides the password set in the URL
  # (if any).
  # CLI flag: -ruler.alertmanager-client.basic-auth-password
  [basic_auth_password: <string> | default = ""]

  # HTTP Header authorization type (default: Bearer).
  # CLI flag: -ruler.alertmanager-client.type
  [type: <string> | default = "Bearer"]

  # HTTP Header authorization credentials.
  # CLI flag: -ruler.alertmanager-client.credentials
  [credentials: <string> | default = ""]

  # HTTP Header authorization credentials file.
  # CLI flag: -ruler.alertmanager-client.credentials-file
  [credentials_file: <string> | default = ""]

# Max time to tolerate outage for restoring "for" state of alert.
# CLI flag: -ruler.for-outage-tolerance
[for_outage_tolerance: <duration> | default = 1h]

# Minimum duration between alert and restored "for" state. This is maintained
# only for alerts with configured "for" time greater than the grace period.
# CLI flag: -ruler.for-grace-period
[for_grace_period: <duration> | default = 10m]

# Minimum amount of time to wait before resending an alert to Alertmanager.
# CLI flag: -ruler.resend-delay
[resend_delay: <duration> | default = 1m]

# Distribute rule evaluation using ring backend.
# CLI flag: -ruler.enable-sharding
[enable_sharding: <boolean> | default = false]

# The sharding strategy to use. Supported values are: default, shuffle-sharding.
# CLI flag: -ruler.sharding-strategy
[sharding_strategy: <string> | default = "default"]

# The sharding algorithm to use for deciding how rules & groups are sharded.
# Supported values are: by-group, by-rule.
# CLI flag: -ruler.sharding-algo
[sharding_algo: <string> | default = "by-group"]

# Time to spend searching for a pending ruler when shutting down.
# CLI flag: -ruler.search-pending-for
[search_pending_for: <duration> | default = 5m]

# Ring used by Loki ruler. The CLI flags prefix for this block configuration is
# 'ruler.ring'.
ring:
  kvstore:
    # Backend storage to use for the ring. Supported values are: consul, etcd,
    # inmemory, memberlist, multi.
    # CLI flag: -ruler.ring.store
    [store: <string> | default = "consul"]

    # The prefix for the keys in the store. Should end with a /.
    # CLI flag: -ruler.ring.prefix
    [prefix: <string> | default = "rulers/"]

    # Configuration for a Consul client. Only applies if the selected kvstore is
    # consul.
    # The CLI flags prefix for this block configuration is: ruler.ring
    [consul: <consul>]

    # Configuration for an ETCD v3 client. Only applies if the selected kvstore
    # is etcd.
    # The CLI flags prefix for this block configuration is: ruler.ring
    [etcd: <etcd>]

    multi:
      # Primary backend storage used by multi-client.
      # CLI flag: -ruler.ring.multi.primary
      [primary: <string> | default = ""]

      # Secondary backend storage used by multi-client.
      # CLI flag: -ruler.ring.multi.secondary
      [secondary: <string> | default = ""]

      # Mirror writes to the secondary store.
      # CLI flag: -ruler.ring.multi.mirror-enabled
      [mirror_enabled: <boolean> | default = false]

      # Timeout for storing a value to the secondary store.
      # CLI flag: -ruler.ring.multi.mirror-timeout
      [mirror_timeout: <duration> | default = 2s]

  # Interval between heartbeats sent to the ring.
  # CLI flag: -ruler.ring.heartbeat-period
  [heartbeat_period: <duration> | default = 5s]

  # The heartbeat timeout after which ruler ring members are considered
  # unhealthy within the ring. 0 = never (timeout disabled).
  # CLI flag: -ruler.ring.heartbeat-timeout
  [heartbeat_timeout: <duration> | default = 1m]

  # Name of network interface to read addresses from.
  # CLI flag: -ruler.ring.instance-interface-names
  [instance_interface_names: <list of strings> | default = [<private network interfaces>]]

  # The number of tokens the lifecycler will generate and put into the ring if
  # it joined without transferring tokens from another lifecycler.
  # CLI flag: -ruler.ring.num-tokens
  [num_tokens: <int> | default = 128]

# Period with which to attempt to flush rule groups.
# CLI flag: -ruler.flush-period
[flush_period: <duration> | default = 1m]

# Enable the ruler API.
# CLI flag: -ruler.enable-api
[enable_api: <boolean> | default = true]

# Comma separated list of tenants whose rules this ruler can evaluate. If
# specified, only these tenants will be handled by ruler, otherwise this ruler
# can process rules from all tenants. Subject to sharding.
# CLI flag: -ruler.enabled-tenants
[enabled_tenants: <string> | default = ""]

# Comma separated list of tenants whose rules this ruler cannot evaluate. If
# specified, a ruler that would normally pick the specified tenant(s) for
# processing will ignore them instead. Subject to sharding.
# CLI flag: -ruler.disabled-tenants
[disabled_tenants: <string> | default = ""]

# Report the wall time for ruler queries to complete as a per user metric and as
# an info level log message.
# CLI flag: -ruler.query-stats-enabled
[query_stats_enabled: <boolean> | default = false]

# Disable the rule_group label on exported metrics.
# CLI flag: -ruler.disable-rule-group-label
[disable_rule_group_label: <boolean> | default = false]

wal:
  # The directory in which to write tenant WAL files. Each tenant will have its
  # own directory one level below this directory.
  # CLI flag: -ruler.wal.dir
  [dir: <string> | default = "ruler-wal"]

  # Frequency with which to run the WAL truncation process.
  # CLI flag: -ruler.wal.truncate-frequency
  [truncate_frequency: <duration> | default = 1h]

  # Minimum age that samples must exist in the WAL before being truncated.
  # CLI flag: -ruler.wal.min-age
  [min_age: <duration> | default = 5m]

  # Maximum age that samples must exist in the WAL before being truncated.
  # CLI flag: -ruler.wal.max-age
  [max_age: <duration> | default = 4h]

wal_cleaner:
  # The minimum age of a WAL to consider for cleaning.
  # CLI flag: -ruler.wal-cleaner.min-age
  [min_age: <duration> | default = 12h]

  # How often to run the WAL cleaner. 0 = disabled.
  # CLI flag: -ruler.wal-cleaner.period
  [period: <duration> | default = 0s]

# Remote-write configuration to send rule samples to a Prometheus remote-write
# endpoint.
remote_write:
  # Configure remote write clients. A map with remote client id as key. For
  # details, see
  # https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write
  # Specifying a header with key 'X-Scope-OrgID' under the 'headers' section of
  # RemoteWriteConfig is not permitted. If specified, it will be dropped during
  # config parsing.
  [clients: <map of string to RemoteWriteConfig>]

  # Enable remote-write functionality.
  # CLI flag: -ruler.remote-write.enabled
  [enabled: <boolean> | default = false]

  # Minimum period to wait between refreshing remote-write reconfigurations.
  # This should be greater than or equivalent to -runtime-config.reload-period.
  # CLI flag: -ruler.remote-write.config-refresh-period
  [config_refresh_period: <duration> | default = 10s]

  # Add an X-Scope-OrgID header in remote write requests with the tenant ID of a
  # Loki tenant that the recording rules are part of.
  # CLI flag: -ruler.remote-write.add-org-id-header
  [add_org_id_header: <boolean> | default = true]

# Configuration for rule evaluation.
evaluation:
  # The evaluation mode for the ruler. Can be either 'local' or 'remote'. If set
  # to 'local', the ruler will evaluate rules locally. If set to 'remote', the
  # ruler will evaluate rules remotely. If unset, the ruler will evaluate rules
  # locally.
  # CLI flag: -ruler.evaluation.mode
  [mode: <string> | default = "local"]

  # Upper bound of random duration to wait before rule evaluation to avoid
  # contention during concurrent execution of rules. Jitter is calculated
  # consistently for a given rule. Set 0 to disable (default).
  # CLI flag: -ruler.evaluation.max-jitter
  [max_jitter: <duration> | default = 0s]

  query_frontend:
    # GRPC listen address of the query-frontend(s). Must be a DNS address
    # (prefixed with dns:///) to enable client side load balancing.
    # CLI flag: -ruler.evaluation.query-frontend.address
    [address: <string> | default = ""]

    # The grpc_client block configures the gRPC client used to communicate
    # between a client and server component in Loki.
    # The CLI flags prefix for this block configuration is:
    # ruler.evaluation.query-frontend
    [<grpc_client>]
```

### runtime_config

Configuration for 'runtime config' module, responsible for reloading runtime configuration file.

```yaml
# How often to check runtime config files.
# CLI flag: -runtime-config.reload-period
[period: <duration> | default = 10s]

# Comma separated list of yaml files or URLs with the configuration that can be
# updated at runtime. Runtime config files will be merged from left to right. An
# entry can end with semicolon-separated parameters that say what happens when
# it cannot be read: ";optional-on-startup" lets the process start without it,
# but a later failure still fails the reload;
# ";optional-keep-last-value-on-failure" also lets the process start without it,
# and a later failure keeps the value the source supplied last. Without a
# parameter, a source that cannot be read fails the load. Quote the value in a
# shell, because ";" starts a new command.
# CLI flag: -runtime-config.file
[file: <string> | default = ""]

# HTTP client timeout when fetching runtime config from URLs.
# CLI flag: -runtime-config.http-client-timeout
[http_client_timeout: <duration> | default = 30s]

http_client_cluster_validation:
  # Primary cluster validation label.
  # CLI flag: -runtime-config.http-client-cluster-validation.label
  [label: <string> | default = ""]

# Disable HTTP keep-alives for the runtime config HTTP client. When enabled,
# each reload opens a new connection, which prevents long-lived connections from
# being pinned to a single backend when the runtime config URL is served by
# multiple replicas behind a connection-level (L4) load balancer, such as a
# Kubernetes Service.
# CLI flag: -runtime-config.http-client-disable-keep-alives
[http_client_disable_keep_alives: <boolean> | default = true]
```

### s3_storage_config

The `s3_storage_config` block configures the connection to Amazon S3 object storage backend. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `common.storage`
- `ruler.storage`

&nbsp;

```yaml
# S3 endpoint URL with escaped Key and Secret encoded. If only region is
# specified as a host, proper endpoint will be deduced. Use
# inmemory:///<bucket-name> to use a mock in-memory implementation.
# CLI flag: -<prefix>.s3.url
[s3: <url>]

# Set this to `true` to force the request to use path-style addressing.
# CLI flag: -<prefix>.s3.force-path-style
[s3forcepathstyle: <boolean> | default = false]

# Comma separated list of bucket names to evenly distribute chunks over.
# Overrides any buckets specified in s3.url flag
# CLI flag: -<prefix>.s3.buckets
[bucketnames: <string> | default = ""]

# S3 Endpoint to connect to.
# CLI flag: -<prefix>.s3.endpoint
[endpoint: <string> | default = ""]

# AWS region to use.
# CLI flag: -<prefix>.s3.region
[region: <string> | default = ""]

# AWS Access Key ID
# CLI flag: -<prefix>.s3.access-key-id
[access_key_id: <string> | default = ""]

# AWS Secret Access Key
# CLI flag: -<prefix>.s3.secret-access-key
[secret_access_key: <string> | default = ""]

# AWS Session Token
# CLI flag: -<prefix>.s3.session-token
[session_token: <string> | default = ""]

# Disable https on s3 connection. This does not affect TLS certificate
# verification for HTTPS connections; use s3.http.insecure-skip-verify (or
# s3.http.ca-file) for that.
# CLI flag: -<prefix>.s3.insecure
[insecure: <boolean> | default = false]

# Delimiter used to replace the default delimiter ':' in chunk IDs when storing
# chunks. This is mainly intended when you run a MinIO instance on a Windows
# machine. You must not change this value during operations, otherwise you may
# not be able to read existing chunks from object storage.
# CLI flag: -<prefix>.s3.chunk-delimiter
[chunk_delimiter: <string> | default = ""]

http_config:
  # Timeout specifies a time limit for requests made by s3 Client.
  # CLI flag: -<prefix>.s3.http.timeout
  [timeout: <duration> | default = 0s]

  # The maximum amount of time an idle connection will be held open.
  # CLI flag: -<prefix>.s3.http.idle-conn-timeout
  [idle_conn_timeout: <duration> | default = 1m30s]

  # If non-zero, specifies the amount of time to wait for a server's response
  # headers after fully writing the request.
  # CLI flag: -<prefix>.s3.http.response-header-timeout
  [response_header_timeout: <duration> | default = 0s]

  # Set to true to skip verifying the certificate chain and hostname.
  # CLI flag: -<prefix>.s3.http.insecure-skip-verify
  [insecure_skip_verify: <boolean> | default = false]

  # Path to the trusted CA file that signed the SSL certificate of the S3
  # endpoint.
  # CLI flag: -<prefix>.s3.http.ca-file
  [ca_file: <string> | default = ""]

# The signature version to use for authenticating against S3. Supported values
# are: v4.
# CLI flag: -<prefix>.s3.signature-version
[signature_version: <string> | default = "v4"]

# The S3 storage class which objects will use. Supported values are: GLACIER,
# DEEP_ARCHIVE, GLACIER_IR, INTELLIGENT_TIERING, ONEZONE_IA, OUTPOSTS,
# REDUCED_REDUNDANCY, STANDARD, STANDARD_IA.
# CLI flag: -<prefix>.s3.storage-class
[storage_class: <string> | default = "STANDARD"]

sse:
  # Enable AWS Server Side Encryption. Supported values: SSE-KMS, SSE-S3.
  # CLI flag: -<prefix>.s3.sse.type
  [type: <string> | default = ""]

  # KMS Key ID used to encrypt objects in S3
  # CLI flag: -<prefix>.s3.sse.kms-key-id
  [kms_key_id: <string> | default = ""]

  # KMS Encryption Context used for object encryption. It expects JSON formatted
  # string.
  # CLI flag: -<prefix>.s3.sse.kms-encryption-context
  [kms_encryption_context: <string> | default = ""]

# Configures back off when S3 get Object.
backoff_config:
  # Minimum backoff time when s3 get Object
  # CLI flag: -<prefix>.s3.min-backoff
  [min_period: <duration> | default = 100ms]

  # Maximum backoff time when s3 get Object
  # CLI flag: -<prefix>.s3.max-backoff
  [max_period: <duration> | default = 3s]

  # Maximum number of times to retry for s3 GetObject or ObjectExists
  # CLI flag: -<prefix>.s3.max-retries
  [max_retries: <int> | default = 5]

# Disable forcing S3 dualstack endpoint usage.
# CLI flag: -<prefix>.s3.disable-dualstack
[disable_dualstack: <boolean> | default = false]
```

### schema_config

Configures the chunk index schema and where it is stored.

```yaml
[configs: <list of period_configs>]
```

### server

Configures the `server` of the launched module(s).

```yaml
# HTTP server listen network, default tcp
# CLI flag: -server.http-listen-network
[http_listen_network: <string> | default = "tcp"]

# HTTP server listen address.
# CLI flag: -server.http-listen-address
[http_listen_address: <string> | default = ""]

# HTTP server listen port.
# CLI flag: -server.http-listen-port
[http_listen_port: <int> | default = 3100]

# Maximum number of simultaneous http connections, <=0 to disable
# CLI flag: -server.http-conn-limit
[http_listen_conn_limit: <int> | default = 0]

# gRPC server listen network
# CLI flag: -server.grpc-listen-network
[grpc_listen_network: <string> | default = "tcp"]

# gRPC server listen address.
# CLI flag: -server.grpc-listen-address
[grpc_listen_address: <string> | default = ""]

# gRPC server listen port.
# CLI flag: -server.grpc-listen-port
[grpc_listen_port: <int> | default = 9095]

# Maximum number of simultaneous grpc connections, <=0 to disable
# CLI flag: -server.grpc-conn-limit
[grpc_listen_conn_limit: <int> | default = 0]

# If true, the max streams by connection gauge will be collected.
# CLI flag: -server.grpc-collect-max-streams-by-conn
[grpc_collect_max_streams_by_conn: <boolean> | default = true]

# Enables PROXY protocol.
# CLI flag: -server.proxy-protocol-enabled
[proxy_protocol_enabled: <boolean> | default = false]

# Comma-separated list of cipher suites to use. If blank, the default Go cipher
# suites is used.
# CLI flag: -server.tls-cipher-suites
[tls_cipher_suites: <string> | default = ""]

# Minimum TLS version to use. Allowed values: VersionTLS10, VersionTLS11,
# VersionTLS12, VersionTLS13. If blank, the Go TLS minimum version is used.
# CLI flag: -server.tls-min-version
[tls_min_version: <string> | default = ""]

# Comma-separated list of TLS curve/group preferences for key exchange. Allowed
# values: CurveP256, CurveP384, CurveP521, X25519, X25519MLKEM768,
# SecP256r1MLKEM768, SecP384r1MLKEM1024. If blank, the default Go curve
# preferences are used.
# CLI flag: -server.tls-curve-preferences
[tls_curve_preferences: <string> | default = ""]

http_tls_config:
  # Server TLS certificate. This configuration parameter is YAML only.
  [cert: <string> | default = ""]

  # Server TLS key. This configuration parameter is YAML only.
  [key: <string> | default = ""]

  # Root certificate authority used to verify client certificates. This
  # configuration parameter is YAML only.
  [client_ca: <string> | default = ""]

  # HTTP server cert path.
  # CLI flag: -server.http-tls-cert-path
  [cert_file: <string> | default = ""]

  # HTTP server key path.
  # CLI flag: -server.http-tls-key-path
  [key_file: <string> | default = ""]

  # HTTP TLS Client Auth type.
  # CLI flag: -server.http-tls-client-auth
  [client_auth_type: <string> | default = ""]

  # HTTP TLS Client CA path.
  # CLI flag: -server.http-tls-ca-path
  [client_ca_file: <string> | default = ""]

grpc_tls_config:
  # Server TLS certificate. This configuration parameter is YAML only.
  [cert: <string> | default = ""]

  # Server TLS key. This configuration parameter is YAML only.
  [key: <string> | default = ""]

  # Root certificate authority used to verify client certificates. This
  # configuration parameter is YAML only.
  [client_ca: <string> | default = ""]

  # GRPC TLS server cert path.
  # CLI flag: -server.grpc-tls-cert-path
  [cert_file: <string> | default = ""]

  # GRPC TLS server key path.
  # CLI flag: -server.grpc-tls-key-path
  [key_file: <string> | default = ""]

  # GRPC TLS Client Auth type.
  # CLI flag: -server.grpc-tls-client-auth
  [client_auth_type: <string> | default = ""]

  # GRPC TLS Client CA path.
  # CLI flag: -server.grpc-tls-ca-path
  [client_ca_file: <string> | default = ""]

# Register the intrumentation handlers (/metrics etc).
# CLI flag: -server.register-instrumentation
[register_instrumentation: <boolean> | default = true]

# If set to true, gRPC statuses will be reported in instrumentation labels with
# their string representations. Otherwise, they will be reported as "error".
# CLI flag: -server.report-grpc-codes-in-instrumentation-label-enabled
[report_grpc_codes_in_instrumentation_label_enabled: <boolean> | default = false]

# Timeout for graceful shutdowns
# CLI flag: -server.graceful-shutdown-timeout
[graceful_shutdown_timeout: <duration> | default = 30s]

# Read timeout for entire HTTP request, including headers and body.
# CLI flag: -server.http-read-timeout
[http_server_read_timeout: <duration> | default = 30s]

# Read timeout for HTTP request headers. If set to 0, value of
# -server.http-read-timeout is used.
# CLI flag: -server.http-read-header-timeout
[http_server_read_header_timeout: <duration> | default = 0s]

# Write timeout for HTTP server
# CLI flag: -server.http-write-timeout
[http_server_write_timeout: <duration> | default = 30s]

# Idle timeout for HTTP server
# CLI flag: -server.http-idle-timeout
[http_server_idle_timeout: <duration> | default = 2m]

# Log closed connections that did not receive any response, most likely because
# client didn't send any request within timeout.
# CLI flag: -server.http-log-closed-connections-without-response-enabled
[http_log_closed_connections_without_response_enabled: <boolean> | default = false]

# Limit on the size of a gRPC message this server can receive (bytes).
# CLI flag: -server.grpc-max-recv-msg-size-bytes
[grpc_server_max_recv_msg_size: <int> | default = 4194304]

# Limit on the size of a gRPC message this server can send (bytes).
# CLI flag: -server.grpc-max-send-msg-size-bytes
[grpc_server_max_send_msg_size: <int> | default = 4194304]

# Limit on the number of concurrent streams for gRPC calls per client connection
# (0 = unlimited)
# CLI flag: -server.grpc-max-concurrent-streams
[grpc_server_max_concurrent_streams: <int> | default = 100]

# The duration after which an idle connection should be closed. Default:
# infinity
# CLI flag: -server.grpc.keepalive.max-connection-idle
[grpc_server_max_connection_idle: <duration> | default = 2562047h47m16.854775807s]

# The duration for the maximum amount of time a connection may exist before it
# will be closed. Default: infinity
# CLI flag: -server.grpc.keepalive.max-connection-age
[grpc_server_max_connection_age: <duration> | default = 2562047h47m16.854775807s]

# An additive period after max-connection-age after which the connection will be
# forcibly closed. Default: infinity
# CLI flag: -server.grpc.keepalive.max-connection-age-grace
[grpc_server_max_connection_age_grace: <duration> | default = 2562047h47m16.854775807s]

# Duration after which a keepalive probe is sent in case of no activity over the
# connection., Default: 2h
# CLI flag: -server.grpc.keepalive.time
[grpc_server_keepalive_time: <duration> | default = 2h]

# After having pinged for keepalive check, the duration after which an idle
# connection should be closed, Default: 20s
# CLI flag: -server.grpc.keepalive.timeout
[grpc_server_keepalive_timeout: <duration> | default = 20s]

# Minimum amount of time a client should wait before sending a keepalive ping.
# If client sends keepalive ping more often, server will send GOAWAY and close
# the connection.
# CLI flag: -server.grpc.keepalive.min-time-between-pings
[grpc_server_min_time_between_pings: <duration> | default = 10s]

# If true, server allows keepalive pings even when there are no active
# streams(RPCs). If false, and client sends ping when there are no active
# streams, server will send GOAWAY and close the connection.
# CLI flag: -server.grpc.keepalive.ping-without-stream-allowed
[grpc_server_ping_without_stream_allowed: <boolean> | default = true]

# If non-zero, configures the amount of GRPC server workers used to serve the
# requests.
# CLI flag: -server.grpc.num-workers
[grpc_server_num_workers: <int> | default = 0]

# If true, the request_message_bytes, response_message_bytes, and
# inflight_requests metrics will be tracked. Enabling this option prevents the
# use of memory pools for parsing gRPC request bodies and may lead to more
# memory allocations.
# CLI flag: -server.grpc.stats-tracking-enabled
[grpc_server_stats_tracking_enabled: <boolean> | default = true]

# Deprecated option, has no effect and will be removed in a future version.
# CLI flag: -server.grpc.recv-buffer-pools-enabled
[grpc_server_recv_buffer_pools_enabled: <boolean> | default = false]

# Size of the read buffer for each gRPC connection (bytes). A smaller buffer may
# reduce memory usage but may lead to more system calls.
# CLI flag: -server.grpc.read-buffer-size-bytes
[grpc_server_read_buffer_size: <int> | default = 32768]

# Size of the write buffer for each gRPC connection (bytes). A smaller buffer
# may reduce memory usage but may lead to more system calls.
# CLI flag: -server.grpc.write-buffer-size-bytes
[grpc_server_write_buffer_size: <int> | default = 32768]

# Output log messages in the given format. Valid formats: [logfmt, json]
# CLI flag: -log.format
[log_format: <string> | default = "logfmt"]

# Only log messages with the given severity or above. Valid levels: [debug,
# info, warn, error]
# CLI flag: -log.level
[log_level: <string> | default = "info"]

# Optionally log the source IPs.
# CLI flag: -server.log-source-ips-enabled
[log_source_ips_enabled: <boolean> | default = false]

# Log all source IPs instead of only the originating one. Only used if
# server.log-source-ips-enabled is true
# CLI flag: -server.log-source-ips-full
[log_source_ips_full: <boolean> | default = false]

# Header field storing the source IPs. Only used if
# server.log-source-ips-enabled is true. If not set the default Forwarded,
# X-Real-IP and X-Forwarded-For headers are used
# CLI flag: -server.log-source-ips-header
[log_source_ips_header: <string> | default = ""]

# Regex for matching the source IPs. Only used if server.log-source-ips-enabled
# is true. If not set the default Forwarded, X-Real-IP and X-Forwarded-For
# headers are used
# CLI flag: -server.log-source-ips-regex
[log_source_ips_regex: <string> | default = ""]

# Optionally log request headers.
# CLI flag: -server.log-request-headers
[log_request_headers: <boolean> | default = false]

# Optionally log requests at info level instead of debug level. Applies to
# request headers as well if server.log-request-headers is enabled.
# CLI flag: -server.log-request-at-info-level-enabled
[log_request_at_info_level_enabled: <boolean> | default = false]

# Comma separated list of headers to exclude from loggin. Only used if
# server.log-request-headers is true.
# CLI flag: -server.log-request-headers-exclude-list
[log_request_exclude_headers_list: <string> | default = ""]

# Optionally add request headers to tracing spans.
# CLI flag: -server.trace-request-headers
[trace_request_headers: <boolean> | default = false]

# Comma separated list of headers to exclude from tracing spans. Only used if
# server.trace-request-headers is true. The following headers are always
# excluded: Authorization, Cookie, X-Access-Token, X-Csrf-Token, X-Grafana-Id.
# CLI flag: -server.trace-request-headers-exclude-list
[trace_request_exclude_headers_list: <string> | default = ""]

# Base path to serve all API routes from (e.g. /v1/)
# CLI flag: -server.path-prefix
[http_path_prefix: <string> | default = ""]

cluster_validation:
  # Primary cluster validation label.
  # CLI flag: -server.cluster-validation.label
  [label: <string> | default = ""]

  # Comma-separated list of additional cluster validation labels that the server
  # will accept from incoming requests.
  # CLI flag: -server.cluster-validation.additional-labels
  [additional_labels: <string> | default = ""]

  grpc:
    # When enabled, cluster label validation is executed: configured cluster
    # validation label is compared with the cluster validation label received
    # through the requests.
    # CLI flag: -server.cluster-validation.grpc.enabled
    [enabled: <boolean> | default = false]

    # When enabled, soft cluster label validation is executed. Can be enabled
    # only together with server.cluster-validation.grpc.enabled
    # CLI flag: -server.cluster-validation.grpc.soft-validation
    [soft_validation: <boolean> | default = false]

  http:
    # When enabled, cluster label validation is executed: configured cluster
    # validation label is compared with the cluster validation label received
    # through the requests.
    # CLI flag: -server.cluster-validation.http.enabled
    [enabled: <boolean> | default = false]

    # When enabled, soft cluster label validation is executed. Can be enabled
    # only together with server.cluster-validation.http.enabled
    # CLI flag: -server.cluster-validation.http.soft-validation
    [soft_validation: <boolean> | default = false]

    # Comma-separated list of url paths that are excluded from the cluster
    # validation check.
    # CLI flag: -server.cluster-validation.http.excluded-paths
    [excluded_paths: <string> | default = ""]

    # Comma-separated list of user agents that are excluded from the cluster
    # validation check.
    # CLI flag: -server.cluster-validation.http.excluded-user-agents
    [excluded_user_agents: <string> | default = ""]

# Creates new traces for each call rather than continuing the existing trace. A
# span link is used to allow navigation to the parent trace. Only works when
# using Open-Telemetry tracing.
# CLI flag: -server.create-new-traces
[create_new_traces: <boolean> | default = false]

# Specifies if this handler should emit start timestamps for counters,
# histograms and summaries over OpenMetrics 1.0, which are defined as extra
# series with the same name and "_created" suffix. Only applies if
# -server.register-instrumentation is set to true.
# CLI flag: -server.enable-open-metrics-text-created-samples
[enable_open_metrics_text_created_samples: <boolean> | default = false]
```

### storage_config

The `storage_config` block configures one of many possible stores for both the index and chunks. Which configuration to be picked should be defined in schema_config block.

```yaml
# The alibabacloud_storage_config block configures the connection to Alibaba
# Cloud Storage object storage backend.
[alibabacloud: <alibabacloud_storage_config>]

# The s3_storage_config block configures the connection to Amazon S3 object
# storage backend.
[aws: <s3_storage_config>]

# The azure_storage_config block configures the connection to Azure object
# storage backend.
[azure: <azure_storage_config>]

# The bos_storage_config block configures the connection to Baidu Object Storage
# (BOS) object storage backend.
[bos: <bos_storage_config>]

# Configures storing chunks in GCS. Required fields only required when gcs is
# defined in config.
[gcs: <gcs_storage_config>]

# Configures storing the chunks on the local file system. Required fields only
# required when filesystem is present in the configuration.
[filesystem: <local_storage_config>]

# The swift_storage_config block configures the connection to OpenStack Object
# Storage (Swift) object storage backend.
[swift: <swift_storage_config>]

hedging:
  # If set to a non-zero value a second request will be issued at the provided
  # duration. Default is 0 (disabled)
  # CLI flag: -store.hedge-requests-at
  [at: <duration> | default = 0s]

  # The maximum of hedge requests allowed.
  # CLI flag: -store.hedge-requests-up-to
  [up_to: <int> | default = 2]

  # The maximum of hedge requests allowed per seconds.
  # CLI flag: -store.hedge-max-per-second
  [max_per_second: <int> | default = 5]

# Configures additional object stores for a given storage provider.
# Supported stores: aws, azure, bos, filesystem, gcs, swift.
# Example:
# ```yaml
#     storage_config:
#       named_stores:
#         aws:
#           store-1:
#             endpoint: s3://foo-bucket
#             region: us-west1
# ```
# Named store from this example can be used by setting object_store to store-1
# in period_config.
[named_stores: <named_stores_config>]

# The cos_storage_config block configures the connection to IBM Cloud Object
# Storage (COS) backend.
[cos: <cos_storage_config>]

congestion_control:
  # Use storage congestion control (default: disabled).
  # CLI flag: -store.congestion-control.enabled
  [enabled: <boolean> | default = false]

  controller:
    # Congestion control strategy to use (default: none, options: 'aimd').
    # CLI flag: -store.congestion-control.strategy
    [strategy: <string> | default = ""]

    aimd:
      # AIMD starting throughput window size: how many requests can be sent per
      # second (default: 2000).
      # CLI flag: -store.congestion-control.strategy.aimd.start
      [start: <int> | default = 2000]

      # AIMD maximum throughput window size: upper limit of requests sent per
      # second (default: 10000).
      # CLI flag: -store.congestion-control.strategy.aimd.upper-bound
      [upper_bound: <int> | default = 10000]

      # AIMD backoff factor when upstream service is throttled to decrease
      # number of requests sent per second (default: 0.5).
      # CLI flag: -store.congestion-control.strategy.aimd.backoff-factor
      [backoff_factor: <float> | default = 0.5]

  retry:
    # Congestion control retry strategy to use (default: none, options:
    # 'limited').
    # CLI flag: -store.congestion-control.retry.strategy
    [strategy: <string> | default = ""]

    # Maximum number of retries allowed.
    # CLI flag: -store.congestion-control.retry.strategy.limited.limit
    [limit: <int> | default = 2]

  hedging:
    config:
      [at: <duration>]

      [up_to: <int>]

      [max_per_second: <int>]

    # Congestion control hedge strategy to use (default: none, options:
    # 'limited').
    # CLI flag: -store.congestion-control.hedge.strategy
    [strategy: <string> | default = ""]

# Experimental. Sets a constant prefix for all keys inserted into object
# storage. Example: loki/
# CLI flag: -store.object-prefix
[object_prefix: <string> | default = ""]

# Maximum number of parallel chunk reads.
# CLI flag: -store.max-parallel-get-chunk
[max_parallel_get_chunk: <int> | default = 150]

# Enables the use of thanos-io/objstore clients for connecting to object
# storage. When set to true, the configuration inside
# `storage_config.object_store` or `common.storage.object_store` block takes
# effect.
# CLI flag: -use-thanos-objstore
[use_thanos_objstore: <boolean> | default = true]

object_store:
  # The thanos_object_store_config block configures the connection to object
  # storage backend using thanos-io/objstore clients. This will become the
  # default way of configuring object store clients in future releases.
  # Currently this is opt-in and takes effect only when `-use-thanos-objstore`
  # is set to true.
  # The CLI flags prefix for this block configuration is: object-store
  [<thanos_object_store_config>]

  named_stores:
    [azure: <map of string to NamedAzureStorageConfig>]

    [filesystem: <map of string to NamedFilesystemStorageConfig>]

    [gcs: <map of string to NamedGCSStorageConfig>]

    [s3: <map of string to NamedS3StorageConfig>]

    [swift: <map of string to NamedSwiftStorageConfig>]

# The maximum number of chunks to fetch per batch.
# CLI flag: -store.max-chunk-batch-size
[max_chunk_batch_size: <int> | default = 50]

# Configures storing index in an Object Store
# (GCS/S3/Azure/Swift/COS/Filesystem) in a prometheus TSDB-like format. Required
# fields only required when TSDB is defined in config.
tsdb_shipper:
  # Directory where ingesters would write index files which would then be
  # uploaded by shipper to configured storage
  # CLI flag: -tsdb.shipper.active-index-directory
  [active_index_directory: <string> | default = ""]

  # Cache location for restoring index files from storage for queries
  # CLI flag: -tsdb.shipper.cache-location
  [cache_location: <string> | default = ""]

  # TTL for index files restored in cache for queries
  # CLI flag: -tsdb.shipper.cache-ttl
  [cache_ttl: <duration> | default = 24h]

  # Resync downloaded files with the storage
  # CLI flag: -tsdb.shipper.resync-interval
  [resync_interval: <duration> | default = 5m]

  # Number of days of common index to be kept downloaded for queries. For per
  # tenant index query readiness, use limits overrides config.
  # CLI flag: -tsdb.shipper.query-ready-num-days
  [query_ready_num_days: <int> | default = 0]

  # Timeout for downloading a table's initial set of index files from object
  # storage when serving a query. Raise this for tenants with large indexes when
  # slow object-storage responses cause downloads to hit the deadline; lower it
  # to fail queries faster when storage is degraded.
  # CLI flag: -tsdb.shipper.download-timeout
  [download_timeout: <duration> | default = 1m]

  # Experimental. Implementation used to read TSDB index files off disk.
  # Supported values: mmap (memory-map the file, the historical default) or
  # stream (experimental, not yet fully implemented).
  # CLI flag: -tsdb.shipper.index-reader-mode
  [index_reader_mode: <string> | default = "mmap"]

  index_gateway_client:
    # The grpc_client block configures the gRPC client used to communicate
    # between a client and server component in Loki.
    # The CLI flags prefix for this block configuration is:
    # tsdb.shipper.index-gateway-client.grpc
    [grpc_client_config: <grpc_client>]

    # Hostname or IP of the Index Gateway gRPC server running in simple mode.
    # Can also be prefixed with dns+, dnssrv+, or dnssrvnoa+ to resolve a DNS A
    # record with multiple IP's, a DNS SRV record with a followup A record
    # lookup, or a DNS SRV record without a followup A record lookup,
    # respectively.
    # CLI flag: -tsdb.shipper.index-gateway-client.server-address
    [server_address: <string> | default = ""]

    # Whether requests sent to the gateway should be logged or not.
    # CLI flag: -tsdb.shipper.index-gateway-client.log-gateway-requests
    [log_gateway_requests: <boolean> | default = false]

    # Experimental: Defines buckets for time-based sharding. Time based sharding
    # only takes affect when index gateways run in simple mode. To enable client
    # side time-based sharding of queries across index gateway instances set at
    # least one bucket in the format of a string representation of a
    # time.Duration, e.g. ['168h', '336h', '504h']
    # CLI flag: -tsdb.shipper.index-gateway-client.time-based-sharding-buckets
    [time_based_sharding_buckets: <list of strings> | default = []]

    # Minimum number of index gateway instances included in the shuffle shard,
    # regardless of the max-capacity setting. A value of 0 disables the minimum.
    # Only applies to simple mode.
    # CLI flag: -tsdb.shipper.index-gateway-client.min-shuffle-shard-size
    [min_shuffle_shard_size: <int> | default = 3]

    # Experimental: Maximum number of requests this index gateway client may
    # have in flight at once. Requests arriving when the limit is reached are
    # rejected immediately with an HTTP 503 status instead of waiting, which
    # bounds the resources this process commits to an index gateway that is
    # slow, saturated, or unreachable. The limit applies per client: one client
    # is built per schema period config, so the process-wide number of in-flight
    # requests can reach this value multiplied by the number of clients. 0
    # disables the limit.
    # CLI flag: -tsdb.shipper.index-gateway-client.max-in-flight-requests
    [max_in_flight_requests: <int> | default = 0]

    # Experimental: Maximum number of other index gateway instances a failed
    # request is retried against. Each instance is tried at most once, so a
    # request makes at most this many retries plus one attempt in total.
    # Bounding this stops a single request from walking every replica, which can
    # otherwise block the calling goroutine for the sum of every replica's
    # timeout. -1 preserves the existing behavior: up to 2 retries for GetShards
    # and all candidate instances for other requests. 0 disables retries.
    # CLI flag: -tsdb.shipper.index-gateway-client.max-retries
    [max_retries: <int> | default = -1]

  # Experimental. Number of idle file handles the stream index reader keeps open
  # per index file. Only applies when -shipper.index-reader-mode=stream. Set to
  # 0 to disable pooling.
  # CLI flag: -tsdb.shipper.streaming-index-max-idle-file-handles
  [streaming_index_max_idle_file_handles: <int> | default = 16]

  # Experimental. Caches expanded postings for downloaded per-tenant TSDB index
  # files.
  # The CLI flags prefix for this block configuration is:
  # tsdb.shipper.postings-cache
  [postings_cache: <cache_config>]

  [ingestername: <string> | default = ""]

  [mode: <string> | default = ""]

  [ingesterdbretainperiod: <duration>]

# Deprecated: Configures the bloom shipper component, which contains the store
# abstraction to fetch bloom filters from and put them to object storage.
bloom_shipper:
  # Working directory to store downloaded bloom blocks. Supports multiple
  # directories, separated by comma.
  # CLI flag: -bloom.shipper.working-directory
  [working_directory: <string> | default = "/data/blooms"]

  # Maximum size of bloom pages that should be queried. Larger pages than this
  # limit are skipped when querying blooms to limit memory usage.
  # CLI flag: -bloom.max-query-page-size
  [max_query_page_size: <int> | default = 64MiB]

  # The amount of maximum concurrent bloom blocks downloads. Usually set to 2x
  # number of CPU cores.
  # CLI flag: -bloom.download-parallelism
  [download_parallelism: <int> | default = 8]

  blocks_cache:
    # Cache for bloom blocks. Soft limit of the cache in bytes. Exceeding this
    # limit will trigger evictions of least recently used items in the
    # background.
    # CLI flag: -bloom.blocks-cache.soft-limit
    [soft_limit: <int> | default = 32GiB]

    # Cache for bloom blocks. Hard limit of the cache in bytes. Exceeding this
    # limit will block execution until soft limit is deceeded.
    # CLI flag: -bloom.blocks-cache.hard-limit
    [hard_limit: <int> | default = 64GiB]

    # Cache for bloom blocks. The time to live for items in the cache before
    # they get purged.
    # CLI flag: -bloom.blocks-cache.ttl
    [ttl: <duration> | default = 24h]

  # The cache_config block configures the cache backend for a specific Loki
  # component.
  # The CLI flags prefix for this block configuration is: bloom.metas-cache
  [metas_cache: <cache_config>]

  metas_lru_cache:
    # In-memory LRU cache for bloom metas. Whether embedded cache is enabled.
    # CLI flag: -bloom.metas-lru-cache.enabled
    [enabled: <boolean> | default = false]

    # In-memory LRU cache for bloom metas. Maximum memory size of the cache in
    # MB.
    # CLI flag: -bloom.metas-lru-cache.max-size-mb
    [max_size_mb: <int> | default = 100]

    # In-memory LRU cache for bloom metas. Maximum number of entries in the
    # cache.
    # CLI flag: -bloom.metas-lru-cache.max-size-items
    [max_size_items: <int> | default = 0]

    # In-memory LRU cache for bloom metas. The time to live for items in the
    # cache before they get purged.
    # CLI flag: -bloom.metas-lru-cache.ttl
    [ttl: <duration> | default = 1h]
```

### swift_storage_config

The `swift_storage_config` block configures the connection to OpenStack Object Storage (Swift) object storage backend. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `common.storage`
- `ruler.storage`

&nbsp;

```yaml
# OpenStack Swift authentication API version. 0 to autodetect.
# CLI flag: -<prefix>.swift.auth-version
[auth_version: <int> | default = 0]

# OpenStack Swift authentication URL
# CLI flag: -<prefix>.swift.auth-url
[auth_url: <string> | default = ""]

# Set this to true to use the internal OpenStack Swift endpoint URL
# CLI flag: -<prefix>.swift.internal
[internal: <boolean> | default = false]

# OpenStack Swift username.
# CLI flag: -<prefix>.swift.username
[username: <string> | default = ""]

# OpenStack Swift user's domain name.
# CLI flag: -<prefix>.swift.user-domain-name
[user_domain_name: <string> | default = ""]

# OpenStack Swift user's domain ID.
# CLI flag: -<prefix>.swift.user-domain-id
[user_domain_id: <string> | default = ""]

# OpenStack Swift user ID.
# CLI flag: -<prefix>.swift.user-id
[user_id: <string> | default = ""]

# OpenStack Swift API key.
# CLI flag: -<prefix>.swift.password
[password: <string> | default = ""]

# OpenStack Swift user's domain ID.
# CLI flag: -<prefix>.swift.domain-id
[domain_id: <string> | default = ""]

# OpenStack Swift user's domain name.
# CLI flag: -<prefix>.swift.domain-name
[domain_name: <string> | default = ""]

# OpenStack Swift project ID (v2,v3 auth only).
# CLI flag: -<prefix>.swift.project-id
[project_id: <string> | default = ""]

# OpenStack Swift project name (v2,v3 auth only).
# CLI flag: -<prefix>.swift.project-name
[project_name: <string> | default = ""]

# ID of the OpenStack Swift project's domain (v3 auth only), only needed if it
# differs the from user domain.
# CLI flag: -<prefix>.swift.project-domain-id
[project_domain_id: <string> | default = ""]

# Name of the OpenStack Swift project's domain (v3 auth only), only needed if it
# differs from the user domain.
# CLI flag: -<prefix>.swift.project-domain-name
[project_domain_name: <string> | default = ""]

# OpenStack Swift Region to use (v2,v3 auth only).
# CLI flag: -<prefix>.swift.region-name
[region_name: <string> | default = ""]

# Name of the OpenStack Swift container to put chunks in.
# CLI flag: -<prefix>.swift.container-name
[container_name: <string> | default = ""]

# Max retries on requests error.
# CLI flag: -<prefix>.swift.max-retries
[max_retries: <int> | default = 3]

# Time after which a connection attempt is aborted.
# CLI flag: -<prefix>.swift.connect-timeout
[connect_timeout: <duration> | default = 10s]

# Time after which an idle request is aborted. The timeout watchdog is reset
# each time some data is received, so the timeout triggers after X time no data
# is received on a request.
# CLI flag: -<prefix>.swift.request-timeout
[request_timeout: <duration> | default = 5s]

http:
  # Path to the CA certificates to validate server certificate against. If not
  # set, the host's root CA certificates are used.
  # CLI flag: -<prefix>.swift.http.tls-ca-path
  [tls_ca_path: <string> | default = ""]
```

### thanos_object_store_config

The `thanos_object_store_config` block configures the connection to object storage backend using thanos-io/objstore clients. This will become the default way of configuring object store clients in future releases.
Currently this is opt-in and takes effect only when `-use-thanos-objstore` is set to true. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `common.storage.object-store`
- `object-store`
- `ruler-storage`

&nbsp;

```yaml
s3:
  # The S3 bucket endpoint. It could be an AWS S3 endpoint listed at
  # https://docs.aws.amazon.com/general/latest/gr/s3.html or the address of an
  # S3-compatible service in hostname:port format.
  # CLI flag: -<prefix>.s3.endpoint
  [endpoint: <string> | default = ""]

  # S3 region. If unset, the client will issue a S3 GetBucketLocation API call
  # to autodetect it.
  # CLI flag: -<prefix>.s3.region
  [region: <string> | default = ""]

  # S3 bucket name
  # CLI flag: -<prefix>.s3.bucket-name
  [bucket_name: <string> | default = ""]

  # S3 secret access key
  # CLI flag: -<prefix>.s3.secret-access-key
  [secret_access_key: <string> | default = ""]

  # S3 access key ID
  # CLI flag: -<prefix>.s3.access-key-id
  [access_key_id: <string> | default = ""]

  # S3 session token
  # CLI flag: -<prefix>.s3.session-token
  [session_token: <string> | default = ""]

  # If enabled, use http:// for the S3 endpoint instead of https://. This could
  # be useful in local dev/test environments while using an S3-compatible
  # backend storage, like Minio.
  # CLI flag: -<prefix>.s3.insecure
  [insecure: <boolean> | default = false]

  # Use a specific version of the S3 list object API. Supported values are v1 or
  # v2. Default is unset.
  # CLI flag: -<prefix>.s3.list-objects-version
  [list_objects_version: <string> | default = ""]

  # Bucket lookup style type, used to access bucket in S3-compatible service.
  # Default is auto. Supported values are: auto, path, virtual-hosted.
  # CLI flag: -<prefix>.s3.bucket-lookup-type
  [bucket_lookup_type: <int> | default = auto]

  # When enabled, direct all AWS S3 requests to the dual-stack IPv4/IPv6
  # endpoint for the configured region.
  # CLI flag: -<prefix>.s3.dualstack-enabled
  [dualstack_enabled: <boolean> | default = true]

  # The S3 storage class to use, not set by default. Details can be found at
  # https://aws.amazon.com/s3/storage-classes/. Supported values are: STANDARD,
  # REDUCED_REDUNDANCY, GLACIER, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING,
  # DEEP_ARCHIVE, OUTPOSTS, GLACIER_IR, SNOW, EXPRESS_ONEZONE, FSX_OPENZFS,
  # FSX_ONTAP, AWS_BACKUP_WARM, AWS_BACKUP_LOW_COST_WARM
  # CLI flag: -<prefix>.s3.storage-class
  [storage_class: <string> | default = ""]

  # If enabled, it will use the default authentication methods of the AWS SDK
  # for go based on known environment variables and known AWS config files.
  # CLI flag: -<prefix>.s3.native-aws-auth-enabled
  [native_aws_auth_enabled: <boolean> | default = false]

  # The minimum file size in bytes used for multipart uploads. If 0, the value
  # is optimally computed for each object.
  # CLI flag: -<prefix>.s3.part-size
  [part_size: <int> | default = 0]

  # If enabled, a Content-MD5 header is sent with S3 Put Object requests.
  # Consumes more resources to compute the MD5, but may improve compatibility
  # with object storage services that do not support checksums.
  # CLI flag: -<prefix>.s3.send-content-md5
  [send_content_md5: <boolean> | default = false]

  # Accessing S3 resources using temporary, secure credentials provided by AWS
  # Security Token Service.
  # CLI flag: -<prefix>.s3.sts-endpoint
  [sts_endpoint: <string> | default = ""]

  # The maximum number of retries for S3 requests that are retryable. Default is
  # 10, set this to 1 to disable retries.
  # CLI flag: -<prefix>.s3.max-retries
  [max_retries: <int> | default = 10]

  sse:
    # Enable AWS Server Side Encryption. Supported values: SSE-KMS, SSE-S3.
    # CLI flag: -<prefix>.s3.sse.type
    [type: <string> | default = ""]

    # KMS Key ID used to encrypt objects in S3
    # CLI flag: -<prefix>.s3.sse.kms-key-id
    [kms_key_id: <string> | default = ""]

    # KMS Encryption Context used for object encryption. It expects JSON
    # formatted string.
    # CLI flag: -<prefix>.s3.sse.kms-encryption-context
    [kms_encryption_context: <string> | default = ""]

  http:
    # The time an idle connection will remain idle before closing.
    # CLI flag: -<prefix>.s3.http.idle-conn-timeout
    [idle_conn_timeout: <duration> | default = 1m30s]

    # The amount of time the client will wait for a servers response headers.
    # CLI flag: -<prefix>.s3.http.response-header-timeout
    [response_header_timeout: <duration> | default = 2m]

    # If the client connects via HTTPS and this option is enabled, the client
    # will accept any certificate and hostname.
    # CLI flag: -<prefix>.s3.http.insecure-skip-verify
    [insecure_skip_verify: <boolean> | default = false]

    # Maximum time to wait for a TLS handshake. 0 means no limit.
    # CLI flag: -<prefix>.s3.tls-handshake-timeout
    [tls_handshake_timeout: <duration> | default = 10s]

    # The time to wait for a server's first response headers after fully writing
    # the request headers if the request has an Expect header. 0 to send the
    # request body immediately.
    # CLI flag: -<prefix>.s3.expect-continue-timeout
    [expect_continue_timeout: <duration> | default = 1s]

    # Maximum number of idle (keep-alive) connections across all hosts. 0 means
    # no limit.
    # CLI flag: -<prefix>.s3.max-idle-connections
    [max_idle_connections: <int> | default = 100]

    # Maximum number of idle (keep-alive) connections to keep per-host. If 0, a
    # built-in default value is used.
    # CLI flag: -<prefix>.s3.max-idle-connections-per-host
    [max_idle_connections_per_host: <int> | default = 100]

    # Maximum number of connections per host. 0 means no limit.
    # CLI flag: -<prefix>.s3.max-connections-per-host
    [max_connections_per_host: <int> | default = 0]

    # Path to the CA certificates to validate server certificate against. If not
    # set, the host's root CA certificates are used.
    # CLI flag: -<prefix>.s3.http.tls-ca-path
    [tls_ca_path: <string> | default = ""]

    # Path to the client certificate, which will be used for authenticating with
    # the server. Also requires the key path to be configured.
    # CLI flag: -<prefix>.s3.http.tls-cert-path
    [tls_cert_path: <string> | default = ""]

    # Path to the key for the client certificate. Also requires the client
    # certificate to be configured.
    # CLI flag: -<prefix>.s3.http.tls-key-path
    [tls_key_path: <string> | default = ""]

    # Override the expected name on the server certificate.
    # CLI flag: -<prefix>.s3.http.tls-server-name
    [tls_server_name: <string> | default = ""]

  trace:
    # When enabled, low-level S3 HTTP operation information is logged at the
    # debug level.
    # CLI flag: -<prefix>.s3.trace.enabled
    [enabled: <boolean> | default = false]

gcs:
  # GCS bucket name
  # CLI flag: -<prefix>.gcs.bucket-name
  [bucket_name: <string> | default = ""]

  # JSON either from a Google Developers Console client_credentials.json file,
  # or a Google Developers service account key. Needs to be valid JSON, not a
  # filesystem path. If empty, fallback to Google default logic:
  # 1. A JSON file whose path is specified by the GOOGLE_APPLICATION_CREDENTIALS
  # environment variable. For workload identity federation, refer to
  # https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation
  # on how to generate the JSON configuration file for on-prem/non-Google cloud
  # platforms.
  # 2. A JSON file in a location known to the gcloud command-line tool:
  # $HOME/.config/gcloud/application_default_credentials.json.
  # 3. On Google Compute Engine it fetches credentials from the metadata server.
  # CLI flag: -<prefix>.gcs.service-account
  [service_account: <string> | default = ""]

  # The maximum size of the buffer that GCS client for a single PUT request. 0
  # to disable buffering.
  # CLI flag: -<prefix>.gcs.chunk-buffer-size
  [chunk_buffer_size: <int> | default = 0]

  # The maximum number of retries for idempotent operations. Overrides the
  # default gcs storage client behavior if this value is greater than 0. Set
  # this to 1 to disable retries.
  # CLI flag: -<prefix>.gcs.max-retries
  [max_retries: <int> | default = 10]

azure:
  # Azure storage account name
  # CLI flag: -<prefix>.azure.account-name
  [account_name: <string> | default = ""]

  # Azure storage account key. If unset, Azure managed identities will be used
  # for authentication instead.
  # CLI flag: -<prefix>.azure.account-key
  [account_key: <string> | default = ""]

  # If `connection-string` is set, the value of `endpoint-suffix` will not be
  # used. Use this method over `account-key` if you need to authenticate via a
  # SAS token. Or if you use the Azurite emulator.
  # CLI flag: -<prefix>.azure.connection-string
  [connection_string: <string> | default = ""]

  # Azure storage container name
  # CLI flag: -<prefix>.azure.container-name
  [container_name: <string> | default = ""]

  # Azure storage endpoint suffix without schema. The account name will be
  # prefixed to this value to create the FQDN. If set to empty string, default
  # endpoint suffix is used.
  # CLI flag: -<prefix>.azure.endpoint-suffix
  [endpoint_suffix: <string> | default = ""]

  # Number of retries for recoverable errors
  # CLI flag: -<prefix>.azure.max-retries
  [max_retries: <int> | default = 20]

  # User assigned managed identity. If empty, then System assigned identity is
  # used.
  # CLI flag: -<prefix>.azure.user-assigned-id
  [user_assigned_id: <string> | default = ""]

  # Delimiter used to replace ':' in chunk IDs when storing chunks
  # CLI flag: -<prefix>.azure.chunk-delimiter
  [chunk_delimiter: <string> | default = "-"]

  http_config:
    # The time an idle connection will remain idle before closing.
    # CLI flag: -<prefix>.azure.http.idle-conn-timeout
    [idle_conn_timeout: <duration> | default = 1m30s]

    # The amount of time the client will wait for a servers response headers.
    # CLI flag: -<prefix>.azure.http.response-header-timeout
    [response_header_timeout: <duration> | default = 2m]

    # If the client connects via HTTPS and this option is enabled, the client
    # will accept any certificate and hostname.
    # CLI flag: -<prefix>.azure.http.insecure-skip-verify
    [insecure_skip_verify: <boolean> | default = false]

    # Maximum time to wait for a TLS handshake. 0 means no limit.
    # CLI flag: -<prefix>.azure.tls-handshake-timeout
    [tls_handshake_timeout: <duration> | default = 10s]

    # The time to wait for a server's first response headers after fully writing
    # the request headers if the request has an Expect header. 0 to send the
    # request body immediately.
    # CLI flag: -<prefix>.azure.expect-continue-timeout
    [expect_continue_timeout: <duration> | default = 1s]

    # Maximum number of idle (keep-alive) connections across all hosts. 0 means
    # no limit.
    # CLI flag: -<prefix>.azure.max-idle-connections
    [max_idle_connections: <int> | default = 100]

    # Maximum number of idle (keep-alive) connections to keep per-host. If 0, a
    # built-in default value is used.
    # CLI flag: -<prefix>.azure.max-idle-connections-per-host
    [max_idle_connections_per_host: <int> | default = 100]

    # Maximum number of connections per host. 0 means no limit.
    # CLI flag: -<prefix>.azure.max-connections-per-host
    [max_connections_per_host: <int> | default = 0]

    # Path to the CA certificates to validate server certificate against. If not
    # set, the host's root CA certificates are used.
    # CLI flag: -<prefix>.azure.http.tls-ca-path
    [tls_ca_path: <string> | default = ""]

    # Path to the client certificate, which will be used for authenticating with
    # the server. Also requires the key path to be configured.
    # CLI flag: -<prefix>.azure.http.tls-cert-path
    [tls_cert_path: <string> | default = ""]

    # Path to the key for the client certificate. Also requires the client
    # certificate to be configured.
    # CLI flag: -<prefix>.azure.http.tls-key-path
    [tls_key_path: <string> | default = ""]

    # Override the expected name on the server certificate.
    # CLI flag: -<prefix>.azure.http.tls-server-name
    [tls_server_name: <string> | default = ""]

swift:
  # OpenStack Swift application credential id
  # CLI flag: -<prefix>.swift.application-credential-id
  [application_credential_id: <string> | default = ""]

  # OpenStack Swift application credential name
  # CLI flag: -<prefix>.swift.application-credential-name
  [application_credential_name: <string> | default = ""]

  # OpenStack Swift application credential secret
  # CLI flag: -<prefix>.swift.application-credential-secret
  [application_credential_secret: <string> | default = ""]

  # OpenStack Swift authentication API version. 0 to autodetect.
  # CLI flag: -<prefix>.swift.auth-version
  [auth_version: <int> | default = 0]

  # OpenStack Swift authentication URL
  # CLI flag: -<prefix>.swift.auth-url
  [auth_url: <string> | default = ""]

  # OpenStack Swift username.
  # CLI flag: -<prefix>.swift.username
  [username: <string> | default = ""]

  # OpenStack Swift user's domain name.
  # CLI flag: -<prefix>.swift.user-domain-name
  [user_domain_name: <string> | default = ""]

  # OpenStack Swift user's domain ID.
  # CLI flag: -<prefix>.swift.user-domain-id
  [user_domain_id: <string> | default = ""]

  # OpenStack Swift user ID.
  # CLI flag: -<prefix>.swift.user-id
  [user_id: <string> | default = ""]

  # OpenStack Swift API key.
  # CLI flag: -<prefix>.swift.password
  [password: <string> | default = ""]

  # OpenStack Swift user's domain ID.
  # CLI flag: -<prefix>.swift.domain-id
  [domain_id: <string> | default = ""]

  # OpenStack Swift user's domain name.
  # CLI flag: -<prefix>.swift.domain-name
  [domain_name: <string> | default = ""]

  # OpenStack Swift project ID (v2,v3 auth only).
  # CLI flag: -<prefix>.swift.project-id
  [project_id: <string> | default = ""]

  # OpenStack Swift project name (v2,v3 auth only).
  # CLI flag: -<prefix>.swift.project-name
  [project_name: <string> | default = ""]

  # ID of the OpenStack Swift project's domain (v3 auth only), only needed if it
  # differs the from user domain.
  # CLI flag: -<prefix>.swift.project-domain-id
  [project_domain_id: <string> | default = ""]

  # Name of the OpenStack Swift project's domain (v3 auth only), only needed if
  # it differs from the user domain.
  # CLI flag: -<prefix>.swift.project-domain-name
  [project_domain_name: <string> | default = ""]

  # OpenStack Swift Region to use (v2,v3 auth only).
  # CLI flag: -<prefix>.swift.region-name
  [region_name: <string> | default = ""]

  # Name of the OpenStack Swift container to put chunks in.
  # CLI flag: -<prefix>.swift.container-name
  [container_name: <string> | default = ""]

  # Max retries on requests error.
  # CLI flag: -<prefix>.swift.max-retries
  [max_retries: <int> | default = 3]

  # Time after which a connection attempt is aborted.
  # CLI flag: -<prefix>.swift.connect-timeout
  [connect_timeout: <duration> | default = 10s]

  # Time after which an idle request is aborted. The timeout watchdog is reset
  # each time some data is received, so the timeout triggers after X time no
  # data is received on a request.
  # CLI flag: -<prefix>.swift.request-timeout
  [request_timeout: <duration> | default = 5s]

  http:
    # The time an idle connection will remain idle before closing.
    # CLI flag: -<prefix>.swift.http.idle-conn-timeout
    [idle_conn_timeout: <duration> | default = 1m30s]

    # The amount of time the client will wait for a servers response headers.
    # CLI flag: -<prefix>.swift.http.response-header-timeout
    [response_header_timeout: <duration> | default = 2m]

    # If the client connects via HTTPS and this option is enabled, the client
    # will accept any certificate and hostname.
    # CLI flag: -<prefix>.swift.http.insecure-skip-verify
    [insecure_skip_verify: <boolean> | default = false]

    # Maximum time to wait for a TLS handshake. 0 means no limit.
    # CLI flag: -<prefix>.swift.tls-handshake-timeout
    [tls_handshake_timeout: <duration> | default = 10s]

    # The time to wait for a server's first response headers after fully writing
    # the request headers if the request has an Expect header. 0 to send the
    # request body immediately.
    # CLI flag: -<prefix>.swift.expect-continue-timeout
    [expect_continue_timeout: <duration> | default = 1s]

    # Maximum number of idle (keep-alive) connections across all hosts. 0 means
    # no limit.
    # CLI flag: -<prefix>.swift.max-idle-connections
    [max_idle_connections: <int> | default = 100]

    # Maximum number of idle (keep-alive) connections to keep per-host. If 0, a
    # built-in default value is used.
    # CLI flag: -<prefix>.swift.max-idle-connections-per-host
    [max_idle_connections_per_host: <int> | default = 100]

    # Maximum number of connections per host. 0 means no limit.
    # CLI flag: -<prefix>.swift.max-connections-per-host
    [max_connections_per_host: <int> | default = 0]

    # Path to the CA certificates to validate server certificate against. If not
    # set, the host's root CA certificates are used.
    # CLI flag: -<prefix>.swift.http.tls-ca-path
    [tls_ca_path: <string> | default = ""]

    # Path to the client certificate, which will be used for authenticating with
    # the server. Also requires the key path to be configured.
    # CLI flag: -<prefix>.swift.http.tls-cert-path
    [tls_cert_path: <string> | default = ""]

    # Path to the key for the client certificate. Also requires the client
    # certificate to be configured.
    # CLI flag: -<prefix>.swift.http.tls-key-path
    [tls_key_path: <string> | default = ""]

    # Override the expected name on the server certificate.
    # CLI flag: -<prefix>.swift.http.tls-server-name
    [tls_server_name: <string> | default = ""]

filesystem:
  # Local filesystem storage directory.
  # CLI flag: -<prefix>.filesystem.dir
  [dir: <string> | default = ""]

alibaba:
  # Endpoint to connect to.
  # CLI flag: -<prefix>.oss.endpoint
  [endpoint: <string> | default = ""]

  # Name of OSS bucket.
  # CLI flag: -<prefix>.oss.bucketname
  [bucket: <string> | default = ""]

  # alibabacloud Access Key ID
  # CLI flag: -<prefix>.oss.access-key-id
  [access_key_id: <string> | default = ""]

  # alibabacloud Secret Access Key
  # CLI flag: -<prefix>.oss.access-key-secret
  [access_key_secret: <string> | default = ""]

bos:
  # Name of BOS bucket.
  # CLI flag: -<prefix>.bos.bucket
  [bucket: <string> | default = ""]

  # BOS endpoint to connect to.
  # CLI flag: -<prefix>.bos.endpoint
  [endpoint: <string> | default = ""]

  # Baidu Cloud Engine (BCE) Access Key ID.
  # CLI flag: -<prefix>.bos.access-key
  [access_key: <string> | default = ""]

  # Baidu Cloud Engine (BCE) Secret Access Key.
  # CLI flag: -<prefix>.bos.secret-key
  [secret_key: <string> | default = ""]

# Prefix for all objects stored in the backend storage. For simplicity, it may
# only contain digits, English alphabet letters and dashes.
# CLI flag: -<prefix>.storage-prefix
[storage_prefix: <string> | default = ""]
```

### tls_config

The TLS configuration. The supported CLI flags `<prefix>` used to reference this configuration block are:

- `bloom-build.builder.grpc`
- `bloom-gateway-client.grpc`
- `bloom.metas-cache.memcached`
- `common.storage.ring.etcd`
- `compactor.grpc-client`
- `compactor.ring.etcd`
- `dataobj.metadata-cache.memcached`
- `distributor.ring.etcd`
- `etcd`
- `frontend.grpc-client-config`
- `frontend.index-stats-results-cache.memcached`
- `frontend.instant-metric-results-cache.memcached`
- `frontend.label-results-cache.memcached`
- `frontend.memcached`
- `frontend.series-results-cache.memcached`
- `frontend.tail-tls-config`
- `frontend.volume-results-cache.memcached`
- `index-gateway.ring.etcd`
- `ingest-limits-frontend-client`
- `ingest-limits-frontend.etcd`
- `ingest-limits-frontend.limits-client`
- `ingest-limits.etcd`
- `ingester.client`
- `ingester.partition-ring.etcd`
- `memberlist`
- `pattern-ingester.client`
- `pattern-ingester.etcd`
- `querier.frontend-client`
- `querier.frontend-grpc-client`
- `querier.scheduler-grpc-client`
- `query-scheduler.grpc-client-config`
- `query-scheduler.ring.etcd`
- `reporting.tls-config`
- `ruler.alertmanager-client`
- `ruler.client`
- `ruler.evaluation.query-frontend`
- `ruler.ring.etcd`
- `store.chunks-cache-l2.memcached`
- `store.chunks-cache.memcached`
- `tsdb.shipper.index-gateway-client.grpc`
- `tsdb.shipper.postings-cache.memcached`

&nbsp;

```yaml
# Path to the client certificate, which will be used for authenticating with the
# server. Also requires the key path to be configured.
# CLI flag: -<prefix>.tls-cert-path
[tls_cert_path: <string> | default = ""]

# Path to the key for the client certificate. Also requires the client
# certificate to be configured.
# CLI flag: -<prefix>.tls-key-path
[tls_key_path: <string> | default = ""]

# Path to the CA certificates to validate server certificate against. If not
# set, the host's root CA certificates are used.
# CLI flag: -<prefix>.tls-ca-path
[tls_ca_path: <string> | default = ""]

# Override the expected name on the server certificate.
# CLI flag: -<prefix>.tls-server-name
[tls_server_name: <string> | default = ""]

# Skip validating server certificate.
# CLI flag: -<prefix>.tls-insecure-skip-verify
[tls_insecure_skip_verify: <boolean> | default = false]

# Override the default cipher suite list (separated by commas). Allowed values:
# 
# Secure Ciphers:
# - TLS_AES_128_GCM_SHA256
# - TLS_AES_256_GCM_SHA384
# - TLS_CHACHA20_POLY1305_SHA256
# - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
# - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
# - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
# - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
# - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
# - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
# - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
# - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
# - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
# - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
# 
# Insecure Ciphers:
# - TLS_RSA_WITH_RC4_128_SHA
# - TLS_RSA_WITH_3DES_EDE_CBC_SHA
# - TLS_RSA_WITH_AES_128_CBC_SHA
# - TLS_RSA_WITH_AES_256_CBC_SHA
# - TLS_RSA_WITH_AES_128_CBC_SHA256
# - TLS_RSA_WITH_AES_128_GCM_SHA256
# - TLS_RSA_WITH_AES_256_GCM_SHA384
# - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
# - TLS_ECDHE_RSA_WITH_RC4_128_SHA
# - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
# - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
# - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
# CLI flag: -<prefix>.tls-cipher-suites
[tls_cipher_suites: <string> | default = ""]

# Override the default minimum TLS version. Allowed values: VersionTLS10,
# VersionTLS11, VersionTLS12, VersionTLS13
# CLI flag: -<prefix>.tls-min-version
[tls_min_version: <string> | default = ""]
```

### tracing

Configuration for `tracing`.

```yaml
# Set to false to disable tracing.
# CLI flag: -tracing.enabled
[enabled: <boolean> | default = true]
```

<!-- vale Grafana.Spelling = YES -->

## Runtime Configuration file

Loki has a concept of "runtime config" file, which is simply a file that is reloaded while Loki is running. It is used by some Loki components to allow operator to change some aspects of Loki configuration without restarting it. File is specified by using `-runtime-config.file=<filename>` flag and reload period (which defaults to 10 seconds) can be changed by `-runtime-config.reload-period=<duration>` flag.

At the moment, two components use runtime configuration: limits and multi KV store.

Options for runtime configuration reload can also be configured via YAML:

```yaml
# Configuration file to periodically check and reload.
[file: <string>: default = empty]

# How often to check the file.
[period: <duration>: default 10s]
```

Example runtime configuration file:

```yaml
overrides:
  tenant1:
    ingestion_rate_mb: 10
    max_streams_per_user: 100000
    max_chunks_per_query: 100000
  tenant2:
    max_streams_per_user: 1000000
    max_chunks_per_query: 1000000

multi_kv_config:
    mirror-enabled: false
    primary: consul
```

## Accept out-of-order writes

Loki accepts log entries of a stream to arrive out of order by time.
How far into the past accepted out-of-order log entries may be is configurable with `max_chunk_age`.
The setting `max_chunk_age` defaults to 2 hours, but it is advised to not increase it.
Loki calculates the earliest time that out-of-order entries may have and be accepted with.

```yaml
time_of_most_recent_line - (max_chunk_age/2)
```
This means the allowed out-of-order window is half of the configured `max_chunk_age`.

Log entries with timestamps that are after this earliest time are accepted.
Log entries further back in time return a `too_far_behind` error.

For example, if `max_chunk_age` is 2 hours
and the stream `{foo="bar"}` has one entry at `8:00`,
the earliest accepted timestamp will be calculated as: 8:00 - (2h / 2) = `7:00`.

Loki will accept data for that stream as far back in time as `7:00`.

If another log line is written at `10:00`,
the earliest accepted timestamp becomes: 10:00 - (2h / 2) = `9:00`.

Loki will accept data for that stream as far back in time as `9:00`.

# index

---
headless: true
description: |
  A headless page sets the render and list build options to never, creating a bundle of page resources.
  These page resources are accessible to the `docs/shared` shortcode.
---

# otel

---
headless: true
description: Shared file for Loki OTEL content.
labels:
  products:
    - enterprise
    - oss
    - cloud
---

[//]: # 'This file documents configuring the OTEL collector to import logs to Loki'
[//]: #
[//]: # 'This shared file is included in these locations:'
[//]: # '/website/docs/grafana-cloud/send-data/logs/collect-logs-with-otel.md'
[//]: # '/loki/docs/loki/latest/send-data/otel/_index.md'
[//]: #
[//]: # 'If you make changes to this file, verify that the meaning and content are not changed in any place where the file is included.'
[//]: # 'Any links should be fully qualified and not relative: /docs/grafana/ instead of ../grafana/.'

Loki natively supports ingesting OpenTelemetry logs over HTTP.
For ingesting logs to Loki using the OpenTelemetry Collector, you must use the [`otlphttp` exporter](https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/otlphttpexporter).

{{< youtube id="snXhe1fDDa8" >}}

For more information about using OpenTelemetry with Grafana products, refer to the [Grafana OpenTelemetry documentation](https://grafana.com/docs/opentelemetry/).

## Loki configuration

When logs are ingested by Loki using an OpenTelemetry protocol (OTLP) ingestion endpoint, some of the data is stored as [Structured Metadata](../../get-started/labels/structured-metadata/).

You must set `allow_structured_metadata` to `true` within your Loki config file. Otherwise, Loki will reject the log payload as malformed.  Note that Structured Metadata is enabled by default in Loki 3.0 and later.

```yaml
limits_config:
  allow_structured_metadata: true
```

## Configure the OpenTelemetry Collector to write logs into Loki

You need to make the following changes to the [OpenTelemetry Collector config](https://opentelemetry.io/docs/collector/configuration/) to write logs to Loki on its OTLP ingestion endpoint.

```yaml
exporters:
  otlphttp:
    endpoint: http://<loki-addr>/otlp
```

And enable it in `service.pipelines`:

```yaml
service:
  pipelines:
    logs:
      receivers: [...]
      processors: [...]
      exporters: [..., otlphttp]
```

If you want to authenticate using basic auth, we recommend the [`basicauth` extension](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/basicauthextension).

```yaml
extensions:
  basicauth/otlp:
    client_auth:
      username: username
      password: password

exporters:
  otlphttp:
    auth:
      authenticator: basicauth/otlp
    endpoint: http://<loki-addr>/otlp

service:
  extensions: [basicauth/otlp]
  pipelines:
    logs:
      receivers: [...]
      processors: [...]
      exporters: [..., otlphttp]
```

## Format considerations

Since the OpenTelemetry protocol differs from the Loki storage model, here is how data in the OpenTelemetry format will be mapped by default to the Loki data model during ingestion, which can be changed as explained later:

- Index labels: Resource attributes map well to index labels in Loki, since both usually identify the source of the logs. The default list of Resource Attributes to store as Index labels can be configured using `default_resource_attributes_as_index_labels` under [distributor's otlp_config](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#distributor). By default, the following resource attributes will be stored as index labels, while the remaining attributes are stored as [Structured Metadata](../../get-started/labels/structured-metadata/) with each log entry:
  - cloud.availability_zone
  - cloud.region
  - container.name
  - deployment.environment.name
  - k8s.cluster.name
  - k8s.container.name
  - k8s.cronjob.name
  - k8s.daemonset.name
  - k8s.deployment.name
  - k8s.job.name
  - k8s.namespace.name
  - k8s.pod.name
  - k8s.replicaset.name
  - k8s.statefulset.name
  - service.instance.id
  - service.name
  - service.namespace

    {{< admonition type="note" >}}
    Because Loki has a default limit of 15 index labels, we recommend storing only select resource attributes as index labels. Although the default config selects more than 15 Resource Attributes, it should be fine since a few are mutually exclusive.
    {{< /admonition >}}

    {{< admonition type="tip" >}}
    For Grafana Cloud Logs, see the [current OpenTelemetry guidance](https://grafana.com/docs/grafana-cloud/send-data/otlp/otlp-format-considerations/#logs).
    {{< /admonition >}}

- Timestamp: One of `LogRecord.TimeUnixNano` or `LogRecord.ObservedTimestamp`, based on which one is set. If both are not set, the ingestion timestamp will be used.

- LogLine: `LogRecord.Body` holds the body of the log. However, since Loki only supports Log body in string format, we will stringify non-string values using the [AsString method from the OTel collector lib](https://github.com/open-telemetry/opentelemetry-collector/blob/ab3d6c5b64701e690aaa340b0a63f443ff22c1f0/pdata/pcommon/value.go#L353).

- [Structured Metadata](../../get-started/labels/structured-metadata/): Anything which can’t be stored in Index labels and LogLine would be stored as Structured Metadata. Here is a non-exhaustive list of what will be stored in Structured Metadata to give a sense of what it will hold:
  - Resource Attributes not stored as Index labels is replicated and stored with each log entry.
  - Everything under InstrumentationScope is replicated and stored with each log entry.
  - Everything under LogRecord except `LogRecord.Body`, `LogRecord.TimeUnixNano` and sometimes `LogRecord.ObservedTimestamp`.

The default list of resource attributes to store as labels can be configured using `default_resource_attributes_as_index_labels` under the [distributor's otlp_config](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#distributor). You can set global limits using [limits_config.otlp_config](/docs/loki/<LOKI_VERSION>/configure/#limits_config). If you are using Grafana Cloud, contact support to configure this setting.

{{< admonition type="caution" >}}
Because of the potential for high [cardinality](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/cardinality/), `k8s.pod.name` and `service.instance.id` are no longer recommended as default labels. But because removing these resource attributes from the default labels would be a breaking change for existing users, they have not yet been deprecated as default labels. If you are a new user of Grafana Loki, we recommend that you modify your Alloy or OpenTelemetry Collector configuration to convert `k8s.pod.name` and `service.instance.id` from index labels to structured metadata.
For sample configurations, refer to [Modify default labels](https://grafana.com/docs/loki/<LOKI_VERSION>/get-started/labels/modify-default-labels).
{{< /admonition >}}

Things to note before ingesting OpenTelemetry logs to Loki:

- Dots (.) are converted to underscores (_).

  Loki does not support `.` or any other special characters other than `_` in label names. The unsupported characters are replaced with an `_` while converting Attributes to Index Labels or Structured Metadata.
  Also, please note that while writing the queries, you must use the normalized format, i.e. use `_` instead of special characters while querying data using OTel Attributes.

  For example, `service.name` in OTLP would become `service_name` in Loki.

- Flattening of nested Attributes

  While converting Attributes in OTLP to Index labels or Structured Metadata, any nested attribute values are flattened out using `_` as a separator.
  It is done in a similar way as to how it is done in the [LogQL json parser](/docs/loki/<LOKI_VERSION>/query/log_queries/#json).

- Stringification of non-string Attribute values

  While converting Attribute values in OTLP to Index label values or Structured Metadata, any non-string values are converted to string using [AsString method from the OTel collector lib](https://github.com/open-telemetry/opentelemetry-collector/blob/ab3d6c5b64701e690aaa340b0a63f443ff22c1f0/pdata/pcommon/value.go#L353).

  ### Changing the default mapping of OTLP to Loki Format

Loki supports [per tenant](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config) OTLP config which lets you change the default mapping of OTLP to Loki format for each tenant.
It currently only supports changing the storage of Attributes. Here is what the config looks like:

```yaml
# OTLP log ingestion configurations
limits_config:
  otlp_config:
    # Configuration for Resource Attributes to store them as index labels or
    # Structured Metadata or drop them altogether
    resource_attributes:
      # Configure whether to ignore the default list of resource attributes set in
      # 'distributor.otlp.default_resource_attributes_as_index_labels' to be
      # stored as index labels and only use the given resource attributes config
      [ignore_defaults: <boolean>]
  
      [attributes_config: <list of attributes_configs>]
  
    # Configuration for Scope Attributes to store them as Structured Metadata or
    # drop them altogether
    [scope_attributes: <list of attributes_configs>]
  
    # Configuration for Log Attributes to store them as Structured Metadata or
    # drop them altogether
    [log_attributes: <list of attributes_configs>]
  
  attributes_config:
    # Configures action to take on matching Attributes. It allows one of
    # [structured_metadata, drop] for all Attribute types. It additionally allows
    # index_label action for Resource Attributes
    [action: <string> | default = ""]
  
    # List of attributes to configure how to store them or drop them altogether
    [attributes: <list of strings>]
  
    # Regex to choose attributes to configure how to store them or drop them
    # altogether
    [regex: <Regexp>]
```

{{< admonition type="note" >}}
If you are a Grafana Cloud customer, you can use the [config self-serve API](https://grafana.com/docs/grafana-cloud/send-data/logs/config-self-serve/#otlp-label-mappings) to configure your OTLP config.
{{< /admonition >}}

# promtail-deprecation

---
description: Deprecation notice for Promtail.
headless: true
labels:
  products:
    - enterprise
    - oss
---

[//]: # 'This file provides an admonition caution for the deprecation of Promtail.'

{{< admonition type="caution" >}}
Promtail has been deprecated and is in Long-Term Support (LTS) through February 28, 2026. Promtail will reach an End-of-Life (EOL) on March 2, 2026. You can find migration resources [here](https://grafana.com/docs/alloy/latest/set-up/migrate/from-promtail/).
{{< /admonition >}}

# troubleshoot-query

---
headless: true
description: |
  A headless page sets the render and list build options to never, creating a bundle of page resources.
  These page resources are accessible to the `docs/shared` shortcode.
---

[//]: # 'This file documents query error messages and troubleshooting'
[//]: #
[//]: # 'This shared file is included in these locations:'
[//]: # '/loki/docs/loki/latest/query/troubleshoot-query.md'
[//]: # '/loki/docs/loki/latest/operations/troubleshooting/troubleshoot-query.md'
[//]: #
[//]: # 'If you make changes to this file, verify that the meaning and content are not changed in any place where the file is included.'
[//]: # 'Any links should be fully qualified and not relative: /docs/grafana/ instead of ../grafana/.'

This guide helps you troubleshoot errors that occur when querying logs from Loki. When Loki rejects or fails query requests, it's typically due to query syntax errors, exceeding limits, timeout issues, or storage access problems.

Before you begin, ensure you have the following:

- Access to Grafana Loki logs and metrics
- Understanding of [LogQL query language](https://grafana.com/docs/loki/<LOKI_VERSION>/query/) basics
- Permissions to configure limits and settings if needed

## Monitoring query errors

Query errors can be observed using these Prometheus metrics:

- `loki_request_duration_seconds` - Query latency by route and status code
- `loki_logql_querystats_bytes_processed_per_seconds` - Bytes processed during queries
- `loki_logql_querystats_bytes_processed_total` - Total bytes processed by queries, broken down by `tenant`. This is the metric equivalent of the `total_bytes` field in the `caller=metrics.go` query log line and lets you track per-tenant query bytes without parsing logs.
- `loki_frontend_query_range_duration_seconds_bucket` - Frontend query latency

You can set up alerts on 4xx and 5xx status codes to detect query problems early. This can be helpful when tuning limits configurations.

## LogQL parse errors

Parse errors occur when the LogQL query syntax is invalid. Loki returns HTTP status code `400 Bad Request` for all parse errors.

### Error: Failed to parse the log query

**Error message:**

`failed to parse the log query`

Or with position details:

`parse error at line <line>, col <col>: <message>`

**Cause:**

The LogQL query contains syntax errors. This could be due to:

- Missing or mismatched brackets, quotes, or braces
- Invalid characters or operators
- Incorrect function syntax
- Invalid duration format

**Common examples:**

| Invalid Query | Error | Fix |
| -------------- | ------- | ----- |
| `{app="foo"` | Missing closing brace | `{app="foo"}` |
| `{app="foo"} \|= test` | Unquoted filter string | `{app="foo"} \|= "test"` |
| `rate({app="foo"}[5minutes])` | Invalid duration unit | `rate({app="foo"}[5m])` |
| `{app="foo"} \| json` | Missing pipe symbol before parser | `{app="foo"} \| json` |

**Resolution:**

Start with a simple stream selector: `{job="app"}`, then add filters and operations incrementally to identify syntax issues.

* **Check bracket matching** - Ensure all `{`, `}`, `(`, `)`, `[`, `]` are properly closed.
* **Verify string quoting** - All label values and filter strings must be quoted.
* **Use valid duration units** - Use `ns`, `us`, `ms`, `s`, `m`, `h`, `d`, `w`, `y` , for example, `5m` not `5minutes`.
* **Review operator syntax** - Ensure label matchers use proper operators (`=`, `!=`, `=~`, `!~`). Check the [LogQL documentation](https://grafana.com/docs/loki/<LOKI_VERSION>/query/) for correct operator usage.
* **Use Grafana Assistant** - If you are a Cloud Logs user, you can use Grafana Assistant to write or revise your query using natural language, for example, “What errors occurred for application foo in the last hour?”

**Properties:**

- Enforced by: Query Frontend/Querier
- Retryable: No (query must be fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: At least one equality matcher required

**Error message:**

`parse error : queries require at least one regexp or equality matcher that does not have an empty-compatible value. For instance, app=~".*" does not meet this requirement, but app=~".+" will`

**Cause:**

The query uses only negative matchers (`!=`, `!~`) or matchers that match empty strings (`=~".*"`), which would select all streams. This is prevented to protect against accidentally querying the entire database.

**Invalid examples:**

```logql
{foo!="bar"}
{app=~".*"}
{foo!~"bar|baz"}
```

**Valid examples:**

```logql
{foo="bar"}
{app=~".+"}
{app="baz", foo!="bar"}
```

**Resolution:**

* **Add at least one positive matcher** that selects specific streams.
* **Use `.+` instead of `.*`** in regex matchers to require at least one character.
* **Add additional label selectors** to narrow down the query scope.
* **Use Grafana Assistant** - If you are a Cloud Logs user, you can use Grafana Assistant to write or revise your query using natural language, for example, “Find logs containing ’foo' but not 'bar' or 'baz'.”

**Properties:**

- Enforced by: Query Frontend
- Retryable: No (query must be fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Only label matchers are supported

**Error message:**

`only label matchers are supported`

**Cause:**

The query was passed to an API that only accepts label matchers (like the series API), but included additional expressions like line filters or parsers.

**Resolution:**

* **Use only stream selectors** for APIs that don't support full LogQL:

   ```logql
   # Valid for series API
   {app="foo", env="prod"}
   
   # Invalid for series API
   {app="foo"} |= "error"
   ```

**Properties:**

- Enforced by: API handler
- Retryable: No (query must be fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Log queries not supported as instant query type

**Error message:**

`log queries are not supported as an instant query type, please change your query to a range query type`

**Cause:**

A [log query](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/) (one that returns log lines rather than metrics) was submitted to the instant query endpoint (`/loki/api/v1/query`). Log queries must use the range query endpoint.

**Resolution:**

* **Convert to a range query** Convert log queries to range queries with a time range. Range queries are the default in Grafana Explore.
* **Use the range query endpoint** `/loki/api/v1/query_range` for log queries.
* **Convert to a metric query** if you need to use instant queries:

   ```logql
   # This is a log query (returns logs)
   {app="foo"} |= "error"
   
   # This is a metric query (can be instant)
   count_over_time({app="foo"} |= "error"[5m])
   ```

* **Use Grafana Assistant** - If you are a Cloud Logs user, you can use Grafana Assistant to write or revise your query.

**Properties:**

- Enforced by: Query API
- Retryable: No (use correct endpoint or query type)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Invalid aggregation without unwrap

**Error message:**

`parse error : invalid aggregation sum_over_time without unwrap`

**Cause:**

Aggregation functions like `sum_over_time`, `avg_over_time`, `min_over_time`, `max_over_time` require an `unwrap` expression to extract a numeric value from log lines.

**Resolution:**

* **Add an unwrap expression** to extract the numeric label:

   ```logql
   # Invalid
   sum_over_time({app="foo"} | json [5m])
   
   # Valid - unwrap a numeric label
   sum_over_time({app="foo"} | json | unwrap duration [5m])
   ```

**Properties:**

- Enforced by: Query Parser
- Retryable: No (query must be fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

### Error: Invalid aggregation with unwrap

**Error message:**

`parse error : invalid aggregation count_over_time with unwrap`

**Cause:**

The `count_over_time` function doesn't use unwrapped values - it just counts log lines. Using it with `unwrap` is invalid.

**Resolution:**

* **Remove the unwrap expression** for count_over_time:

   ```logql
   # Invalid
   count_over_time({app="foo"} | json | unwrap duration [5m])
   
   # Valid
   count_over_time({app="foo"} | json [5m])
   ```

* **Use sum_over_time** if you want to sum unwrapped values.

**Properties:**

- Enforced by: Query Parser
- Retryable: No (query must be fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

## Query limit errors

These errors occur when queries exceed configured resource limits. They return HTTP status code `400 Bad Request`.

### Error: Maximum series reached

**Error message:**

`maximum number of series (<limit>) reached for a single query; consider reducing query cardinality by adding more specific stream selectors, reducing the time range, or aggregating results with functions like sum(), count() or topk()`

**Cause:**

The query matches more unique label combinations (series) than the configured limit allows. This protects against queries that would consume excessive memory.

**Default configuration:**

- `max_query_series`: 500 (default)

**Resolution:**

* **Add more specific stream selectors** to reduce cardinality:

   ```logql
   # Too broad
   {job="ingress-nginx"}
   
   # More specific
   {job="ingress-nginx", namespace="production", pod=~"ingress-nginx-.*"}
   ```

* **Reduce the time range** of the query.

* **Use label filters** to narrow down results: `{job="app"} |= "error"`

* **Use aggregation functions** to reduce cardinality:

   ```logql
   sum by (status) (rate({job="nginx"} | json [5m]))
   ```

* **Increase the limit** if resources allow:

   ```yaml
   limits_config:
     max_query_series: 1000  #default is 500
   ```

**Properties:**

- Enforced by: Query Frontend
- Retryable: No (query must be modified)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Cardinality issues

**Error message:**

`cardinality limit exceeded for {}; 100001 entries, more than limit of 100000`

**Cause:**

The query produces results with too many unique label combinations. This protects against queries that would generate excessive memory usage and slow performance.

**Default configuration:**

- `cardinality_limit`: 100000

**Resolution:**

* **Use more specific label selectors** to reduce the number of unique streams.
* **Apply aggregation functions** to reduce cardinality:

   ```logql
   sum by (status) (rate({job="nginx"}[5m]))
   ```

* **Use `by()` or `without()` clauses** to group results and reduce dimensions:

   ```logql
   sum by (status, method) (rate({job="nginx"} | json [5m]))
   ```

   Another alternative is using `drop` or `keep` to reduce the number of labels and hence the cardinality:

   ```logql
   # Drop high-cardinality labels like request_id or trace_id
   {job="nginx"} | json | drop request_id, trace_id, session_id
   
   # Keep only the labels you need
   {job="nginx"} | json | keep status, method, path
   ```

* **Increase the limit** if needed:

   ```yaml
   limits_config:
     cardinality_limit: 200000  #default is 100000
   ```

**Properties:**

- Enforced by: Query Engine
- Retryable: No (query must be modified)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Max entries limit per query exceeded

**Error message:**

`max entries limit per query exceeded, limit > max_entries_limit_per_query (<requested> > <limit>)`

**Cause:**

The query requests more log entries than the configured maximum. This applies to log queries (not metric queries).

**Default configuration:**

- `max_entries_limit_per_query`: 5000

**Resolution:**

* **Reduce the limit parameter** in your query request.

* **Add more specific filters** to return fewer results:

   ```logql
   {app="foo"} |= "error" 
   ```

* **Reduce the time range** of the query.

* **Increase the limit** if needed:

   ```yaml
   limits_config:
     max_entries_limit_per_query: 10000  #default is 5000
   ```

**Properties:**

- Enforced by: Querier/Query Frontend
- Retryable: No (query must be modified)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Query would read too many bytes

**Error message:**

`the query would read too many bytes (query: <size>, limit: <limit>); consider adding more specific stream selectors or reduce the time range of the query`

**Cause:**

The estimated data volume for the query exceeds the configured limit. This is determined before query execution using index statistics.

**Default configuration:**

- `max_query_bytes_read`: 0B (disabled by default)

**Resolution:**

* **Add more specific stream selectors** to reduce data volume.
* **Reduce the time range** of the query.

* **Increase the limit** if resources allow:

   ```yaml
   limits_config:
     max_query_bytes_read: 10GB
   ```

**Properties:**

- Enforced by: Query Frontend
- Retryable: No (query must be modified)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Too many chunks (count)

**Error message:**

`the query hit the max number of chunks limit (limit: 2000000 chunks)`

**Cause:**

The number of chunks that the query would read exceeds the configured limit. This protects against queries that would scan excessive amounts of data and consume too much memory.

**Default configuration:**

- `max_chunks_per_query`: 2000000

**Resolution:**

* **Narrow stream selectors** to reduce the number of matching chunks:

   ```logql
   # Too broad
   {job="app"}
   
   # More specific
   {job="app", environment="production", namespace="api"}
   ```

* **Reduce the query time range** to scan fewer chunks.

* **Increase the limit** if resources allow:

   ```yaml
   limits_config:
     max_chunks_per_query: 5000000  #default is 2000000
   ```

**Properties:**

- Enforced by: Query Frontend
- Retryable: No (query must be modified)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Stream matcher limits

**Error message:**

`max streams matchers per query exceeded, matchers-count > limit (1500 > 1000)`

**Cause:**

The query contains too many stream matchers. This limit prevents queries with excessive complexity that could impact query performance.

**Default configuration:**

- `max_streams_matchers_per_query`: 1000

**Resolution:**

* **Simplify your query** by using fewer label matchers.
* **Combine multiple queries** instead of using many OR conditions.
* **Use regex matchers** to consolidate multiple values:

   ```logql
   # Good: 3 matchers using regex patterns
   {cluster="prod", namespace=~"api|web", pod=~"nginx-.*"}
   ```

* **Increase the limit** if needed:

   ```yaml
   limits_config:
     max_streams_matchers_per_query: 2000  #default is 1000
   ```

**Properties:**

- Enforced by: Querier
- Retryable: No (query must be modified)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Query too large for single querier

**Error message:**

`query too large to execute on a single querier: (query: <size>, limit: <limit>); consider adding more specific stream selectors, reduce the time range of the query, or adjust parallelization settings`

Or for un-shardable queries:

`un-shardable query too large to execute on a single querier: (query: <size>, limit: <limit>); consider adding more specific stream selectors or reduce the time range of the query`

**Cause:**

Even after query splitting and sharding, individual query shards exceed the per-querier byte limit.

**Default configuration:**

- `max_querier_bytes_read`: 150GB (per querier)

**Resolution:**

* **Add more specific stream selectors**.
* **Reduce the time range** or Break large queries into smaller time ranges.
* **Simplify the query** if possible - some queries cannot be sharded.
* **Increase the limit** (requires more querier resources):

   ```yaml
   limits_config:
     max_querier_bytes_read: 200GB  # default is 150GB
   ```

* **Scale querier resources**

**Properties:**

- Enforced by: Query Frontend
- Retryable: No (query must be modified)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Response larger than max message size

**Error message:**

```text
response larger than the max message size (<size> vs <max>)
```

**Cause:**

A query result from the querier to the frontend exceeds the maximum allowed gRPC response size. This typically happens with queries that return very large result sets.

**Default configuration:**

- `server.grpc_server_max_send_msg_size`: 4MB (gRPC server send limit on the querier)
- `querier.query_frontend_grpc_client.max_recv_msg_size`: 100MB (gRPC client receive limit on the querier worker)

**Resolution:**

- **Reduce query scope** to return fewer results:
  - Add more specific label matchers
  - Reduce the time range
  - Lower the entries limit

- **Increase gRPC message size limits** if needed. Apply these settings to querier nodes:

  ```yaml
  server:
    grpc_server_max_send_msg_size: 209715200   # 200MB

  querier:
    query_frontend_grpc_client:
      max_recv_msg_size: 209715200             # 200MB
  ```

**Properties:**

- Enforced by: Querier worker
- Retryable: No (query scope or limits must change)
- HTTP status: 413 Request Entity Too Large
- Configurable per tenant: No

### Error: Interval value exceeds limit

**Error message:**

`[interval] value exceeds limit`

**Cause:**

The range vector interval (in brackets like `[5m]`) exceeds configured limits.

**Resolution:**

* **Reduce the range interval** in your query:

   ```logql
   # If [1d] is too large, try smaller intervals
   rate({app="foo"}[1h])
   ```

* **Check your configuration** for `max_query_length` limits. The default is `30d1h`.

**Properties:**

- Enforced by: Query Engine
- Retryable: No (query must be modified)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

## Time range errors

These errors relate to the time range specified in queries.

### Error: Query time range exceeds limit

**Error message:**

`the query time range exceeds the limit (query length: <duration>, limit: <limit>)`

**Cause:**

The difference between the query's start and end time exceeds the maximum allowed query length.

**Default configuration:**

- `max_query_length`: 721h (30 days + 1 hour)

**Resolution:**

* **Reduce the query time range**:

   ```logcli
   # Instead of querying 60 days
   logcli query '{app="foo"}' --from="60d" --to="now"
   
   # Query 30 days or less
   logcli query '{app="foo"}' --from="30d" --to="now"
   ```

* **Increase the limit** if storage retention supports it:

   ```yaml
   limits_config:
     max_query_length: 2160h  # 90 days
   ```

**Properties:**

- Enforced by: Query Frontend/Querier
- Retryable: No (query must be modified)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Data is no longer available

**Error message:**

`this data is no longer available, it is past now - max_query_lookback (<duration>)`

**Cause:**

The entire query time range falls before the `max_query_lookback` limit. This happens when trying to query data older than the configured lookback period.

**Default configuration:**

- `max_query_lookback`: 0 (The default value of 0 does not set a limit.)

**Resolution:**

* **Query more recent data** within the lookback window.
* **Adjust the lookback limit** if the data should be queryable:

   ```yaml
   limits_config:
     max_query_lookback: 8760h  # 1 year
   ```

   {{< admonition type="caution" >}}
   The lookback limit should not exceed your retention period.
   {{< /admonition >}}

**Properties:**

- Enforced by: Query Frontend/Querier
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Invalid query time range

**Error message:**

`invalid query, through < from (<end> < <start>)`

**Cause:**

The query end time is before the start time, which is invalid.

**Resolution:**

* **Swap start and end times** if they were reversed.
* **Check timestamp formats** to ensure times are correctly specified.

**Properties:**

- Enforced by: Query Frontend/Querier
- Retryable: No (query must be fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: No

## Required labels errors

These errors occur when queries don't meet configured label requirements.

### Error: Missing required matchers

**Error message:**

`stream selector is missing required matchers [<required_labels>], labels present in the query were [<present_labels>]`

**Cause:**

The tenant is configured to require certain label matchers in all queries, but the query doesn't include them.

**Default configuration:**

- `required_labels`: [] (none required by default)

**Resolution:**

* **Check with your administrator** about which labels are required.

* **Add the required labels** to your query:

   ```logql
   # If 'namespace' is required
   {app="foo", namespace="production"}
   ```

**Properties:**

- Enforced by: Query Frontend
- Retryable: No (query must include required labels)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Not enough label matchers

**Error message:**

`stream selector has less label matchers than required: (present: [<labels>], number_present: <count>, required_number_label_matchers: <required>)`

**Cause:**

The tenant is configured to require a minimum number of label matchers, but the query has fewer.

**Default configuration:**

- `minimum_labels_number`: 0 (no minimum by default)

**Resolution:**

* **Add more label matchers** to meet the minimum requirement:

   ```logql
   # If minimum is 2, add another selector
   {app="foo", namespace="production"}
   ```

**Properties:**

- Enforced by: Query Frontend
- Retryable: No (query must meet requirements)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

## Timeout errors

Timeout errors occur when queries take too long to execute.

### Error: Request timed out

**Error message:**

`request timed out, decrease the duration of the request or add more label matchers (prefer exact match over regex match) to reduce the amount of data processed`

Or:

`context deadline exceeded`

**Cause:**

The query exceeded the configured timeout. This can happen due to:

- Large time ranges
- High cardinality queries
- Complex query expressions
- Insufficient cluster resources
- Network issues

**Default configuration:**

- `query_timeout`: 1m
- `server.http_server_read_timeout`: 30s
- `server.http_server_write_timeout`: 30s

**Resolution:**

* **Reduce the time range** of the query.
* **Add more specific filters** to reduce data processing:

   ```logql
   # Less specific (slower)
   {namespace=~"prod.*"}
   
   # More specific (faster)
   {namespace="production"}
   ```

* **Prefer exact matchers over regex** when possible.
* **Add line filters early** in the pipeline:

   ```logql
   {app="foo"} |= "error" | json | level="error"
   ```

* **Increase timeout limits** (if resources allow):

   ```yaml
   limits_config:
     query_timeout: 5m
   
   server:
     http_server_read_timeout: 5m
     http_server_write_timeout: 5m
   ```

* **Use sampling** for exploratory queries

  ```logql
   {job="app"} | line_format "{{__timestamp__}} {{.msg}}" | sample 0.1
   ```

* **Check for network issues** between components.

**Properties:**

- Enforced by: Query Frontend/Querier
- Retryable: Yes (with modifications)
- HTTP status: 504 Gateway Timeout
- Configurable per tenant: Yes (query_timeout)

### Error: Request cancelled by client

**Error message:**

`the request was cancelled by the client`

**Cause:**

The client closed the connection before receiving a response. This is typically caused by:

- Client-side timeout
- User navigating away in Grafana
- Network interruption

**Resolution:**

* **Increase client timeout** in Grafana or LogCLI.
* **Optimize the query** to return faster.
* **Check network connectivity** between client and Loki.

**Properties:**

- Enforced by: Client
- Retryable: Yes
- HTTP status: 499 Client Closed Request
- Configurable per tenant: No

## Query blocked errors

These errors occur when queries are administratively blocked.

### Error: Query blocked by policy

**Error message:**

`query blocked by policy`

**Cause:**

The query matches a configured block rule. Administrators create tenant policies and rate limiting rules to block specific queries or query patterns to protect the cluster from expensive or problematic queries.

Refer to [Block unwanted queries](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/blocking-queries/) for details on how to configure and troubleshoot these policies.

**Resolution:**

* **Check with your Loki administrator** about blocked queries and to review policy settings.
* **Modify the query** to avoid the block pattern:
  - Change the stream selectors
  - Adjust the time range
  - Use different aggregations
* **Request the block to be removed** if the query is legitimate.

**Configuration reference:**

```yaml
limits_config:
  blocked_queries:
    - pattern: ".*"          # Regex pattern to match
      regex: true
      types:                 # Query types to block
        - metric
        - filter
      hash: 0               # Or block specific query hash
```

**Properties:**

- Enforced by: Query Engine
- Retryable: No (unless block is removed)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Querying is disabled

**Error message:**

`querying is disabled, please contact your Loki operator`

**Cause:**

Query parallelism is set to 0, effectively disabling queries for the tenant.

**Resolution:**

* **Contact your Loki administrator** to enable querying.
* **Check configuration** for `max_query_parallelism`:

   ```yaml
   limits_config:
     max_query_parallelism: 32     #(the default)
   ```

**Properties:**

- Enforced by: Query Frontend
- Retryable: No (until configuration is fixed)
- HTTP status: 400 Bad Request
- Configurable per tenant: Yes

### Error: Parse error on a `variants()` query

Multi variant queries were an experimental feature that ran multiple query variants over the same underlying data. For example, running both a `rate()` and a `count_over_time()` query over the same range selector. The feature was removed, and `variants()` is no longer part of LogQL.

**Error message:**

A parse error, for example `parse error at line 1, col 1: syntax error: unexpected IDENTIFIER`.

**Cause:**

The query uses a `variants()` expression.

**Resolution:**

**Rewrite the query** as separate queries, one per variant. For example, replace `variants(rate({app="foo"}[5m]), count_over_time({app="foo"}[5m])) of ({app="foo"}[5m])` with a `rate({app="foo"}[5m])` query and a `count_over_time({app="foo"}[5m])` query.

**Properties:**

- Enforced by: Query Frontend
- Retryable: No
- HTTP status: 400 Bad Request
- Configurable per tenant: No

## Pipeline processing errors

Pipeline errors occur during log line processing. For log queries, these errors don't cause query failures: affected log lines are annotated with error labels instead. For metric queries, an errored sample fails the query unless a `__error__` label filter in the pipeline says to keep it; see [Pipeline Errors in Metric Queries](https://grafana.com/docs/loki/<LOKI_VERSION>/query/query_reference/#pipeline-errors-in-metric-queries).

### Understanding pipeline errors

When a pipeline stage fails (for example, parsing JSON that isn't valid JSON), Loki:

1. Does NOT filter out the log line
2. Adds an `__error__` label with the error type
3. Optionally adds `__error_details__` with more information
4. Passes the log line to the next pipeline stage

### Error types

| Error Label Value | Cause |
| ------------------ | ------- |
| `JSONParserErr` | Log line is not valid JSON |
| `LogfmtParserErr` | Log line is not valid logfmt |
| `SampleExtractionErr` | Failed to extract numeric value for metrics |
| `LabelFilterErr` | Label filter operation failed |
| `TemplateFormatErr` | Template formatting failed |

### Viewing pipeline errors

To see logs with errors:

```logql
{app="foo"} | json | __error__!=""
```

To see error details:

```logql
{app="foo"} | json | __error__!="" | line_format "Error: {{.__error__}} - {{.__error_details__}}"
```

### Filtering out errors

To exclude logs with parsing errors:

```logql
{app="foo"} | json | __error__=""
```

To exclude specific error types:

```logql
{app="foo"} | json | __error__!="JSONParserErr"
```

### Dropping error labels

To remove error labels from results:

```logql
{app="foo"} | json | drop __error__, __error_details__
```

## Authentication and connection errors

These errors occur when connecting to Loki, often when using LogCLI.

### Error: No org ID

**Error message:**

```text
no org id
```

**Cause:**

Multi-tenancy is enabled but no tenant ID was provided in the request.

**Resolution:**

1. **Add the X-Scope-OrgID header** in your request.
1. **For LogCLI**, use the `--org-id` flag:

   ```bash
   logcli query '{app="foo"}' --org-id="my-tenant"
   ```

1. **In Grafana**, configure the tenant ID in the data source settings.

**Properties:**

- Enforced by: Loki API
- Retryable: Yes (with tenant ID)
- HTTP status: 401 Unauthorized
- Configurable per tenant: No

### Error: Authentication configuration conflict

**Error message:**

```text
at most one of HTTP basic auth (username/password), bearer-token & bearer-token-file is allowed to be configured
```

Or:

```text
at most one of the options bearer-token & bearer-token-file is allowed to be configured
```

**Cause:**

Multiple authentication methods are configured simultaneously in LogCLI.

**Resolution:**

1. **Use only one authentication method**:

   ```bash
   # Basic auth
   logcli query '{app="foo"}' --username="user" --password="pass"
   
   # OR bearer token
   logcli query '{app="foo"}' --bearer-token="token"
   
   # OR bearer token file
   logcli query '{app="foo"}' --bearer-token-file="/path/to/token"
   ```

**Properties:**

- Enforced by: LogCLI
- Retryable: Yes (with correct configuration)
- HTTP status: N/A (client-side error)
- Configurable per tenant: No

### Error: Run out of attempts while querying

**Error message:**

```text
run out of attempts while querying the server
```

**Cause:**

LogCLI exhausted all retry attempts when trying to reach Loki. This usually indicates:

- Network connectivity issues
- Server unavailability
- Authentication failures

**Resolution:**

1. **Check Loki server availability**.
1. **Verify network connectivity**.
1. **Check authentication credentials**.
1. **Increase retries** if transient issues are expected:

   ```bash
   logcli query '{app="foo"}' --retries=5
   ```

**Properties:**

- Enforced by: LogCLI
- Retryable: Yes (automatic retries exhausted)
- HTTP status: Varies
- Configurable per tenant: No

### Error: WebSocket connection closed unexpectedly

**Error message:**

```text
websocket: close 1006 (abnormal closure): unexpected EOF
```

**Cause:**

When tailing logs, the WebSocket connection was closed unexpectedly. This can happen if:

- The querier handling the tail request stopped
- Network interruption occurred
- Server-side timeout

**Resolution:**

1. LogCLI will automatically attempt to reconnect, up to 5 times.
1. **Check Loki querier health** if reconnections fail.
1. **Review network stability** between client and server.

**Properties:**

- Enforced by: Network/Server
- Retryable: Yes (automatic reconnection)
- HTTP status: N/A (WebSocket error)
- Configurable per tenant: No

## Data availability errors

These errors occur when requested data is not available.

### Error: No data found

**Error message:**

```text
no data found
```

Or an empty result set with no error message.

**Cause:**

The query time range contains no matching log data. This can happen if:

- No logs match the stream selectors
- The time range is outside the data retention period
- Log ingestion is not working
- Stream labels don't match any existing streams

**Resolution:**

1. **Verify the time range** contains data for your streams.
1. **Check if log ingestion is working** correctly:

   ```bash
   # Check if any data is being ingested
   logcli query '{job=~".+"}'
   ```

1. **Verify stream selectors** match existing log streams:

   ```bash
   # List available streams
   curl http://loki:3100/loki/api/v1/series
   ```

1. **Check data retention** settings to ensure logs are still available.
1. **Use broader selectors** to test if any data exists:

   ```logql
   {job=~".+"}
   ```

**Properties:**

- Enforced by: Query Engine
- Retryable: Yes (with different parameters)
- HTTP status: 200 OK (with empty result)
- Configurable per tenant: No

### Error: Index not ready

**Error message:**

```text
index not ready
```

Or:

```text
index gateway not ready for time range
```

**Cause:**

The index for the requested time range is not yet available for querying. This can happen when:

- Index files are still being synced from storage
- The index gateway is still starting up
- Querying data older than the configured ready index period

**Default configuration:**

- `query_ready_index_num_days`: 0 (all indexes are considered ready)

**Resolution:**

1. **Wait for the index to become available** - this is often a temporary issue during startup.
1. **Query more recent data** that's available in ingesters:

   ```logql
   {app="foo"} # Query last few hours instead of older data
   ```

1. **Check the configuration** for index readiness:

   ```yaml
   query_range:
     query_ready_index_num_days: 7  #default is 0
   ```

1. **Verify index synchronization** is working correctly by checking ingester and index gateway logs.

**Properties:**

- Enforced by: Index Gateway/Querier
- Retryable: Yes (wait and retry)
- HTTP status: 503 Service Unavailable
- Configurable per tenant: No

### Error: Tenant limits

**Error message:**

```text
max concurrent tail requests limit exceeded, count > limit (10 > 5)
```

**Cause:**

The tenant has exceeded the maximum number of concurrent streaming (tail) requests. This limit protects the cluster from excessive resource consumption by real-time log streaming.

**Default configuration:**

- `max_concurrent_tail_requests`: 10

**Resolution:**

1. **Reduce the number of concurrent tail/streaming queries**.
1. **Use batch queries** instead of real-time streaming where possible:

   ```logql
   # Instead of tailing in real-time
   # Use periodic range queries
   {app="foo"} |= "error"
   ```

1. **Increase the limit** if more concurrent tails are needed:

   ```yaml
   limits_config:
     max_concurrent_tail_requests: 20  #default is 10
   ```

**Properties:**

- Enforced by: Querier
- Retryable: Yes (when connections are available)
- HTTP status: 429 Too Many Requests
- Configurable per tenant: Yes

## Storage errors

These errors occur when Loki cannot read data from storage.

### Error: Failed to load chunk

**Error message:**

```text
failed to load chunk '<chunk_key>'
```

**Cause:**

Loki couldn't retrieve a chunk from object storage. Possible causes:

- Chunk was deleted or moved
- Storage permissions issue
- Network connectivity to storage
- Storage service unavailable

**Resolution:**

1. **Check storage connectivity** from Loki components.
1. **Verify storage credentials and permissions**.
1. **Check for chunk corruption** or deletion.
1. **Review storage service status**.

**Properties:**

- Enforced by: Storage Client
- Retryable: Yes (automatically)
- HTTP status: 500 Internal Server Error
- Configurable per tenant: No

### Error: Object not found in storage

**Error message:**

```text
object not found in storage
```

**Cause:**

The requested chunk or object doesn't exist in storage. This might happen if:

- Data was deleted due to retention
- Compaction removed the chunk
- Chunk was never written successfully

**Resolution:**

1. **Check if data is within retention period**.
1. **Verify data was ingested successfully**.
1. **Review compaction jobs** for issues.

**Properties:**

- Enforced by: Storage Client
- Retryable: No (data doesn't exist)
- HTTP status: 404 or 500 depending on context
- Configurable per tenant: No

### Error: Failed to decode chunk

**Error message:**

```text
failed to decode chunk '<chunk_key>' for tenant '<tenant>': <error>
```

**Cause:**

A chunk was retrieved from storage but couldn't be decoded. This indicates chunk corruption.

**Resolution:**

1. **Report to Loki administrators** for investigation.
1. **Check for storage data integrity issues**.
1. Note that the corrupted chunk data may be unrecoverable.

**Properties:**

- Enforced by: Storage Client
- Retryable: No (chunk is corrupted)
- HTTP status: 500 Internal Server Error
- Configurable per tenant: No

## Troubleshooting workflow

Follow this workflow when investigating query issues:

1. **Check the error message** - Identify which category of error you're encountering.

1. **Review query syntax** - Use the LogQL documentation to validate your query.

1. **Check query statistics** - In Grafana, enable "Query Inspector" to see:
   - Bytes processed
   - Number of chunks scanned
   - Execution time breakdown

1. **Simplify the query** - Start with a basic selector and add complexity:

   ```logql
   # Start simple
   {app="foo"}
   
   # Add filters
   {app="foo"} |= "error"
   
   # Add parsing
   {app="foo"} |= "error" | json
   
   # Add label filters
   {app="foo"} |= "error" | json | level="error"
   ```

1. **Check metrics** for query performance:

   ```promql
   # Query latency
   histogram_quantile(0.99, sum(rate(loki_request_duration_seconds_bucket[5m])) by (le, route))
   
   # Query errors
   sum by (status_code) (rate(loki_request_duration_seconds_count[5m]))
   ```

1. **Review Loki logs** for detailed error information:

   ```bash
   kubectl logs -l app=loki-read --tail=100 | grep -i error
   ```

1. **Test with LogCLI** for more detailed output:

   ```bash
   logcli query '{app="foo"}' --stats --limit=10
   ```

## Related resources

- Learn more about [LogQL Query Language](https://grafana.com/docs/loki/<LOKI_VERSION>/query/)
- Configure appropriate [query limits](https://grafana.com/docs/loki/<LOKI_VERSION>/configure/#limits_config)
- Learn more about [Query performance tuning](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/query-acceleration/)
- Review the [LogCLI documentation](https://grafana.com/docs/loki/<LOKI_VERSION>/query/logcli/)
- Learn more about [LogQL query optimization](https://grafana.com/docs/loki/<LOKI_VERSION>/query/log_queries/)
- Review [query performance best practices](https://grafana.com/docs/loki/<LOKI_VERSION>/best-practices/)
- Use [query debugging features](https://grafana.com/docs/loki/<LOKI_VERSION>/query/query_stats/) to analyze slow queries
- Explore the [Grafana Loki GitHub repository](https://github.com/grafana/loki) for community support

# Visualize

# Visualize log data

Grafana Loki does not have its own user interface. Most users [install Grafana](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/installation/) in order to visualize their log data. Grafana has built-in support for Grafana Loki and [LogQL](https://grafana.com/docs/loki/<LOKI_VERSION>/query/).

There are several different options for how to visualize your log data in Grafana:

- [Logs Drilldown](https://grafana.com/docs/grafana-cloud/visualizations/simplified-exploration/logs/) lets you automatically visualize and explore your logs from your Loki data source without having to write LogQL queries.
- [Grafana Explore](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/explore/logs-integration/) helps you build and iterate on queries written in LogQL. Once you have a query that finds the data you're looking for, you can consider using your query in a Grafana dashboard.
- [Loki Mixins](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/mixins/) include a pre-built set of dashboards, recording rules, and alerts for monitoring Loki.
- [Grafana Dashboards](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/dashboards/) let you query, transform, visualize, and understand your log data. You can create your own custom dashboards, or import and modify public dashboards shared by the community.

## Logs Drilldown

Grafana Logs Drilldown lets you automatically visualize and explore logs. Logs Drilldown makes assumptions about what data you might want to see to help you quickly get started viewing your logs without having to learn LogQL and write queries.

- If you are a Grafana Cloud user, you can access Logs Drilldown from the Grafana Cloud main navigation menu: select **Drilldown** > **Logs**.

- If you are an open source or Grafana Enterprise Logs user, you can install the [Logs Drilldown plugin](https://grafana.com/docs/grafana-cloud/visualizations/simplified-exploration/logs/access/). For more information, refer to the [Logs Drilldown documentation](https://grafana.com/docs/grafana-cloud/visualizations/simplified-exploration/logs/).

{{< admonition type="note" >}}
Starting with Grafana v11.3, the plugin for the Logs Drilldown app is installed in Grafana by default.
{{< /admonition >}}

Logs Drilldown also requires some Loki-side configuration. Enable pattern ingestion, structured metadata, the volume endpoint, and log level detection in your Loki configuration file. For details, refer to [Access or install Grafana Logs Drilldown](https://grafana.com/docs/grafana-cloud/visualizations/simplified-exploration/logs/access/).

{{< docs/learning-paths title="Explore logs using Logs Drilldown" url="https://grafana.com/docs/learning-paths/drilldown-logs/" >}}

{{< admonition type="tip" >}}
There is a link to Grafana Explore available on every panel in Logs Drilldown. Click the menu icon (three stacked dots) to access the link to Explore.  
{{< /admonition >}}

## Grafana Explore

[Grafana Explore](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/explore/) helps you build and iterate on a LogQL query outside of the dashboard user interface. If you just want to explore your data and do not want to create a dashboard, then Explore makes this much easier.

Before you can use Explore with Loki, you need to add Loki as a data source in Grafana. For instructions, refer to [Configure the Loki data source](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/datasources/loki/configure/). If your Loki server has [multi-tenancy](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/multi-tenancy/) enabled, you must also provide your tenant ID in the `X-Scope-OrgID` header when configuring the data source. Multi-tenancy is enabled by default when running Loki with Helm on Kubernetes.

To query your logs with Explore:

1. Log into your Grafana instance. If this is your first time running Grafana, the username and password are both defaulted to `admin`.
1. In the Grafana main menu, click **Explore**.
1. Select the Loki data source from the data source menu.
1. You can click **Kick start your query** to select from a list of common queries, or use the **Label filters** to start choosing labels that you want to query. For more information about the Loki query language, refer to the [LogQL section](https://grafana.com/docs/loki/<LOKI_VERSION>/query/).

If you would like to see an example of this live, you can try [Grafana Play's Explore feature](https://play.grafana.org/explore?schemaVersion=1&panes=%7B%22v1d%22:%7B%22datasource%22:%22grafanacloud-logs%22,%22queries%22:%5B%7B%22refId%22:%22A%22,%22expr%22:%22%7Blabel_environment%3D%5C%22production%5C%22%7D%20%7C%3D%20%60%60%22,%22queryType%22:%22range%22,%22datasource%22:%7B%22type%22:%22loki%22,%22uid%22:%22grafanacloud-logs%22%7D,%22editorMode%22:%22builder%22,%22direction%22:%22backward%22%7D%5D,%22range%22:%7B%22from%22:%22now-1h%22,%22to%22:%22now%22%7D,%22panelsState%22:%7B%22logs%22:%7B%22logs%22:%7B%22visualisationType%22:%22logs%22%7D,%22sortOrder%22:%22Descending%22%7D%7D,%22compact%22:false%7D%7D&orgId=1).

Learn more about the Grafana Explore feature in the [Grafana documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/explore/logs-integration/).

## Loki mixins

The Loki mixin provides a set of Grafana dashboards, Prometheus recording rules and alerts for monitoring Loki itself. For instructions on how to install the Loki mixins, refer to the [installation topic](https://grafana.com/docs/loki/<LOKI_VERSION>/operations/meta-monitoring/mixins/).

## Using Grafana dashboards

{{< docs/play title="Loki Example Grafana Dashboard" url="https://play.grafana.org/d/T512JVH7z/" >}}

Because Loki can be used as a built-in data source, you can use LogQL queries based on that data source to build complex visualizations that persist on Grafana dashboards.

To configure Loki as a data source via provisioning, refer to the documentation for [Loki data source](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/datasources/loki/configure/#provision-the-data-source).

Read more about how to build Grafana Dashboards in [build your first dashboard](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/getting-started/build-first-dashboard/).

{{< docs/learning-paths title="Visualize logs in a Grafana Cloud dashboard" url="https://grafana.com/docs/learning-paths/visualization-logs/" >}}

## Learn more

To learn more about visualizations in Grafana, you can visit the [Grafana YouTube channel](https://www.youtube.com/@Grafana), where you can find videos like the following:

- [Creating Visualizations](https://www.youtube.com/watch?v=yNRnLyVntUw)
- [Visualizing Logs](https://www.youtube.com/watch?v=jSSi_x-fD_8)