Skip to content
pnowyPublic

About

Simple, self-hosted expense tracker with a beautiful UI.

Topics

Resources

Stars

9 stars

Watchers

1 watching

Forks

Repository files navigation

Sknerus Logo

Sknerus

CI Publish Latest Release License

Why Sknerus? • Quick Showcase • Features • Screenshots
Installation • Configuration • Demo Data


Sknerus is a self-hosted personal finance tracker focused on clarity and speed. Track income and expenses across custom categories, visualize spending trends over time, and get a clear picture of your cashflow — all from a clean, modern interface.

💰 Sknerus (pronounced skneh-roos) is Polish for Scrooge — a penny-pinching miser who knows exactly where every złoty goes. If you're obsessive enough about your finances to self-host a tracker, the name fits.

Sknerus App Preview


Why Sknerus?

The original inspiration for this project was ExpenseOwl — a simple self-hosted expense tracker. However, it is no longer actively developed and maintained, and it lacked a few things I needed: multi-chart dashboards, multi-currency support, and a richer data visualization experience.

Sknerus picks up where ExpenseOwl left off. It keeps the philosophy of dead simple, self-hosted, single-user or family expense tracking, while adding:

  • A proper analytics dashboard with multiple chart types and time-range navigation
  • Table view with grouping and sorting
  • Full recurring expense management (create, edit, delete templates)
  • Multi-currency support with automatic exchange rate resolution
  • A modern SSR React stack (TanStack Start)

It is still not a budgeting app. No accounts, no complex budgets, no bank syncing. Just a fast, honest look at where your money goes.

Quick Showcase

Sknerus Quick Showcase

Features

Core Functionality

  • Add income and expenses with category, date, amount, currency, optional tags, and free-text notes
  • Toggle tags and notes on or off globally from Settings — disabled fields disappear from forms, tables, and cards if you don't use them
  • Custom categories with color coding — reorder, rename, or remove them at any time; mark a category as income to preset the income toggle when adding a transaction
  • Recurring transactions — define a template once, the app materializes entries automatically up to today
  • Multi-currency support: record transactions in any currency, configure supported currencies, and exchange rates are resolved and cached automatically. Two providers available: Frankfurter (31 currencies, no key needed) and ExchangeRate-API (165 currencies, free API key required)
  • Export and import — bring your data in from any tool or take it anywhere, also from ExpenseOwl
  • Configurable fiscal month start date (e.g. set to 5 to count from the 5th of each month)
  • Configurable default start page (dashboard or table view)
  • Floating add-expense button on mobile — placed in thumb reach, switchable between bottom right, bottom left, or off
  • Light and dark theme with system-preference detection
  • PWA — installable on desktop and mobile for a native-app feel

Dashboard

The dashboard is the heart of the app, organized into five tabs (on phones they become a swipeable row of chips):

  1. Breakdown — pie charts of spending by category and income by source for the selected period, plus cashflow cards showing total income, total expenses, and net balance. Use the eye icon next to a category to temporarily exclude it from the chart.
  2. Monthly — stacked bar chart of expenses across all months, broken down by category, to spot seasonal patterns.
  3. Trends — category-level trend lines across months to see which spending areas are growing or shrinking.
  4. Income vs Expenses — bar chart comparing income and spending month by month, giving a quick cashflow health view over time.
  5. Cashflow — Sankey diagram of how income flows into expense categories, with each node's amount and share. Smaller categories are grouped into an expandable "Other" node.

All dashboard views support flexible time-range navigation: switch between monthly, quarterly, and yearly scopes and step forward/backward through time. Choose Custom to filter by an exact start and end date (both inclusive). Custom ranges are saved in the URL and preserved when switching dashboard tabs; click the displayed dates to edit them.

Table View

  • Chronological list of all transactions for the selected period
  • The same preset and custom date ranges as the dashboard
  • Optional search (enable Show search in Settings → Display): click the search icon to find transactions by name, notes, or tags across your whole history; Esc clears the query, then closes search
  • Toggle between flat list and grouped-by-category view
  • Inline delete with confirmation
  • Add new expenses directly from the table view

