Why replace Promtail with Alloy now
The official Promtail page leaves no room for doubt: the agent has been end of life since March 2, 2026, commercial support has ended and all development continues in Grafana Alloy. A collection agent is a privileged process that runs permanently: leaving it without fixes is not sustainable.
Alloy is not just a renamed Promtail. It is a single collector, configured through components: a log source, a system metrics exporter, a sender to Loki, another to Prometheus. On a VPS that ran Promtail and node_exporter side by side, one service is now enough. It can also relay OTLP if you instrument your applications, a topic covered in our Tempo and OpenTelemetry guide.
This guide does not reinstall Loki or Prometheus: the server side is described in the Loki and Grafana guide and in the Grafana and Prometheus guide. Here, we focus on the agent.
What a single agent changes on your server
- One service to watch — logs and metrics go through the same
alloyprocess, with a single journal to read when something breaks. - Updates that still arrive — Alloy is published in Grafana's apt and rpm repositories: an
apt upgradekeeps it current like the rest of the system. - A tooled migration — the
alloy convertcommand translates an existing Promtail configuration and produces a report of what it could not convert. - Built-in system metrics — the
prometheus.exporter.unixcomponent replaces node_exporter without an extra binary or port to open. - A local debugging interface — the UI on port 12345 shows the component graph and their values, reachable through an SSH tunnel.
- Output to your own stack or to Grafana Cloud — the same file sends to a self-hosted Loki or to a managed service by changing one URL.
Sized prerequisites for Alloy on a VPS
Alloy installs on Debian, Ubuntu, AlmaLinux or Rocky Linux from Grafana's official repositories. The package creates an alloy system user, a systemd service of the same name and the configuration file /etc/alloy/config.alloy.
For resources, the Alloy documentation gives two orders of magnitude: about 1 core and 120 MiB of memory per MiB/s of ingested logs, and about 11 GiB of memory per million active Prometheus series. A standalone VPS exposes a few thousand series and rarely writes more than a few hundred KiB of logs per second. Our editorial recommendation: reserve half a vCPU and 256 to 512 MB of RAM for the agent, plus a few GB of disk under /var/lib/alloy for read positions and the send buffer.
Finally, you need a destination: a Loki reachable on /loki/api/v1/push, and a Prometheus started with the --web.enable-remote-write-receiver flag, which opens the /api/v1/write endpoint. No inbound port needs to be opened on the agent's VPS: Alloy pushes, it is not polled.
Install Alloy and connect logs and metrics
Add the Grafana repository on Debian or Ubuntu
sudo mkdir -p /etc/apt/keyringssudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.keysudo chmod 644 /etc/apt/keyrings/grafana.ascecho "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.listsudo apt-get update && sudo apt-get install alloyOr install it on AlmaLinux and Rocky Linux
Import the key with
wget -q -O gpg.key https://rpm.grafana.com/gpg.keythensudo rpm --import gpg.key. Create/etc/yum.repos.d/grafana.repowithbaseurl=https://rpm.grafana.com,repo_gpgcheck=1,gpgcheck=1andenabled=1, as the Alloy Linux installation page shows, then runsudo dnf install alloy.Start the service and check its user
sudo systemctl enable --now alloysudo systemctl status alloysystemctl cat alloy | grep UserThe last command should print
User=alloy. The package does not set aGroup=: read access therefore comes from that user's secondary groups, which matters for what follows.Declare the log destination
Open
/etc/alloy/config.alloyand add the sender to Loki:loki.write "default" {endpoint {url = "http://127.0.0.1:3100/loki/api/v1/push"}}Replace the address with your Loki's. If it is remote, use HTTPS and a
basic_authblock.Collect journald and log files
loki.source.journal "system" {forward_to = [loki.write.default.receiver]labels = {"job" = "systemd-journal"}}local.file_match "nginx" {path_targets = [{"__path__" = "/var/log/nginx/*.log", "job" = "nginx"}]}loki.source.file "nginx" {targets = local.file_match.nginx.targetsforward_to = [loki.write.default.receiver]}By default, the journal source reads
/var/log/journaland/run/log/journal, and only goes back seven hours on first start.Collect system metrics
prometheus.exporter.unix "node" { }prometheus.scrape "node" {targets = prometheus.exporter.unix.node.targetsforward_to = [prometheus.remote_write.default.receiver]}prometheus.remote_write "default" {endpoint {url = "http://127.0.0.1:9090/api/v1/write"}}If node_exporter was already running on the machine, stop it after the switch so the same series are not collected twice.
Validate before reloading
sudo alloy validate /etc/alloy/config.alloysudo systemctl reload alloysudo journalctl -u alloy -fA syntax error or a wrongly referenced component is reported by
validatebefore the service loads the configuration.Grant read access
id alloyshould list theadmandsystemd-journalgroups. The package adds them when they exist; after a binary install or a removed group, restore them withsudo usermod -aG adm,systemd-journal alloythensudo systemctl restart alloy. For an application folder owned by another user, the documentation prefers an ACL to one more group:sudo setfacl -R -m u:alloy:rx /var/log/monappandsudo setfacl -R -d -m u:alloy:rx /var/log/monapp.Check end to end, then connect your dashboards
Write a marker line with
logger -t alloy-test "ligne de test alloy", then look for it in Grafana, Explore tab, with the query{job="systemd-journal"} |= "ligne de test alloy". On the metrics side, thenode_load1query should return a series for your VPS. Both answer: reuse the dashboards from our Grafana and Prometheus guide, which read the samenode_*series.
Alloy and Docker: read containers without opening everything
To read your containers' logs, Alloy installed on the host combines discovery.docker and loki.source.docker, both pointed at unix:///var/run/docker.sock. The alloy user must then be able to open that socket, which in practice means sudo usermod -aG docker alloy. Do it knowingly: Docker's documentation reminds you that the docker group grants root-level privileges. Your log agent effectively becomes an administrator of the machine.
You can also run Alloy itself in a container, with the grafana/alloy image, the file mounted on /etc/alloy/config.alloy and the --storage.path=/var/lib/alloy/data argument on a persistent volume. Mounting the socket as :ro does not limit calls to the Docker API: a socket is still a socket. And for host metrics, the container must receive the machine's filesystem, /proc and /sys as mounts.
On a single VPS, the systemd package remains the simplest to audit.
Migrating from Promtail: the real risk is positions
Promtail keeps a positions file that records, for each tracked file, where it stopped reading. Alloy stores its own elsewhere, in the folder set by --storage.path. The conversion knows this: the loki.source.file block produced by alloy convert carries a legacy_positions_file argument that points to the old file, so reading resumes at the right place.
This mechanism has a documented weak spot. In grafana/alloy issue #5493, a user describes the error error reading legacy positions file followed by permission denied: Alloy could not read the old file, ignored it and re-sent every log on the server from the beginning. The maintainer considered it legitimate to stop Alloy in that case, and the issue is closed, but do not bet on your version: check permissions before the first start.
The other risk is the opposite: Promtail and Alloy running together send every line twice. The order below avoids both.
Migrate a Promtail configuration without loss or duplicates
Locate the configuration and the positions file
Open the Promtail configuration, often
/etc/promtail/config.yml, and note the value ofpositions.filename. In a Docker Compose install, this file lives in a volume: note its real path on the host.Convert with a report
alloy convert --source-format=promtail --report=/tmp/promtail-report.txt --output=/tmp/promtail.alloy /etc/promtail/config.ymlRead the report first. The
--bypass-errorsoption forces a partial conversion, but the documentation warns that behavior may then differ from the original.Check that the alloy user can read the old file
grep legacy_positions_file /tmp/promtail.alloysudo -u alloy cat /var/lib/promtail/positions.yamlAdapt the path to the one noted above. If the second command fails, copy the file into a folder readable by
alloyand fix the path in the converted configuration.Stop Promtail before starting Alloy
sudo systemctl stop promtail, ordocker compose stop promtailfor a container stack. Promtail then writes its last positions, and no line is sent twice during the switch.Switch the configuration
Copy the converted blocks into
/etc/alloy/config.alloy, runsudo alloy validate /etc/alloy/config.alloy, thensudo systemctl restart alloy. Immediately checksudo journalctl -u alloy | grep -i positions: noerror reading legacy positions fileline should appear.Monitor for a week, then uninstall
Compare log volume in Grafana before and after the switch, and follow Alloy's
loki_write_sent_entries_totalcounter. Keep the Promtail configuration a few days, thensudo systemctl disable promtailand remove the package or the Compose service.
Promtail and Alloy, side by side
Scroll the table
| Criterion | Promtail | Grafana Alloy |
|---|---|---|
| Status | End of life since March 2, 2026, no more updates | Actively developed, successor designated by Grafana |
| Signals collected | Logs only | Logs, metrics, OTLP traces and profiles |
| System metrics | node_exporter to install separately | Built-in `prometheus.exporter.unix` component |
| Configuration | YAML | Alloy component syntax, conversion with `alloy convert` |
| Read positions | File set in the configuration | `--storage.path` folder, resumed through `legacy_positions_file` |
| Debugging | Service journal | Service journal and local UI on port 12345 |
| Service user | Depends on the install method | `alloy`, `adm` and `systemd-journal` groups added by the package |
Post-install configuration: UI, secrets and storage
The debugging interface listens on 127.0.0.1:12345 by default. Leave it that way and open it through a tunnel: ssh -L 12345:127.0.0.1:12345 [email protected], then http://localhost:12345 in your browser. To expose it anyway, add --server.http.listen-addr=0.0.0.0:12345 to CUSTOM_ARGS, but only behind a firewall that filters that port.
Launch options live in /etc/default/alloy on Debian and Ubuntu, and in /etc/sysconfig/alloy on AlmaLinux and Rocky. That is also where you put secrets: declare a LOKI_PASSWORD variable, read it in the configuration with sys.env("LOKI_PASSWORD"), and keep the password out of config.alloy.
For a remote Loki, never send plain HTTP over the Internet: put Loki behind a TLS reverse proxy with authentication, as described in our Loki guide. Finally, watch the space in /var/lib/alloy: if Loki is unreachable, that is where data waiting to be sent piles up.
Harden the service without breaking collection
The Alloy documentation proposes a drop-in file, created with sudo systemctl edit alloy so it survives package upgrades: NoNewPrivileges=yes, ProtectSystem=strict, ProtectHome=yes, PrivateTmp=yes and ReadWritePaths=/var/lib/alloy. With ProtectSystem=strict, add ReadOnlyPaths=/var/log/journal and ReadOnlyPaths=/run/log/journal, otherwise the journal source reads nothing. Finish with sudo systemctl daemon-reload and sudo systemctl restart alloy. Prefer a per-folder ACL to adding groups, and only grant the docker group if you actually collect containers.
Troubleshooting: the errors Alloy does not shout about
Most Alloy failures on a VPS are not crashes. The service is active, the interface is green, and Grafana stays empty. The reason is documented by the maintainers themselves in grafana/alloy issue #2994, closed in favor of #6340: a component's health means it is running, not that it is sending.
Two reflexes are enough to get out of this fog. Read the service journal with sudo journalctl -u alloy --since "10 min ago", and query the agent's internal metrics with curl -s http://127.0.0.1:12345/metrics | grep loki_write. The loki_write_sent_entries_total counter should rise; loki_write_dropped_entries_total counts lines dropped after every send attempt failed.
Error messages and fixes
- The journal source starts, but no line arrives — the
loki.source.journaldocumentation says it: without theadmandsystemd-journalgroups, the component starts without error and collects nothing. Fix it withsudo usermod -aG adm,systemd-journal alloy, then restart the service, because an added group only takes effect at the next launch. failed to tail the file: open /var/log/…: permission denied— the file exists butalloycannot read it. Grant an ACL on the folder withsetfacl -R -m u:alloy:rxand its-dvariant for files later created by rotation.- No message, no line for a whole folder — grafana/alloy issue #6542 describes logs never sent without any error. A maintainer confirms there that an unreadable file is logged, but an inaccessible folder is not. Test with
sudo -u alloy ls /var/log/monapp. error reading legacy positions file … permission denied— Alloy did not read Promtail's positions and may re-send everything from the beginning (issue #5493). Stop the service, make the file readable foralloy, then start again.failed to identify the read-write layer ID … mount-id: permission denied— an error from the cAdvisor component under systemd (issue #4068). Thedockergroup is not enough; users report that a drop-in withAmbientCapabilities=CAP_DAC_READ_SEARCHandCapabilityBoundingSet=CAP_DAC_READ_SEARCHsolves it without running the service as root. It is a community workaround, to test before rolling it out.- The UI is green, but Grafana stays empty — check
loki_write_sent_entries_totaland the test line written bylogger. If the counter stalls, the failure is upstream, on the source side.
An up-to-date agent on a server that belongs to you
Alloy needs what shared hosting does not give: a systemd service, read access to /var/log and the journal, sometimes the Docker socket. On a VPS, you choose your distribution, keep your Loki and Prometheus stack, and decide which permissions the agent gets. If you would rather not administer the system yourself, the VPS administration option exists for the server.
The official documentation remains the reference for each component: migrating from Promtail, Linux permissions and the Promtail end-of-life page. For application tracing, continue with our Tempo and OpenTelemetry guide.