Self-hosted service
BetterBahn/betterbahn avatar
BetterBahn/betterbahn

BetterBahn: split ticketing for German train journeys, and the DB endpoint it depends on

BetterBahn is an open-source project that aims to improve the train travel experience in germany. The current focus is on split-ticketing. However, further functions are planned to follow in the future.

2,532 stars186 forksTypeScriptAGPL-3.0

At a glance

What is it?
BetterBahn is a Next.js web app that queries Deutsche Bahn ticketing data through db-vendo-client and can split a journey into cheaper legs. It is AGPL-3.0-only, and the README warns that a DB-side change currently breaks it with ENOTFOUND app.vendo.noncd.db.de.
Who is it for?
BetterBahn is worth adopting if you want to read or extend a TypeScript split-ticketing implementation and you accept that it talks to an unofficial Deutsche Bahn interface. It is not a good choice if you need a supported booking channel, or if you expect split ticketing to fire on most searches, since the README calls it the exception rather than the rule.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 21 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem BetterBahn targets: one ticket, one price, one route

German long-distance fares are priced per connection, not per kilometre. A direct ICE from A to B can cost more than two regional legs that cover the same track, and the booking interface will not offer you the second option because it only prices the connection you asked for. BetterBahn exists to search for train journeys in Germany and to try split ticketing, meaning it breaks a trip into separate legs and prices them independently in the hope that the sum is lower than the direct fare. The README is honest about the payoff: split ticketing is supported, but it is "rather the exception" than the rule. That single sentence should shape your expectations more than anything else on the project's website. The audience is therefore narrow and specific: people who travel German rail often enough to care about fare construction, and developers who want a TypeScript codebase they can read and modify. It is not aimed at someone who wants a polished booking app on their phone. The README also states that further functions are planned, and that split ticketing is the current focus, so treat everything else as roadmap rather than capability.

How the Next.js app reaches Deutsche Bahn ticketing data

The architecture is a Next.js app in the app/ and components/ directories, with tRPC as the client-server layer. The package.json lists @trpc/server, @trpc/next, @trpc/client and @trpc/tanstack-react-query, alongside @tanstack/react-query and superjson for serialisation. That combination means the browser does not talk to Deutsche Bahn directly: it calls tRPC procedures, and the server side does the upstream work. Upstream access comes from db-vendo-client, pinned at ^6.10.8, which the README describes as the library used "for accessing Deutsche Bahn ticketing data" and which is licensed under the ISC License. Station lookups use db-hafas-stations at ^2.0.0. The Dockerfile gives away something the README does not spell out: the standalone build does not include everything the app needs at runtime, so the image copies node_modules/.pnpm/[email protected] back in with an explicit comment that "some data files used at runtime are not included in standalone". That is a real deployment constraint, not a cosmetic detail. If you build the app your own way, expect to reproduce that copy step. Routing is Next.js App Router era code (next 16.2.11, react 19.2.x), and linting is oxlint with a .oxlintrc.json rather than ESLint.

Installing BetterBahn and running a first search on localhost:3000

The README lists Node.js, pnpm and git as prerequisites, and gives two ways to get pnpm. Corepack ships with Node.js, so the first option is to enable it and activate the latest pnpm. Note that the repository pins [email protected] in package.json and in the Dockerfile, so the lockfile expects that major line.

bash
corepack enable
corepack prepare pnpm@latest --activate

The alternative is a global npm install. The README writes the version range as pnpm@latest-10, which is worth reading carefully: it names a major version, while the repository's own packageManager field says 11.1.1. If you take this route, check which pnpm you actually ended up with before installing dependencies.

bash
npm install -g pnpm@latest-10

With pnpm available, clone the repository and install. The README's clone line points at github.com/l2xu/betterbahn, not at the BetterBahn organisation path, so use the URL exactly as written.

bash
git clone https://github.com/l2xu/betterbahn.git
cd betterbahn
pnpm install

Start the development server and open the URL the README gives. The app is served on port 3000.

bash
pnpm run dev

If you prefer containers, a Dockerfile is included and the README also documents docker compose. The compose command uses a project directory flag, which matters because the compose file lives in a subdirectory.

bash
docker compose -f docker-compose/docker-compose.yaml --project-directory=./ up -d

The README points at a wiki page, Installation-documentation---Dokumentation-zur-Installation, for per-platform detail. It does not document what a successful search looks like, so the first thing to verify after startup is whether the app can reach Deutsche Bahn at all. Given the warning at the top of the README, that is not a formality.

The ENOTFOUND failure and what it means for self-hosting

The most important paragraph in the README is a warning block. It states that Deutsche Bahn appears to have introduced a change that prevents BetterBahn from working, producing the error ENOTFOUND app.vendo.noncd.db.de, and it links to issue 225 for details. Read that literally: the failure is a DNS resolution failure against a Deutsche Bahn hostname, which means the app is not merely degraded, it cannot fetch ticketing data. Split ticketing, fare comparison and journey search all depend on that upstream, so a single hostname change takes the whole product down. This is the structural risk of building on db-vendo-client. The library is not an official Deutsche Bahn product, the README's legal notice says using the code or db-vendo-client requires permission from Deutsche Bahn AG, and the project is explicitly not affiliated with or endorsed by Deutsche Bahn. So you have an unofficial dependency, on an unofficial interface, with no compatibility guarantee. The README does not document rollback, a fallback data source, or a configuration switch to point at a different host. If you self-host BetterBahn, you are self-hosting that dependency risk too. A second limitation is scope: the README frames split ticketing as the exception, so even when the upstream is healthy, do not expect it to change most of your searches.

