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.
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.
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.
What it does
-
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.
-
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.
-
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.
-
04
Coverage map
A separate view shows which stations the scanner currently knows about and how stale the cached fares are.
Hosted on a Pi
The whole site — frontend, API, scheduler, fare cache — runs on a single Raspberry Pi 5 sitting on my desk.
Around 5,000 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 SD card without burning through write cycles.
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.
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. Two caching strategies keep the API responsive. First, user-search caching: if user A searches a route, user B's identical query for the next 30 minutes is served straight from the cache. Second, a warm-cache sidecar continuously refreshes results for popular routes and big-city pairs — Berlin, Munich, Frankfurt, Hamburg — so the most common queries are already hot when they arrive. The user sees a sub-second response; the work happens in the background.