FinCouple — self-hosted couple finance PWA. Expenses, budgets, investments, mortgage. Runs on your
7.6K
Self-hosted finance app for two people managing money together. One small Docker image (Go + SQLite + embedded PWA), no cloud, no subscription — your financial data lives on your own server. Installable on iOS, Android, macOS and Windows as an app. Interface in European Portuguese (PT-PT).
Formerly CouplePay Go — the Docker image is still published as
gatoserio/couplepaygofor now; existing deployments keep working unchanged.
Daily money
Credit cards done right
Bank extract import & reconciliation
Budget & statistics
The Folha (spreadsheet view) & Google-Sheet mirror
tools/sheets-mirror) pulls the data with a token and
rebuilds the tabs as values. The app stays the only place where data is
written, so the sheet can never drift.Wealth & investments
Mortgage
Housekeeping
Works on any Docker host — a NAS, a home server, a VPS. Create a folder for the
app, put this docker-compose.yml in it, and start:
services:
couplepay:
image: gatoserio/couplepaygo:latest
container_name: couplepay
restart: unless-stopped
init: true
read_only: true # app only writes to /data
security_opt: [no-new-privileges:true]
cap_drop: [ALL]
user: "${UID:-65532}:${GID:-65532}" # match the owner of ./data (id -u / id -g)
volumes:
- ./data:/data # SQLite lives here — back this folder up!
environment:
- TZ=Europe/Lisbon # set your timezone
ports:
- "8383:8080"
mkdir -p data # create first so it's owned by you, not root
UID=$(id -u) GID=$(id -g) docker compose up -d # then open http://<host-ip>:8383
The first visit shows a one-time setup screen where the two of you pick your
usernames and passwords. To update later: docker compose pull && docker compose up -d.
Install as an app: open the URL in Safari (iOS: Share → Add to Home Screen; macOS: File → Add to Dock) or in Edge/Chrome (install icon in the address bar).
Backups: two complementary options — include the data/ folder in your
server's backup plan, and/or export the JSON file from Config → Dados now and
then (it restores into a fresh instance via the same screen).
To use the app away from home without opening any ports, put it behind a
Cloudflare Tunnel — cloudflared dials out to Cloudflare and your hostname
proxies to the container:
# add to the same docker-compose.yml
cloudflared:
image: cloudflare/cloudflared:latest
container_name: couplepay-tunnel
restart: unless-stopped
command: tunnel run --token ${CF_TUNNEL_TOKEN}
depends_on: [couplepay]
And tell the app to trust forwarding headers from the compose network (needed for correct rate limiting and Secure cookies behind the tunnel) by adding to the app service:
environment:
- TZ=Europe/Lisbon
- TRUSTED_PROXIES=172.16.0.0/12 # docker's default address pool
.env file next to the compose (CF_TUNNEL_TOKEN=...).http://couplepay:8080 (the service name
on the compose network — with the tunnel you can even remove ports: and
stay LAN-invisible).Security posture: argon2id passwords, rate-limited login, strict Content-Security-Policy, cross-origin API writes rejected; behind a trusted proxy the app turns on Secure cookies and HSTS and keys rate limiting on the real client IP. Never expose the container by port-forwarding your router — use a tunnel (or a VPN like Tailscale) instead.
go run ./cmd/server # API on :8080 (embeds web/dist if present)
cd web && npm install && npm run dev # frontend with hot reload (proxies /api)
go vet ./... && go test ./... # tests
cd web && npm run build && cd .. && go build ./cmd/server # single production binary
CI tests every branch and publishes multi-arch (amd64 + arm64) images to Docker
Hub on pushes to main and version tags. Design notes and the data format live
in docs/; the export/import JSON format is documented in
docs/05-data-format.md.
Content type
Image
Digest
sha256:460fe0d8e…
Size
6.8 MB
Last updated
about 1 month ago
docker pull gatoserio/couplepaygo