Grafana Alloy is the agent Grafana Labs now recommends for shipping logs to Grafana Loki. It replaces Promtail, which reached end of life on March 2, 2026 and no longer receives updates or security fixes. In this guide you will install Loki, Grafana and Alloy on Debian 12 (bookworm) or Debian 13 (trixie) from the official Grafana APT repository, ship both plain log files under /var/log and the systemd journal to Loki, and query them in Grafana. If you already run Promtail, the last section shows how to convert its config to Alloy in one command.
The versions used here are Loki 3.7, Alloy 1.21 and Grafana 13. The APT repository always installs the latest release, so check your version numbers against the Loki and Alloy docs if your output looks different.
How the pieces fit together
- Loki stores logs. It indexes only a small set of labels per log stream (such as
job,host,unit), not the log text, which keeps it cheap to run. - Alloy runs on every machine you want logs from. It finds log sources, attaches labels, and pushes the lines to Loki’s HTTP API.
- Grafana is the UI. You add Loki as a data source and query logs with LogQL.
In this guide all three run on one Debian server. To collect logs from more servers, install only Alloy on each of them and point it at the Loki server.
Prerequisites
- A Debian 12 or 13 server with
sudoaccess. - Ports
3000(Grafana) and3100(Loki) reachable where needed. Loki has no authentication in this setup, so do not expose port3100to the internet.
Update the system first:
sudo apt update
sudo apt upgrade -y
Step 1: Add the Grafana APT repository
Loki, Alloy and Grafana are all published to the same repository at apt.grafana.com. Add its signing key and source list:
sudo apt install -y wget
sudo mkdir -p /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
sudo chmod 644 /etc/apt/keyrings/grafana.asc
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" \
| sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update
The signed-by= option ties the key to this one repository. Older guides use apt-key add, which is deprecated and has been removed from current APT.
Step 2: Install and configure Loki
sudo apt install -y loki
The package creates a loki system user, installs a systemd unit, and writes a default config to /etc/loki/config.yml. That default stores data under /tmp/loki, which is wiped on reboot, so replace it with a config that keeps data under /var/lib/loki:
sudo mkdir -p /var/lib/loki
sudo chown -R loki: /var/lib/loki
sudo tee /etc/loki/config.yml <<'EOF'
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
common:
instance_addr: 127.0.0.1
path_prefix: /var/lib/loki
storage:
filesystem:
chunks_directory: /var/lib/loki/chunks
rules_directory: /var/lib/loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2024-04-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
limits_config:
retention_period: 744h
compactor:
working_directory: /var/lib/loki/compactor
retention_enabled: true
delete_request_store: filesystem
analytics:
reporting_enabled: false
EOF
A few notes on this config:
store: tsdbwithschema: v13is the schema Loki 3.x expects. The olderboltdb-shipperandv11/v12schemas you will see in older guides still load, but they do not support newer features such as structured metadata.retention_period: 744h(31 days) plusretention_enabled: trueon the compactor makes Loki delete logs older than a month. Without it Loki keeps everything forever and the disk fills up.analytics.reporting_enabled: falseturns off the anonymous usage report Loki sends to Grafana Labs by default.
Check the config, then restart Loki and enable it at boot:
sudo -u loki loki -config.file=/etc/loki/config.yml -verify-config
sudo systemctl restart loki
sudo systemctl enable loki
The first command should print msg="config is valid". Loki takes about 15 seconds to become ready after it starts:
curl -s http://127.0.0.1:3100/ready
ready
Step 3: Install Grafana
sudo apt install -y grafana
sudo systemctl enable --now grafana-server
Grafana listens on port 3000. If you use ufw, allow it:
sudo ufw allow 3000/tcp
Open http://<server-ip>:3000 and log in with admin / admin. Grafana asks you to change the password on first login.
Step 4: Install Grafana Alloy
sudo apt install -y alloy
The package:
- creates an
alloysystem user and adds it to theadmandsystemd-journalgroups, so it can read/var/log/*.logand the journal without running as root; - installs a systemd unit that reads its config from
/etc/alloy/config.alloy, and extra flags from/etc/default/alloy; - keeps its state (such as how far it has read each file) in
/var/lib/alloy/data.
Step 5: Configure Alloy to ship logs
Alloy configs are written in Alloy’s own syntax: a set of named components, each wired to the next. The config below has three parts:
local.file_match+loki.source.filetail every*.logfile under/var/log.loki.source.journalreads the systemd journal.loki.relabelkeeps the systemd unit and the log priority as labels, because Alloy drops the raw__journal_*fields otherwise.loki.writesends everything to Loki.
Replace the sample config the package installs:
sudo tee /etc/alloy/config.alloy <<'EOF'
// Ship plain-text log files under /var/log
local.file_match "varlogs" {
path_targets = [{
__path__ = "/var/log/*.log",
job = "varlogs",
host = constants.hostname,
}]
}
loki.source.file "varlogs" {
targets = local.file_match.varlogs.targets
forward_to = [loki.write.local.receiver]
}
// Ship the systemd journal, keeping the unit name as a label
loki.relabel "journal" {
forward_to = []
rule {
source_labels = ["__journal__systemd_unit"]
target_label = "unit"
}
rule {
source_labels = ["__journal_priority_keyword"]
target_label = "level"
}
}
loki.source.journal "read" {
max_age = "12h"
relabel_rules = loki.relabel.journal.rules
labels = {job = "systemd-journal", host = constants.hostname}
forward_to = [loki.write.local.receiver]
}
// Where the logs go
loki.write "local" {
endpoint {
url = "http://127.0.0.1:3100/loki/api/v1/push"
}
}
EOF
Check the syntax, then restart Alloy:
sudo alloy fmt /etc/alloy/config.alloy > /dev/null && echo "config OK"
sudo systemctl restart alloy
sudo systemctl enable alloy
sudo systemctl status alloy --no-pager
alloy fmt needs sudo because /etc/alloy is only readable by root and the alloy group. It exits non-zero and prints the line number if the config has a syntax error. After later edits you can apply changes without a restart with sudo systemctl reload alloy.
On a remote server, change the loki.write URL to your Loki server, for example http://10.0.0.5:3100/loki/api/v1/push.
Optional: open the Alloy UI
Alloy has a built-in web UI on port 12345 that shows every component, its health, and the data flowing between them. It only listens on 127.0.0.1 by default. To reach it from another machine, set CUSTOM_ARGS in /etc/default/alloy:
CUSTOM_ARGS="--server.http.listen-addr=0.0.0.0:12345"
Then restart Alloy with sudo systemctl restart alloy and open http://<server-ip>:12345. Restrict the port with a firewall: the UI has no login.
Step 6: Add Loki to Grafana and query your logs
- In Grafana, go to Connections → Data sources → Add data source and pick Loki.
- Set the URL to
http://127.0.0.1:3100(Loki runs on the same host, over plain HTTP). - Click Save & test. Grafana should report that it connected to Loki.
Go to Explore, pick the Loki data source, switch the query editor to Code, and try these LogQL queries:
{job="varlogs"}
{job="systemd-journal", unit="ssh.service"}
{job="systemd-journal", level="error"} |= "failed"
The first shows lines from /var/log/*.log, the second shows SSH daemon logs from the journal, and the third shows journal errors containing the word “failed”. To check from the shell instead, query Loki’s API directly:
curl -sG http://127.0.0.1:3100/loki/api/v1/query_range \
--data-urlencode 'query={job="varlogs"}' \
--data-urlencode 'limit=5'
Migrating from Promtail
If a server already runs Promtail, Alloy can convert its config for you:
sudo alloy convert --source-format=promtail \
--output=/etc/alloy/config.alloy /etc/promtail/config.yaml
For a simple Promtail config that tails /var/log/*log, the output looks like this:
loki.source.file "system" {
targets = [{
__address__ = "localhost",
__path__ = "/var/log/*log",
job = "varlogs",
}]
forward_to = [loki.write.default.receiver]
file_match {
enabled = true
}
legacy_positions_file = "/data/promtail/positions.yaml"
}
loki.write "default" {
endpoint {
url = "http://127.0.0.1:3100/loki/api/v1/push"
}
external_labels = {}
}
legacy_positions_file makes Alloy start from where Promtail stopped reading, so the switch does not ship duplicate lines. Review the output, then swap the services:
sudo systemctl disable --now promtail
sudo systemctl restart alloy
Promtail’s own metrics have different names from Alloy’s, so update any dashboards or alerts that watched Promtail. See the official Promtail migration guide for the conversion’s limits.
Troubleshooting
- No logs from a file: the
alloyuser must be able to read it. Files in/var/logowned byroot:admwork out of the box; for other files, addalloyto the owning group, or adjust permissions. - No journal logs: run
sudo journalctl -u alloy -n 50and look for permission errors fromloki.source.journal. Thealloyuser needs to be in thesystemd-journalgroup (id alloy). - A flood of old logs on first start:
max_age = "12h"on the journal source limits how far back Alloy reads the journal the first time it runs. Raise it if you want more history.
Conclusion
You installed Loki, Grafana and Grafana Alloy from the Grafana APT repository on Debian, set Loki up with the current TSDB schema and a 31-day retention, and configured Alloy to ship both /var/log files and the systemd journal. You also saw how to convert an existing Promtail config. To collect logs from more servers, install Alloy on each one with the same config and point loki.write at this Loki server.
Related content
- How to Setup Promtail, Grafana and Loki for Free Log Management in Debian 11/12: the Promtail version of this guide
- How to Export Kubernetes Logs to Grafana Loki with Helm: run Alloy as a DaemonSet in Kubernetes
- How to Run Grafana Loki with Docker and Docker Compose