Most finance apps are built for one person. CoWallet is built for two. A self-hosted shared dashboard to track expenses, plan ahead, and stay aligned on your budget.
An open source, Docker-deployable expense tracker for couples. Budget planning, bank sync, and real-time notifications. Installable on iOS and Android.
- Monthly tracking - create a month, add charges, mark transfers as done
- Configurable split - 50/50, 60/40, or any ratio you want
- Recurring charges - automatically copied to the next month
- Installment payments - split a purchase over N months, auto-carried forward
- Actual vs planned - enter real amounts alongside budgeted ones, see the delta
- Month validation - mark a month as closed once transfers are done
- Bulk delete - select and delete multiple months at once
- Charge suggestions - autocomplete based on past entries
- Custom categories - create categories with icon and color
- Custom payment methods - add your own payment methods
- Connect your bank - link a joint account via Enable Banking (supports 2000+ banks across Europe)
- Import transactions - manually import transactions month by month, with debit filter and duplicate detection
- Auto-categorization - transactions are automatically categorized based on past imports
- Budget vs actual - compare planned charges against real spending, category by category
- History & charts - monthly evolution, breakdown by category, top recurring costs
- Sankey diagram - visualize income, shared expenses, personal spending and investments
- CSV export - keep a backup of all your data
- Multi-user - invite your partner via a private invite link
- Push notifications - get notified when your partner validates a month (iOS & Android)
- Group isolation - each couple's data is fully isolated
- PWA - installable on iOS (Safari) and Android home screen
- Sticky header - validate button always accessible while scrolling on mobile
- Rate-limited login - brute-force protection on the auth endpoint
- Password policy - minimum 8 characters, at least 1 digit
- First-run setup - no config file needed, just open the app
A docker-compose.example.yml is provided as a starting point. Copy it and adjust to your setup:
cp docker-compose.example.yml docker-compose.ymlReplace <host_folder_data> with the folder on your host where data will be stored (e.g. /home/user/cowallet/data).
services:
cowallet:
image: ghcr.io/ninidas/cowallet:latest
container_name: cowallet
restart: unless-stopped
ports:
- "8000:8000"
environment:
- PUID=1000
- PGID=1000
- APP_LANG=en # Default language for seeded data: en or fr
volumes:
- <host_folder_data>:/dataThen start with docker compose up -d. The app is available at http://localhost:8000. Point your reverse proxy (Traefik, nginx, Caddy…) to port 8000 of the cowallet container.
Open the app in your browser - you'll be guided through a setup wizard to create two user accounts and configure the default split ratio.
Bank sync is optional. The app works fully without it - it only adds the ability to import real transactions from your bank and compare them to your planned budget.
It uses Enable Banking, an open banking aggregator supporting 2000+ banks across Europe (France, Belgium, Spain, Germany, Italy, Netherlands…). A free tier is available and is sufficient for personal use.
openssl genpkey -algorithm RSA -out enablebanking.pem -pkeyopt rsa_keygen_bits:4096
openssl rsa -in enablebanking.pem -pubout -out enablebanking_pub.pemKeep enablebanking.pem private - you will mount it into the container.
You will upload enablebanking_pub.pem to the Enable Banking dashboard in the next step.
- Go to enablebanking.com and sign up
- Create a new application in the dashboard
- Upload
enablebanking_pub.pemas the public key for your application - Note your App ID
In your Enable Banking application settings, add the following redirect URI:
https://your-domain.com/bank/callback
Add the following environment variables and volume to the cowallet service. Replace <host_folder_secrets> with the folder containing your enablebanking.pem file (e.g. /home/user/cowallet/secrets).
environment:
- ENABLEBANKING_APP_ID=your-app-id
- ENABLEBANKING_PRIVATE_KEY_PATH=/backend/secrets/enablebanking.pem
volumes:
- <host_folder_secrets>:/backend/secretsOnce configured, a Bank connection section appears in the app settings where you can connect your bank account.
Push notifications are supported on mobile (iOS and Android). To receive them, install the app on your phone first (Chrome or Safari: use "Add to Home Screen"), then open the app, go to settings and toggle "Notifications". No server configuration needed.
All variables are optional. The app runs without any of them.
| Variable | Description |
|---|---|
SECRET_KEY |
JWT signing key - auto-generated and persisted on first run if not set |
PUID |
User ID to run the backend process as (default: 0 / root) |
PGID |
Group ID to run the backend process as (default: 0 / root) |
APP_LANG |
Default language for seeded data: en (default) or fr. Sets the language of default categories and payment methods on first setup. |
MAX_GROUPS |
Maximum number of groups allowed to register. Defaults to 1. Set to a higher value to allow multiple couples on the same instance. |
ENABLEBANKING_APP_ID |
Enable Banking app ID - required for bank sync |
ENABLEBANKING_PRIVATE_KEY_PATH |
Path to the RSA private key inside the container - required for bank sync |
- Frontend - React, Vite, Tailwind CSS, Recharts, PWA
- Backend - FastAPI, SQLAlchemy, SQLite
- Auth - JWT (python-jose), bcrypt
- Push - Web Push API, VAPID (pywebpush), compatible iOS 16.4+ and Android
- Proxy - nginx
AGPL-3.0 - see LICENSE



