Home
Softono

Savvy

Open source MIT TypeScript
39
Stars
2
Forks
1
Issues
3
Watchers
2 months
Last Commit

 About Savvy

Savvy: Self-hosted finance tracker. One container, zero config, multiple currencies.

Platforms

Web Self-hosted Docker

Languages

TypeScript

Links

Need Help Installing Savvy?

We provide expert installation service for this software. Our team will install, configure, and secure Savvy on your server. plans start at just $30.

Savvy Savvy

Savvy

Selfhosted expense tracker with full multi-currency support. One container โ€” done.

Docker Version License

๐ŸŽฎ Try it now!

Demo

๐Ÿ” [email protected] / demo


Savvy Screenshot

โšก Quick Start

docker run -d -p 3000:80 -v savvy-data:/data truenormis/savvy:latest

Open localhost:3000 and create your account.

โœจ Features

  • Multi-currency โ€” any fiat or crypto, transfers between them
  • Auto exchange rates โ€” currency rates updated automatically via API
  • Recurring transactions โ€” scheduled payments (daily, weekly, monthly, yearly)
  • Automation rules โ€” auto-categorize transactions based on conditions
  • Debts โ€” track loans and borrowings with payment history
  • Budgets โ€” set limits and track progress
  • Categories & tags โ€” flexible organization
  • Multi-user โ€” share with family or team, role-based access (admin/user)
  • Rich analytics โ€” Sankey diagrams, heatmaps, net worth tracking, expense pace
  • CSV import โ€” import transactions from bank exports with duplicate detection
  • Backups โ€” create, restore and download database backups
  • 2FA โ€” two-factor authentication via TOTP (Google Authenticator, etc.)

Savvy Reports

๐Ÿ“ฑ Mobile-Friendly

Fully responsive design built with ShadCN/UI โ€” track expenses from your phone right after purchase.

Mobile Dashboard    

๐Ÿš€ Deployment

Docker Compose (Recommended)

services:
  savvy:
    image: truenormis/savvy:latest
    container_name: savvy
    restart: unless-stopped
    ports:
      - "3000:80"
    volumes:
      - savvy-data:/data
    environment:
      - APP_URL=https://savvy.yourdomain.com
      - TZ=Europe/Kyiv
    healthcheck:
      test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1/livez"]
      interval: 10s
      timeout: 3s
      start_period: 60s
      retries: 3

volumes:
  savvy-data:

Environment Variables

Variable Description Default
APP_URL Public URL of your instance http://localhost
TZ Timezone UTC

Behind a Reverse Proxy

Set APP_URL to your public https:// URL. Savvy honors the X-Forwarded-Proto and X-Forwarded-For headers from the proxy, so HTTPS link generation and real client IPs work automatically โ€” no extra configuration needed.

Health Checks

Two probe endpoints are exposed for orchestrators and uptime monitoring (responses use the IETF application/health+json format):

Endpoint Purpose Healthy Unhealthy
/livez Liveness โ€” the app process is up. Use it for container restart decisions. 200 โ€”
/readyz Readiness โ€” database is reachable and migrations are applied. Gate traffic. 200 503

/livez stays up during maintenance mode; /readyz returns 503 so traffic drains while the instance is not ready.

With Traefik (HTTPS)

services:
  savvy:
    image: truenormis/savvy:latest
    container_name: savvy
    restart: unless-stopped
    volumes:
      - savvy-data:/data
    environment:
      - APP_URL=https://savvy.yourdomain.com
      - TZ=Europe/Kyiv
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.savvy.rule=Host(`savvy.yourdomain.com`)"
      - "traefik.http.routers.savvy.entrypoints=websecure"
      - "traefik.http.routers.savvy.tls.certresolver=letsencrypt"
      - "traefik.http.services.savvy.loadbalancer.server.port=80"
    networks:
      - traefik

volumes:
  savvy-data:

networks:
  traefik:
    external: true

With Nginx Proxy Manager

  1. Run Savvy on internal port:
services:
  savvy:
    image: truenormis/savvy:latest
    container_name: savvy
    restart: unless-stopped
    expose:
      - "80"
    volumes:
      - savvy-data:/data
    environment:
      - APP_URL=https://savvy.yourdomain.com
    networks:
      - npm-network

volumes:
  savvy-data:

networks:
  npm-network:
    external: true
  1. In Nginx Proxy Manager, create proxy host pointing to savvy:80

Kubernetes

Deploy as a single-replica Deployment with a PersistentVolumeClaim mounted at /data. SQLite is single-writer, so use strategy: { type: Recreate }. The container runs as non-root (www-data, uid 82) โ€” grant NET_BIND_SERVICE so it can bind port 80. Wire the probes to the health endpoints:

        startupProbe:
          httpGet: { path: /livez, port: 80 }
          periodSeconds: 3
          failureThreshold: 30
        livenessProbe:
          httpGet: { path: /livez, port: 80 }
          periodSeconds: 10
        readinessProbe:
          httpGet: { path: /readyz, port: 80 }
          periodSeconds: 10

Helm chart coming soon.

๐Ÿ”„ Updating

docker compose pull
docker compose up -d

Your data is safe in the /data volume.

๐Ÿ’พ Backups

Backups can be managed directly from the UI (Settings โ†’ Backups).

[!WARNING] The database runs in WAL mode, so recent writes may still live in the database.sqlite-wal file and won't be in database.sqlite yet. Copying database.sqlite alone can silently lose the latest data. Always checkpoint the WAL into the main file first (or use the in-app backup, which handles this for you).

Manual backup:

# Fold the WAL into the main file, then copy
docker exec savvy php artisan tinker --execute="DB::statement('PRAGMA wal_checkpoint(TRUNCATE);');"
docker cp savvy:/data/database.sqlite ./backup-$(date +%Y%m%d).sqlite

Restore (stop writers first so the WAL doesn't fight the swap):

docker compose down
docker cp ./backup.sqlite savvy:/data/database.sqlite
docker compose up -d

๐Ÿ”’ Privacy

Your data stays with you. SQLite database stored in /data volume โ€” no external services required.

โš™๏ธ How It Works

One container runs everything under Supervisor โ€” Nginx, PHP-FPM, the scheduler (recurring transactions, automatic exchange-rate updates) and a queue worker for background jobs. SQLite lives in /data; no external database, cache, or queue service is required. Migrations run automatically on startup.

๐Ÿ›  Stack

Laravel โ€ข SQLite โ€ข Docker โ€ข ShadCN/UI โ€ข Tailwind CSS

๐Ÿค Contributing

Contributions are welcome! Please open an issue first to discuss what you would like to change.

๐Ÿ“„ License

MIT


Made with โค๏ธ for people who want control over their finances