NJ Naman Jain ← Back
Work / Bahnscanner
Case study  ·  02

Bahnscanner — a cheaper way to search Deutsche Bahn, hosted on a Raspberry Pi in my flat.

A self-hosted fare scanner for the German rail network. Search a single route, or pick Everywhere and let it rank every reachable station by price across a whole flexible month.

Bahnscanner web app: a search bar with From / To / When / Travellers fields, filter chips for ICE, IC/EC, Regional and Night, and three tip cards below explaining the Everywhere feature, BahnCard discounts and round-trip flexibility.

Fig. 01 The search interface at bahnscanner.de. The killer feature: pick Everywhere as a destination, then a flexible month, and the scanner returns the cheapest city you can reach on any day of that month.

~2,500 Monthly users
1 Raspberry Pi 5
€0 Cloud bill
24/7 Uptime, from my flat
i.

The itch

The DB website tells you the price of your route, on your dates. What it doesn't tell you is: where is the cheapest place I can go from Berlin next month, on any day?

That second question is the one I actually ask. A spontaneous weekend trip. A cheap excuse to visit a new city. A flexible Friday-to-Sunday for €19 each way, somewhere I haven't been. DB's search makes you guess a destination first; I wanted the destination to fall out of the search.

ii.

What it does

  1. 01

    Single-route search, but better

    Pick a From and To pair like any other fare tool. Add filters for ICE, IC/EC, Regional or Night services. Optionally apply a BahnCard 25 or 50 discount across every result.

  2. 02

    Everywhere mode

    Set the destination to Everywhere and the engine fans out across every reachable station from your origin, returning a price-ranked list. Pair it with a flexible month and you see the cheapest reachable city across that whole window.

  3. 03

    Flexible-month round trips

    For a single destination, set both outbound and return to flexible months and the scanner computes round-trip totals for every day-pair, surfacing the cheapest combinations.

  4. 04

    Coverage map

    A separate view shows which stations the scanner currently knows about and how stale the cached fares are.

Demo video Walkthrough of Bahnscanner in action — coming soon

Fig. 02 Placeholder for a short screen recording of Bahnscanner — searching a route, switching to Everywhere mode, and watching the cheapest cities surface.

iii.

Hosted on a Pi

The whole site — frontend, API, scheduler, fare cache — runs on a single Raspberry Pi 5 sitting on my desk.

Around 2,500 people a month hit bahnscanner.de, and every request is served from that little board in Berlin. A Cloudflare tunnel exposes it to the open internet so I don't need to punch holes in my router, and the only running cost is the few watts it pulls from the wall.

Self-hosting on a Pi forced a kind of discipline I enjoy: every dependency justified, every background job small enough to recover from a power blip, every cache entry sized to fit on an NVMe SSD without burning through write cycles.

A Raspberry Pi 5 on a wooden desk with a USB-C power cable plugged in, flanked by two toy cars.
Fig. 03 The whole stack lives on this. A Raspberry Pi 5 in my flat, scraping fares overnight and serving the UI to the open internet. The toy cars are not load-bearing.
iv.

How it's built

A Python backend talks to Deutsche Bahn's APIs, caches fares aggressively in a local store, and exposes a FastAPI service. A small React frontend renders the search UI and the tips inline. Everything is containerised with Docker Compose so a fresh Pi can be brought up with one command.

The interesting engineering is in the Everywhere search: a naive implementation would issue hundreds of fare queries per session and get rate-limited within seconds. Instead, the scanner runs scheduled batch jobs overnight, caches results with a short freshness window, and serves from cache for the interactive search. The user sees a sub-second response; the work happens off-peak.

Frontend React · Vite · static, served by the Pi
API FastAPI · uvicorn behind nginx
Scheduler Cron-driven overnight scrapers + freshness windows
Cache SQLite on NVMe · TTL'd fare snapshots
Edge Cloudflare Tunnel · TLS · DDoS shielding
Ops Docker Compose · systemd · GitHub Actions deploys
v.

What I'd do next

Bahnscanner answers where cheaply, but not how. I'd like to add multi-leg journeys, IC bus alternatives, and a weekly digest that quietly emails me the cheapest available weekends from my home station — the version of this tool that uses me rather than the other way around.