Settings

  • Add, rename, reorder, and delete categories (drag-and-drop ordering)
  • Set primary currency and a list of supported currencies for multi-currency entry
  • Configure fiscal month start date and default start page
  • Choose the side of the mobile floating add-expense button (or turn it off)
  • Toggle visibility of tags and notes fields across the app, and enable transaction search in the table view
  • Manage recurring transactions (view, edit, delete templates)
  • Export and import all expenses
  • Enable optional plugins (see below)

Plugins

Optional features that extend the core tracker. Toggle them on under Settings → Plugins — they stay invisible until you need them, so the app stays focused for users who only want basic expense tracking.

Vehicle expense tracking

Track per-vehicle costs alongside your regular expenses. Once enabled, a Vehicles tab appears in Settings where you can:

  • Register one or more vehicles (car or motorcycle) with engine size, fuel tank size, fuel type, and starting odometer
  • Bind a category to a vehicle so any expense in that category is automatically a vehicle expense
  • Pick an expense type per transaction — Fuel, Insurance, Oil change, Purchase, or Accessories — and customize the name, icon, and color of each type per vehicle (the icon/color shows up next to the expense name in tables and cards)
  • Record fuel fills with liters, odometer reading, and post-fill tank level to compute real-world L/100km consumption
  • Set oil-change intervals in both kilometers and months — the app warns when either threshold is approaching, whichever comes first
  • Track insurance and technical inspection expiry dates with status indicators (ok / warning / critical)
  • Open the Stats dialog on a vehicle for a fuel history table and a weekly spend-breakdown chart across all expense types

Units are metric: kilometers, liters, L/100km. Imperial units (miles, gallons, MPG) are not currently supported — open an issue if you'd like a metric/imperial toggle.

Screenshots

Click to expand screenshots
Page View
Desktop - Dashboard – Breakdown (light)
Desktop - Dashboard – Income vs Expenses (dark)
Desktop - Dashboard – Monthly (light)
Desktop - Dashboard – Trends (dark)
Desktop - Dashboard – Cashflow (light)
Mobile - Table view
Desktop - Table View
Desktop - Table View (group by category)
Desktop - Settings
Desktop - Vehicle Settings
Desktop - Vehicle Stats

Installation

The recommended way to run Sknerus is with Docker.

Use ghcr.io/pnowy/sknerus:stable to follow tagged releases. The latest tag follows builds from main; version tags are also available to pin a specific release.

Docker CLI

docker run -d \
  --name sknerus \
  -p 3000:3000 \
  -v sknerus-data:/app/.data \
  ghcr.io/pnowy/sknerus:latest

Docker Compose

services:
  sknerus:
    image: ghcr.io/pnowy/sknerus:latest
    restart: unless-stopped
    ports:
      - "3000:3000"
    volumes:
      - ./data:/app/.data
    environment:
      # Optional — seed demo data on first run (no existing data required)
      # SEED_DEMO_DATA: "true"

The app will be available at http://localhost:3000.

Building from Source

git clone https://github.com/pnowy/sknerus.git
cd sknerus
pnpm install
pnpm build
node .output/server/index.mjs

For development:

pnpm dev   # starts dev server at http://localhost:3000

Configuration

All configuration is done either via environment variables (infrastructure-level) or through the in-app Settings page (user-level).

Environment Variables

Variable Default Description
PORT 3000 Port the server listens on
DATA_DIR .data Directory where JSON data files are stored
SEED_DEMO_DATA (unset) Set to true to seed demo data on first run (see below)
LOG_LEVEL info Log verbosity: debug, info, warn, error
EXCHANGE_RATE_API_KEY (unset) API key for ExchangeRate-API (alternative to setting it in the UI)

REST API

The versioned REST API is also used by the web UI and is always available:

curl http://localhost:3000/api/v1/expenses

All endpoints are under /api/v1 and return JSON. Expense amounts are signed (negative for expenses, positive for income); date uses YYYY-MM-DD.

