Full-Stack · Data· Self-hosted · private

Self-Hosted Finance Dashboard

Private · self-hosted
The Problem

Most budgeting apps make you choose: link your bank and trust a third party with your financial life, or track spending by hand and give up on automation. I wanted the automation (auto-categorized spending, a real budget, a debt-payoff plan) without handing raw bank credentials to anyone but the bank’s own aggregator.

What I Did

I built a self-hosted finance dashboard where Plaid handles the bank connection and the app never sees a login, only encrypted access tokens. Transactions sync via Plaid’s cursor-based endpoint, so ingestion is incremental and idempotent instead of a nightly full re-pull, and a signature-verified webhook triggers a sync the moment a transaction posts.

From there the app turns raw transactions into decisions: auto-categorized spending, recurring-charge detection (Plaid’s ML product plus a custom estimator for variable usage-based bills it can’t peg), a budget generated from income and spending with surplus auto-routed to debt then savings then investing, and an avalanche/snowball payoff planner that simulates APR month by month to a payoff date.

I also extended it past the web app with a Discord bot for quick manual logging (DM it “dinner out 45” and it parses amount, category, and date into the same backend), and a native iOS/Android app in Expo with Face ID unlock and push alerts for large charges. Backend, frontend, bot, and mobile client all share one authenticated API.

Auto-generated budget view Debt-payoff planner: avalanche/snowball simulation

Screens shown with sample data.

Stack
Plaid
Bank/card/investment/debt aggregation via Transactions Sync, Recurring Transactions, and webhooks; the app only ever holds encrypted access tokens, never credentials.
FastAPI + SQLAlchemy
Python backend with cursor-based idempotent sync, Fernet token encryption, and JWT auth enforced in production.
React (Vite) + Tailwind
SPA for spending, auto-budgets, net worth, and the debt-payoff planner, charted with Recharts.
Postgres
Primary datastore (SQLite for local dev).
Discord bot (Python)
Natural-language expense logging that writes straight into the same transaction pipeline as Plaid.
Expo / React Native
iOS/Android client with Face ID unlock, push notifications, and the core dashboard views.
Docker Compose + Caddy + GitHub Actions
Self-hosted on a DigitalOcean droplet; Caddy auto-provisions HTTPS; push-to-deploy on main.
← All work