Install Loki and Promtail for Logs
Install Grafana Loki and Promtail as single binaries, ship systemd-journald logs, and create log-based metric rules — managed with systemd.
Before you start
- ▸A Linux server with systemd and at least 1 GB RAM
- ▸curl and unzip installed
- ▸sudo or root access
- ▸Outbound internet access to download binaries from GitHub
Grafana Loki is a horizontally scalable, log aggregation system designed to index only metadata (labels), not the full log content. Paired with Promtail as the log shipping agent, it gives you a lightweight alternative to a full Elasticsearch stack. This guide walks through installing both as single binaries, configuring Promtail to scrape systemd-journald, and setting up a basic log-based metric rule — all managed under systemd.
Prerequisites and Architecture
Loki listens on port 3100. Promtail runs on each host you want to collect logs from, ships them to Loki over HTTP, and in a single-host setup both services run on the same machine. You will need Grafana separately to visualise the data; this guide does not cover Grafana installation, but adding Loki as a data source takes under two minutes once Loki is running.
- A 64-bit Linux server with at least 1 GB RAM
- Outbound internet access to download binaries from GitHub
- A Grafana instance (local or remote) for dashboarding — optional for this guide
curl,unzip, andsystemdavailable
Step 1 — Download the Binaries
Grafana publishes Loki and Promtail as static binaries on GitHub. Find the latest stable release at github.com/grafana/loki/releases. At time of writing that is 3.1.0; substitute newer versions as they appear.
LOKI_VERSION="3.1.0"
ARCH="amd64" # change to arm64 on ARM hosts
curl -fSL -o /tmp/loki.zip \
"https://github.com/grafana/loki/releases/download/v${LOKI_VERSION}/loki-linux-${ARCH}.zip"
curl -fSL -o /tmp/promtail.zip \
"https://github.com/grafana/loki/releases/download/v${LOKI_VERSION}/promtail-linux-${ARCH}.zip"
cd /tmp
unzip loki.zip
unzip promtail.zip
sudo install -m 755 loki-linux-${ARCH} /usr/local/bin/loki
sudo install -m 755 promtail-linux-${ARCH} /usr/local/bin/promtail
Verify the binaries are in place and executable:
loki --version
promtail --version
Step 2 — Create System Users and Directories
Run each service as a dedicated unprivileged user. Promtail needs supplementary membership in the systemd-journal group to read the journal socket without root.
sudo useradd --system --no-create-home --shell /usr/sbin/nologin loki
sudo useradd --system --no-create-home --shell /usr/sbin/nologin promtail
# Debian/Ubuntu
sudo usermod -aG systemd-journal promtail
# Fedora / RHEL / Rocky — same command; group exists by default
sudo usermod -aG systemd-journal promtail
# Arch — same
sudo usermod -aG systemd-journal promtail
sudo mkdir -p /etc/loki /etc/promtail /var/lib/loki /var/lib/promtail
sudo chown loki:loki /var/lib/loki
sudo chown promtail:promtail /var/lib/promtail
Step 3 — Configure Loki
The minimal single-node configuration below uses the TSDB storage backend (default since Loki 2.8) and stores chunks on local disk. For production, swap filesystem for an object store like S3 or GCS.
sudo tee /etc/loki/loki.yaml <<'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-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
ruler:
storage:
type: local
local:
directory: /var/lib/loki/rules
rule_path: /var/lib/loki/rules-temp
alertmanager_url: http://localhost:9093 # adjust or remove if not using Alertmanager
enable_api: true
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
EOF
Step 4 — Configure Promtail
Promtail's loki_push_api target type (available since Promtail 2.4) is the simplest path to collecting journald, but the journal scrape config is more explicit and easier to label. The configuration below uses journal directly and adds the machine hostname and unit name as labels.
sudo tee /etc/promtail/promtail.yaml <<'EOF'
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /var/lib/promtail/positions.yaml
clients:
- url: http://127.0.0.1:3100/loki/api/v1/push
scrape_configs:
- job_name: journal
journal:
max_age: 12h
labels:
job: systemd-journal
host: __HOSTNAME__ # Promtail substitutes the real hostname at runtime
relabel_configs:
- source_labels: [__journal__systemd_unit]
target_label: unit
- source_labels: [__journal__priority_keyword]
target_label: level
- source_labels: [__journal__syslog_identifier]
target_label: syslog_identifier
EOF
Note: __HOSTNAME__ is a Promtail built-in that expands to the system hostname; you do not need to hardcode it.
Step 5 — Create systemd Units
Loki service
sudo tee /etc/systemd/system/loki.service <<'EOF'
[Unit]
Description=Grafana Loki log aggregation system
After=network.target
[Service]
User=loki
Group=loki
ExecStart=/usr/local/bin/loki -config.file=/etc/loki/loki.yaml
Restart=on-failure
RestartSec=5s
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
Promtail service
sudo tee /etc/systemd/system/promtail.service <<'EOF'
[Unit]
Description=Promtail log shipping agent
After=network.target loki.service
[Service]
User=promtail
Group=promtail
ExecStart=/usr/local/bin/promtail -config.file=/etc/promtail/promtail.yaml
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now loki promtail
Step 6 — Open the Firewall (if applicable)
If Grafana runs on a different host, open port 3100 only to that host. Do not expose Loki to the public internet without authentication.
# ufw (Debian/Ubuntu)
sudo ufw allow from 192.168.1.0/24 to any port 3100 proto tcp
# firewalld (Fedora/RHEL/Rocky)
sudo firewall-cmd --permanent --add-rich-rule=\
'rule family=ipv4 source address=192.168.1.0/24 port port=3100 protocol=tcp accept'
sudo firewall-cmd --reload
Step 7 — Add a Log-Based Metric Rule
Loki's ruler can derive Prometheus-compatible metrics from log streams. Create a recording rule that counts SSH authentication failures per minute — useful for alerting and dashboarding without storing raw log lines in a metrics system.
sudo mkdir -p /var/lib/loki/rules/fake # "fake" is the tenant name when auth_enabled=false
sudo tee /var/lib/loki/rules/fake/ssh-rules.yaml <<'EOF'
groups:
- name: ssh
rules:
- record: job:ssh_auth_failures:rate1m
expr: |
sum by (host) (
rate(
{job="systemd-journal", unit="sshd.service"}
|= "Failed password"
| unwrap __error__
[1m]
)
)
- alert: HighSSHFailures
expr: job:ssh_auth_failures:rate1m > 5
for: 2m
labels:
severity: warning
annotations:
summary: "Elevated SSH failures on {{ $labels.host }}"
EOF
sudo chown -R loki:loki /var/lib/loki/rules
sudo systemctl restart loki
Verification
Check that both services are running cleanly:
systemctl status loki promtail
journalctl -u loki -u promtail --since "5 minutes ago"
Query the Loki HTTP API directly to confirm logs are arriving:
curl -s 'http://localhost:3100/loki/api/v1/query_range' \
--data-urlencode 'query={job="systemd-journal"}' \
--data-urlencode 'limit=5' \
--data-urlencode 'start=1h' | python3 -m json.tool | head -40
A successful response will contain a streams array with log entries. Also hit the ready endpoint:
curl http://localhost:3100/ready
# Expected output: ready
Troubleshooting
- Promtail can't read the journal: Confirm
promtailis in thesystemd-journalgroup (groups promtail) and that the service was restarted after adding the group. The group membership only takes effect in a new login session or service restart. - Loki returns 429 "too many outstanding requests": You are hitting the default ingestion rate limit. Add
ingestion_rate_mb: 16andingestion_burst_size_mb: 32underlimits_configfor a busier host. - "entry out of order" errors in Loki logs: This usually means two Promtail instances are pushing the same stream with overlapping timestamps. Ensure each host has a unique
hostlabel. - Rules not evaluating: Check that the rules directory path in
loki.yamlmatches what you created (/var/lib/loki/rules) and that files are owned by thelokiuser. Verify withcurl http://localhost:3100/loki/api/v1/rules. - Binary not found after install: Confirm
/usr/local/binis on yourPATHor use the full path in the systemd unit'sExecStart.
Frequently asked questions
- Can I run Loki and Promtail on separate machines?
- Yes. Install only Promtail on each log-producing host and point its clients URL to the Loki server's IP on port 3100. Ensure the firewall permits that traffic and consider adding Loki's built-in basic auth or fronting it with an authenticated reverse proxy.
- How is Loki different from an ELK/EFK stack?
- Loki does not full-text index log content; it indexes only the stream labels you define. This makes storage much cheaper but means log searches run as grep-style scans over compressed chunks rather than inverted-index lookups. Query performance scales with the time range and label selectivity, not corpus size.
- How do I add Loki as a data source in Grafana?
- In Grafana go to Connections → Data sources → Add new → Loki. Set the URL to http://localhost:3100 (or the Loki server's address) and click Save & test. No credentials are needed for the single-node auth_enabled: false configuration in this guide.
- What happens to logs if Loki is temporarily unavailable?
- Promtail tracks its read position in the positions.yaml file and will re-send any entries it has not yet confirmed as delivered. By default Promtail retries with exponential back-off, so short Loki outages result in delayed delivery rather than lost logs.
- Should I use the single-binary mode or the microservices deployment for production?
- The single-binary mode (target: all) is well suited for a single server or a small team. For high-volume or multi-tenant environments, deploy Loki in microservices or simple-scalable mode with an object store back-end such as S3 and a distributed ring topology.
Related guides
Configure Prometheus Alertmanager
Configure Prometheus Alertmanager with routing trees, receivers, inhibition rules, grouping, Go templates, and PagerDuty/Slack on-call integrations.
Build an Intranet Server on Linux
Set up a complete small-office intranet on one Linux box: Nginx web server, dnsmasq local DNS, Samba file sharing, and a Wiki.js team wiki.
Build an nftables Firewall Script
Build a complete nftables firewall from scratch: tables, chains, sets, default-deny input policy, service allowlisting, and persistent systemd configuration.
Caddy as a Reverse Proxy
Set up Caddy as a reverse proxy with automatic HTTPS, load balancing, WebSocket passthrough, reusable snippets, and header control — no certbot required.