On March 30, 2026, I made the first commit on a personal finance dashboard. Twelve days and 132 commits later, it had grown into a self-hosted app with roughly 45,000 lines of TypeScript, 27 pages, 30 API route files, and 40+ database tables. Every commit was co-authored with Claude Code, but the part that mattered most was not speed for its own sake. It was finally being able to build the finance tool I actually wanted to use every day.

This is not a toy demo. It runs on my own Linux server, syncs real financial data every four hours, tracks budgets and net worth, forecasts cash flow, imports Amazon orders, and generates reports and alerts. The screenshots in this post are from the app’s demo mode with synthetic data, but the architecture and workflow are the real thing.

Why I built it

I had been bouncing between spreadsheets, Mint before it disappeared, and too much manual tracking. None of it gave me a view of the whole system: checking and credit accounts, investments, assets, liabilities, budgets, recurring bills, and business activity all in one place.

I also wanted control.

Most commercial tools either made tradeoffs I did not like, required handing over sensitive financial data to another service, or locked me into somebody else’s product decisions. I wanted a dashboard I could shape around my own categories, rules, reports, and workflows, and I wanted it to live on infrastructure I control.

What shipped in 12 days

The final app ended up much broader than the original dashboard idea.

  • Household finance dashboard with income, expenses, budgets, net worth, and recent transactions
  • Transaction management with categorization, split transactions, amortization, and merchant-level cleanup
  • Investments, assets, liabilities, and allocation tracking
  • Cash flow forecasting and recurring bill visibility
  • Monthly reports, tax estimates, and export workflows
  • Business tracking for side income and expenses
  • Alerts, scheduled data sync, and AI-assisted insights

The result is the kind of homepage I had wanted for years: one screen that tells me where money is going, what changed recently, and what needs attention next.

Finance dashboard overview showing income, expenses, net cashflow, liquid net worth, budgets, and category spending.

The stack I landed on

I kept the stack straightforward on purpose.

Frontend: React 19, Vite, Tailwind CSS, Radix UI, TanStack Query, TanStack Table, and Recharts. It is also a PWA, so it works cleanly on mobile without forcing a native app path.

Backend: Node.js 22, Express, PostgreSQL, and a fairly traditional REST API. The server handles ingestion, categorization, analytics, reports, alerts, and the app’s configuration surface.

Infrastructure: Docker multi-stage builds, GitHub Actions, GitHub Container Registry, a self-hosted Linux server, Windmill for scheduled jobs, and Apprise for notifications.

This is all packaged as a TypeScript monorepo with npm workspaces. One install at the root, one build pipeline, one Docker image, and a deployment model simple enough that I can still reason about it after a long workday.

The data pipeline matters more than the UI

The UI is the fun part, but the real project lives underneath it.

I use Tiller as the ingestion layer because it handles the hard parts of bank connectivity and pushes clean transaction data into Google Sheets. From there, scheduled jobs pull banking, credit, and investment data into PostgreSQL every four hours. That gives me a data flow that is automated enough to trust but still understandable enough to debug.

Once the transactions hit the database, they move through a few layers:

  1. Rule-based categorization for common merchants and patterns
  2. LLM-assisted categorization for everything the rules miss
  3. Split transactions for mixed purchases
  4. Amortization for bigger one-time items
  5. Subscription and recurrence detection for ongoing charges

That pipeline turned out to be the heart of the application. If transaction ingestion, categorization, and deduplication are messy, the rest of the dashboard becomes decoration.

Transactions view with search, filters, categorization controls, and bulk-friendly finance workflows.

Budgets were where that pipeline started paying off in a visible way. Once transactions were categorized, split, amortized, and rolled into recurring patterns correctly, the budget screen stopped feeling like a glorified spreadsheet and started feeling like an actual operating view.

Budgets and goals view showing budgeted amounts, actual spending, and category-level health at a glance.

I also wanted to see how money was actually moving through the system, not just static balances and category totals. The money-flow view made it much easier to see where income lands, which accounts are doing the heavy lifting, and where spending is concentrated.

Money flow view showing how income moves through accounts into major spending categories.

Claude Code changed the speed, not the responsibility

This was the part that surprised me most.

