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.
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.
- 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
The dashboard is the heart of the app, organized into five tabs (on phones they become a swipeable row of chips):
- 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.
- Monthly — stacked bar chart of expenses across all months, broken down by category, to spot seasonal patterns.
- Trends — category-level trend lines across months to see which spending areas are growing or shrinking.
- Income vs Expenses — bar chart comparing income and spending month by month, giving a quick cashflow health view over time.
- 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.
- 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
- 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)
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.
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.
Click to expand screenshots
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 run -d \
--name sknerus \
-p 3000:3000 \
-v sknerus-data:/app/.data \
ghcr.io/pnowy/sknerus:latestservices:
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.
git clone https://github.com/pnowy/sknerus.git
cd sknerus
pnpm install
pnpm build
node .output/server/index.mjsFor development:
pnpm dev # starts dev server at http://localhost:3000All configuration is done either via environment variables (infrastructure-level) or through the in-app Settings page (user-level).
| 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) |
The versioned REST API is also used by the web UI and is always available:
curl http://localhost:3000/api/v1/expensesAll 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.
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.
Sknerus uses a simple file-based JSON storage by default. Data is stored in three files inside DATA_DIR:
expenses.json— all transactionsconfig.json— categories, currency, and app settingsrecurring.json— recurring transaction templatesexchange-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.
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
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.
If Sknerus saves you a few clicks (or a few złoty), consider:
- ⭐ Starring the repo — it helps others discover the project
- 🚀 Reporting a bug or suggesting a feature — feedback shapes the roadmap
- 🛠️ Opening a PR — contributions are welcome, big or small
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. 🙏













