A tiny local logs server that found the race I couldn't reproduce

How I set up a small Loki+Promtail server (₹300 VPS / Pi) to aggregate dev logs locally — the bug it found, the privacy snafu it caused, and the tradeoffs I learned.

Written by: Arjun Malhotra

A developer's desk with a laptop, notebook, and coffee cup, dimly lit
Photo by Blake Wisz on Unsplash

It was 9:30am. I had a client demo and a flaky feature that only failed on certain laptops. The app would hang for a few seconds, then resume. Remote tracing in prod showed nothing. The user’s machine logs were scattered: browser console, app logs, systemd journal, and an Electron crash dump. By the time I asked for logs, the user had restarted, lost the transient traces, and the bug vanished.

That was the third time this month. Enough to stop doing the usual “please send the logs” dance. I needed a way to collect everything a developer machine produced during a session, reliably and without burning mobile data or my time. So I built a tiny local logs server: Promtail on each dev machine shipping to a Loki instance I run on a cheap VPS (₹300/month) or an old Raspberry Pi (₹3,500). It’s small, fast, and caught the race condition that had been ghosting me.

Why local logs, not remote observability

What I actually built (and why it’s small)

The bug it found During a Wednesday demo, the app hung for ≈3s and resumed. Promtail had been running. I opened Grafana, filtered by demo-id and branch, and saw a burst of lines: the app tried to acquire a file lock, blocked on an NFS mount that had timed out, and then retried. The NFS mount was from a developer’s VM they’d shared; it was slow during lunch when our office NAS kicked in. Without a log timeline that combined systemd and app logs, I wouldn’t have connected the dot between the lock wait and the NFS latency spike. Fix: remove blocking locks and add a 200ms backoff.

The messy tradeoffs and one painful failure This is where I get honest: it’s not all wins.

  1. I once missed the event I needed because Promtail started after the process crashed. We relied on a systemd unit to start promtail on boot. One developer’s machine crashed and rebooted; their app never auto-started, but promtail did. The crucial short-lived crash log lived only in a rotated /var/log and the rotation happened mid-boot. Promtail’s journal reader didn’t pick up the rotated file and the crash trace was gone. I added a tiny cron that pushes rotated logs on reboot; kludgey, but reliable.

  2. Privacy and PII: I forgot to scrub stack traces for user data. A developer pushed demo logs to the VPS and they contained a screenshot filename with a customer’s phone number. The client understandably complained. I immediately added a regex-based scrubber in Promtail pipeline stages and made the VPS accept uploads only from known local IPs or via a short-lived token. Lesson: assume logs contain PII. Scrub early.

  3. Disk and cost: On a Pi or small VPS, disk fills up fast if you ingest verbose browser logs. I set a 48-hour retention, compressed chunks, and a per-host ingestion cap (100MB/day). That works for demos, but if you expect full fidelity for long-term debugging, this setup is not for you.

Why this scale fits small teams and solo devs

How I use it in practice (practical rules)

The limitations I accepted

What I walked away with Local aggregation reduced a recurring, stupidly common friction: “I can’t reproduce; send logs”. The combination of labels + short retention + a cheap host gives a practical middle ground between ad-hoc file dumps and enterprise observability. But it forced me to treat logs as sensitive data — and to engineer safeguards before I trusted them.

If you do this, start small: one host, 48-hour retention, a scrubber, and a demo-id. If you want, borrow my tiny Promtail pipeline (I keep a gist with the regex rules I use) — but don’t forget the rotating-log edge case. It will bite you exactly once, and exactly during a client demo.