Claude Code did not replace engineering judgment. I still made the architectural calls, picked the stack, designed the schemas, and decided which features were worth keeping. What changed was the distance between “I know what I want” and “the implementation exists and mostly works.”

A feature cycle often looked like this:

  • I describe the feature and the constraints
  • Claude writes the migration, API route, service logic, and UI scaffolding
  • I review the design, fix what is off, and push it toward the actual product shape I want
  • Claude iterates quickly on the parts that would normally eat a whole evening

That was especially useful for the boring but necessary work: database migrations, validation, repetitive CRUD paths, alert plumbing, parsing edge cases, and test setup. The real risk was the opposite problem. Once feature implementation gets cheap, it becomes much easier to overbuild.

The feature areas that became genuinely useful

Some parts of the app are neat. A few became essential very quickly.

Assets and liabilities were one of those areas. It is surprisingly hard to get a single picture of liquid accounts, investments, property, vehicles, and debt without manually stitching multiple tools together. Having it all in one place makes net worth tracking much more useful.

Assets view showing real estate, vehicles, and precious metals alongside current values.

Recurring bills and forward-looking cash flow also became much more useful than I expected. Looking backward is helpful, but knowing what is already committed over the next few weeks is what actually changes behavior.

Recurring bills calendar showing upcoming charges and monthly commitments.

The forecast view turned that into something concrete. Instead of staring at today’s balance and guessing, I can see how recurring income and expenses are likely to move the floor over the next 30, 60, or 90 days.

Cash flow forecast view showing projected balances, income, expenses, and the lowest expected point over the next 90 days.

Business tracking also earned its place quickly. I wanted side consulting income and business-related transactions to coexist with the household view without becoming a second disconnected tool or spreadsheet.

Business tracking view showing consulting income, tagged transactions, and net profit for a side business.

One unexpectedly useful area was Amazon order reconciliation. Once the app can connect transactions to actual purchased items instead of just merchant names, it becomes much easier to understand what a big Amazon total actually represented.

Amazon orders import and reconciliation view for matching purchases to financial transactions.

The reporting and export-oriented views also turned out to matter more than I expected. A dashboard is helpful in the moment, but reusable summaries, budget exports, subscription audits, and shareable point-in-time reports are what make a finance tool useful over time.

Exports view showing reusable financial report outputs including monthly summaries, tax summaries, subscription audits, and spending comparisons.

Self-hosting was the right tradeoff

This runs on a small Linux server at home behind Tailscale, which keeps the app private without forcing me into public exposure, port-forwarding, or extra app-facing network complexity.

The production flow is intentionally simple:

  • GitHub Actions runs tests and builds the image
  • the image gets pushed to GitHub Container Registry
  • the server pulls and restarts
  • container startup runs migrations automatically

Windmill handles the scheduled jobs, so banking sync, investment sync, and alert evaluation run on a predictable cadence with logs and manual rerun support when I need it. The app’s own sync page surfaces that status so I do not have to jump between tools to see whether data is fresh.

Data sync view showing scheduled banking, investment, and alert jobs with recent status history.

What I would do differently

Three things stand out.

I would design the data model more deeply before building so many pages. A few schema decisions evolved around UI needs instead of the cleanest long-term model.

I would write tests earlier. The test suite came later than it should have. Claude is good at writing tests when asked, but that still requires asking at the right time.

I would be stricter about scope. Once an AI coding assistant can build a new feature quickly, the dangerous thing is not whether the feature is possible. It is whether you actually need it.

So, is this a good idea?

If you are a developer who cares about controlling your financial data, likes self-hosting, and wants a finance workflow shaped around your own needs, I think the answer is yes.

The bigger lesson for me is not just that I built a finance dashboard in 12 days. It is that projects like this have moved from “interesting someday side project” to “realistically buildable” when you combine a modern full-stack setup, a few good infrastructure choices, and an AI coding assistant that can carry a lot of the implementation load.

Claude Code did not remove the need to know what I was doing. It removed a lot of the drag between an idea, a design, and working software. For a project like this, that changes the economics of building your own tools in a very real way.


Kevin Kinnett is a software engineer building personal tools with TypeScript, PostgreSQL, self-hosted infrastructure, and AI coding assistants. He writes about AI-assisted development, software architecture, and the systems that are worth owning yourself.