BetterBahn compared with DB Navigator and Bestpreissuche

The obvious alternative for most travellers is DB Navigator, Deutsche Bahn's own app, or the Bestpreissuche on bahn.de. The difference is not interface polish, it is who prices the journey. DB Navigator prices the connection you ask for and sells you a ticket for it. BetterBahn does not sell anything: it queries ticketing data through db-vendo-client and then applies its own search, including the split-ticketing pass, before showing you a result. That extra step is the entire reason the project exists, and it is also why the project can break when Deutsche Bahn changes a hostname while DB Navigator keeps working. The trade-off is straightforward. With DB Navigator you get an official channel, a booking flow and no maintenance burden. With BetterBahn you get a codebase you can inspect, modify and self-host under AGPL-3.0-only, plus a fare search that DB's own tools do not perform, at the cost of running and patching an unofficial integration yourself. If your goal is simply to buy a ticket, BetterBahn is the wrong tool. If your goal is to understand or extend how split ticketing is implemented in TypeScript, it is one of the few open projects in that space for German rail.

Licence, maintenance and the cost of keeping it running

BetterBahn is licensed AGPL-3.0-only, stated in both the README and the package.json license field. The practical consequence for anyone deploying it: if you run a modified version as a network service, the AGPL's source-availability condition applies to your users, which is a heavier obligation than a permissive licence. That is a description of the licence, not legal advice; if you plan to host it publicly, read the LICENSE file and get your own advice. There is a second licence layer to track: db-vendo-client is ISC-licensed, and the README's legal notice says permission from Deutsche Bahn AG is necessary to use this code or that library. Two separate obligations, from two separate sources. On maintenance, the last push to the repository was on 2026-09-11, which is recent, and the repository is not archived. The upgrade cost is dominated by the upstream, not by npm. Dependency bumps are routine pnpm work, but the failure mode that matters is a Deutsche Bahn hostname change, and no lockfile update fixes that. Budget for reading issue 225 and for the possibility that the fix is a code change in how the app resolves or calls the upstream service.

What to check before you commit to BetterBahn

Start with connectivity. Confirm that app.vendo.noncd.db.de resolves from the machine that will run the app, because the README's warning says it currently does not, and everything else is moot until it does. Then confirm the toolchain: package.json pins [email protected] and the Dockerfile installs exactly [email protected] with a frozen lockfile, while the README's npm fallback mentions a different major version. Pick one and be consistent, or you will be debugging lockfile drift instead of fares. If you deploy the standalone build, replicate the Dockerfile's copy of node_modules/.pnpm/[email protected], since the file itself notes those runtime data files are missing otherwise. Finally, decide what you are adopting. As a fare tool it is a bet on an unofficial upstream that is currently broken. As a reference implementation of split ticketing in a Next.js and tRPC codebase, it stands on its own, and the AGPL-3.0-only terms are the main thing to weigh before you build on it.

Editorial conclusion

BetterBahn is worth adopting if you want to read or extend a TypeScript split-ticketing implementation and you accept that it talks to an unofficial Deutsche Bahn interface. It is not a good choice if you need a supported booking channel, or if you expect split ticketing to fire on most searches, since the README calls it the exception rather than the rule. Before you install it, check issue #225 and confirm whether app.vendo.noncd.db.de resolves from your network, because that single failure takes the whole app down.

Frequently asked questions

What is BetterBahn?

It is an open-source web app for finding the best train journeys in Germany, with split ticketing as its current focus. The README states that further functions are planned for the future.

How do I install and run BetterBahn locally?

Install Node.js, pnpm and git, clone the repository, run pnpm install, then run pnpm run dev and open http://localhost:3000. The README also documents a Dockerfile and a docker compose setup.

Why does BetterBahn fail with ENOTFOUND app.vendo.noncd.db.de?

The README carries a warning that Deutsche Bahn appears to have introduced a change preventing BetterBahn from working, producing that error, and points to issue 225 for details. The underlying cause is that the app cannot resolve that Deutsche Bahn hostname.

Is BetterBahn an official Deutsche Bahn product?

No. The README's legal notice states it is an independent project, not affiliated with or endorsed by Deutsche Bahn, and that permission from Deutsche Bahn AG is necessary to use this code or db-vendo-client.

Is split ticketing available on every BetterBahn search?

No. The README says split ticketing is supported to help save money but that it is rather the exception, not the rule.

Official sources

  1. BetterBahn/betterbahn on GitHub
  2. Issues
  3. License: AGPL-3.0
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/betterbahn-betterbahn.svg)](https://hysenlabs.com/projects/betterbahn-betterbahn)