Method Endpoint Description
GET /expenses List expenses; optional from, to, categoryId, and tag query filters
POST /expenses Create an expense; currency defaults to the configured currency and tags defaults to an empty list
GET /expenses/:id Get one expense
PUT /expenses/:id Replace an expense
PATCH /expenses/:id Update supplied expense fields
DELETE /expenses/:id Delete an expense
GET /categories List categories
POST /categories/:id/rename Rename a category with { "newName": "..." }
GET, PUT /config Get or replace app configuration
GET, POST /recurring List or create recurring transactions
PUT, DELETE /recurring/:id Update or delete a recurring transaction
POST /recurring/materialize Create due recurring expenses
GET, POST /vehicles List or create vehicles
PUT, DELETE /vehicles/:id Update or delete a vehicle
GET /exchange-rate?date=YYYY-MM-DD&from=USD&to=PLN Resolve an exchange rate

The API uses the same JSON storage as the web app.

In-App Settings

Everything else is configured from the /settings page:

  • Categories — add, rename, reorder (drag-and-drop), and delete categories
  • Currency — set the primary display currency
  • Supported Currencies — currencies available when adding a transaction (exchange rates resolved automatically)
  • Exchange Rate Provider — choose between Frankfurter (31 major currencies, no setup) or ExchangeRate-API (165 currencies including MVR, THB, etc. — requires a free API key)
  • Start Date — day of month that begins a new fiscal period (default: 1st)
  • Start Page — which page loads first: dashboard or table view
  • Mobile Add Button — position of the floating add-expense button on phones: bottom right (default), bottom left, or off
  • Recurring Transactions — manage recurring income and expense templates
  • Export / Import — download all data as CSV or upload a CSV to import transactions. The import modal in the app describes the expected format and supported columns in detail.
  • Plugins — enable optional features such as vehicle expense tracking. See the Plugins section above for details.

Data Storage

Sknerus uses a simple file-based JSON storage by default. Data is stored in three files inside DATA_DIR:

  • expenses.json — all transactions
  • config.json — categories, currency, and app settings
  • recurring.json — recurring transaction templates
  • exchange-rates.json - cache for exchange rates data

Mount DATA_DIR as a persistent volume to retain data across container restarts.

Note: Sknerus does not include authentication (neither for the web UI nor for the REST API). Use a reverse proxy (e.g. Nginx, Caddy, Traefik) with basic auth or SSO in front of it if exposing beyond localhost.

Considered features

There is a bunch o potential features which could be added if there will interest. Some which I consider but don't need them in my use-case:

  • extra storage types (SQL Database – PostgreSQL)
  • authentication and authorization

Demo Data

Sknerus ships with a built-in demo data generator that seeds ~2.5 years of realistic family budget data. It is useful for exploring the app before entering your own data.

To enable: set SEED_DEMO_DATA=true and start with no existing data (or a fresh DATA_DIR).

The seed generates:

  • 17 categories: Salary, Freelance, Apartment Rental, Dividends, Savings Interest, Groceries, Housing, Utilities, Transport, Health, Entertainment, Kids, Dining Out, Clothing, Education, Subscriptions, Motorcycle
  • Income: primary salary (with annual raises), partner salary starting a few months in, December bonuses, occasional freelance income, monthly apartment rental, quarterly dividends, monthly savings interest
  • Fixed costs: rent, internet, streaming subscriptions, gym membership
  • Variable costs: groceries (3–4 trips/month), fuel, utilities with seasonal swings (higher in winter), dining out
  • Seasonal patterns: clothing peaks in spring/autumn, summer vacation in July, Christmas spending in December, school supplies in August/September
  • Sporadic entries: doctor visits, pharmacy, dental, car service, home maintenance
  • Vehicle plugin enabled with a Kawasaki Z900 SE motorcycle bought ~14 months ago: purchase, two annual insurance cycles, gear/accessories, ~25 fuel fills with odometer readings (consumption around 6 L/100km), and two oil changes — enough to populate the fuel-history table and the weekly spend-trend chart

Once data exists, the seed is skipped automatically on subsequent starts — safe to leave the flag enabled.

Show Your Support

If Sknerus saves you a few clicks (or a few złoty), consider:

No paid tier, no telemetry, no analytics — just an app I built for myself and shared. A star is the cheapest way to say thanks. 🙏

About

Simple, self-hosted expense tracker with a beautiful UI.

Topics

Resources

Stars

9 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages