DEV Community

Eastkap
Eastkap

Posted on

The $0 Infra Budget Tracker: How I Serve Users With Literally Zero Server Costs

I built a budgeting app with zero backend infrastructure. No servers. No database. No hosting bill. No monthly recurring cost of any kind.

Zero. Dollars. Per. Month.

And I'm not talking about a free tier that'll hit its limits and surprise me with a $200 bill in three months. I mean architecturally zero -- there is no server-side code to run, so there's literally nothing to pay for.

Here's exactly how it works, and why I think more developers should be building this way.

The Stack

Monee is a static React app deployed on Vercel's free tier. The complete infrastructure picture:

  • Frontend: React + Vite (static files)
  • Storage: localStorage (the browser itself)
  • Hosting: Vercel (free tier, static sites don't hit compute limits)
  • Auth: None (nothing to authenticate against)
  • Database: None (your browser is the database)

That's it. No AWS, no Supabase, no Firebase, no Planetscale, no Redis, no anything.

How Data Persistence Works

// This is the entire "database layer"
const saveTransactions = (transactions) => {
  localStorage.setItem('monee_transactions', JSON.stringify(transactions));
};

const loadTransactions = () => {
  const stored = localStorage.getItem('monee_transactions');
  return stored ? JSON.parse(stored) : [];
};
Enter fullscreen mode Exit fullscreen mode

The browser's localStorage API gives you ~5-10MB of persistent key-value storage per origin, completely free, with zero latency (it's synchronous and local). For a budgeting app where most users track maybe a few hundred transactions per month, that's more than enough.

There's no API call. No network round trip. No CORS headers. No JWT tokens. No rate limiting. It just works.

CSV Import/Export Is the Sync Strategy

The obvious objection: "What about syncing between devices?"

Fair point. Here's how I handled it:

const exportToCSV = (transactions) => {
  const headers = ['date', 'description', 'amount', 'category'];
  const rows = transactions.map(t => 
    [t.date, t.description, t.amount, t.category].join(',')
  );

  const csv = [headers.join(','), ...rows].join('\n');
  const blob = new Blob([csv], { type: 'text/csv' });
  const url = URL.createObjectURL(blob);

  // Trigger download
  const a = document.createElement('a');
  a.href = url;
  a.download = `monee-export-${new Date().toISOString().split('T')[0]}.csv`;
  a.click();
};
Enter fullscreen mode Exit fullscreen mode

Users export a CSV, drop it in iCloud Drive or Google Drive, and import it on another device. Not seamless, but it works. And crucially, it means users own their data in a format that'll outlast the app itself.

The CSV approach also opens up a use case most fintech apps completely ignore: users who already use spreadsheets. They can export from their bank, import into Monee, do the visual work there, then export back. Monee slots into their existing workflow instead of demanding they change it.

The Security Angle That Actually Matters

Here's the thing most developers don't think about: you can't breach data you don't have.

With a traditional backend budgeting app, your users' financial data lives on your servers. That's a liability -- for them if you get breached, and for you if you're subject to data protection regulations.

With localStorage-only architecture, I genuinely cannot access user data. There's no database to subpoena. There's no breach that would expose transaction histories. The data literally does not leave the user's machine unless they choose to export it.

This isn't just a privacy selling point. It's architectural. The security properties come for free from the design.

What You Actually Can't Do

I want to be honest about the tradeoffs. This architecture cannot:

  • Sync automatically across devices -- requires intentional export/import
  • Store data beyond ~10MB -- fine for personal budgeting, wrong for business use
  • Share budgets with other users -- no shared state, no server
  • Send email reminders -- no backend, no cron jobs
  • Provide bank account linking -- Plaid requires a server to hold OAuth tokens

If your users need any of those things, you need a backend. I'm not pretending otherwise.

But for personal budgeting -- which is most of the market -- none of those features are actually required. They're features added because they're technically possible, not because they make the core job-to-be-done better.

The Developer Experience Bonus

Zero infrastructure means zero ops burden:

# Entire deployment pipeline
npm run build
# Vercel auto-deploys from GitHub push
# Done.
Enter fullscreen mode Exit fullscreen mode

No Docker containers. No environment variables to manage (there are none). No database migrations. No server restarts. No uptime monitoring. No pagerduty alerts at 3am.

I spend zero time on infrastructure because there is no infrastructure to maintain. Every hour that would have gone to DevOps goes to product instead.

What I'd Do Differently

If I could go back, I'd add one thing: IndexedDB as a fallback.

localStorage has a 5-10MB limit that varies by browser. IndexedDB has a much larger limit and better async performance for large datasets. The migration path is straightforward, and the trade-off is just API complexity.

// Future version: IndexedDB for larger datasets
const db = await openDB('monee', 1, {
  upgrade(db) {
    db.createObjectStore('transactions', { keyPath: 'id' });
  },
});
Enter fullscreen mode Exit fullscreen mode

But for v1? localStorage is fine. Ship fast, optimize later.

The Point

The instinct in web development is to reach for a backend the moment you start thinking about persistence. "Where will the data live?" immediately becomes "I need a database, which means I need a server, which means I need auth, which means I need..."

Sometimes the answer is: the data lives in the user's browser. That's a valid architectural decision, not a cop-out. For the right use cases, it's actually the correct choice -- cheaper, simpler, more private, and easier to maintain.

Monee is open source. If you're building a similar tool, steal the approach.

Top comments (0)