GigFin helps delivery / rideshare / freelance workers track earnings across platforms, log expenses and mileage, see live dashboards, estimate tax, and keep everything on infrastructure they control.
A hosted demo lives at gigfin.me — but GigFin is designed to be deployed by you. It is air-gap first: the only hard dependency is a self-hosted Convex backend that ships in the compose stack, and every optional integration is off unless you configure it. GigFin runs fully on a private network with no internet access.
- Live logs — income, expenses, mileage and shifts, with one-tap start/stop for live shifts and trips, plus search, filters and inline edit. Updates appear instantly across devices (no refresh).
- Customisable dashboard — 21 drag-and-drop widgets (income/expense trends, platform & category breakdowns, effective £/hour, per-mile metrics, run-rate, cadence heatmap…) under a global timeframe filter; layout saved per user.
- Recurring income & expenses — scheduled by the backend (no external cron); due entries also catch up the moment you open the app.
- Charging vendors — save EV/fuel unit rates; tag a fuel/charging expense with the vendor (shown on the entry) and just enter the amount.
- Goals & budgets — weekly/monthly targets and spending limits with progress.
- Tax estimator + year-end reports — UK Self-Assessment / US Schedule C, mileage allowance, printable/PDF summaries. Computed locally; an estimate, not advice.
- Receipts — image attachments in self-hosted file storage (no S3).
- CSV import/export for income, expenses, mileage and shifts, plus one-click in-app migration from GigFin v1.
- Auth — email/password + TOTP two-factor (with backup codes) + session management (Better Auth).
- PWA, light/dark themes, mobile-friendly.
New to the app? The User guide is a step-by-step walkthrough of every feature — the dashboard and widgets, logging (including live shift/trip tracking), recurring income & expenses, taxes, import/export, settings and more.
GigFin ships as one prebuilt linux/amd64 image (ghcr.io/sayedhfatimi/gigfin)
plus a self-hosted Convex backend. The same docker-compose.yml covers both a
private-network box and public infrastructure — only a couple of env vars differ.
You set at most five vars; scripts/setup.sh generates the three secrets for you.
Requires Docker + Docker Compose.
git clone https://github.com/sayedhfatimi/gigfin.git
cd gigfin
# 1. Generate the three required secrets into .env
sh scripts/setup.sh
# 2. (LAN only) point browsers at this host — edit .env:
# CONVEX_PUBLIC_URL=http://<lan-ip>:3210
# SITE_URL=http://<lan-ip>:3000
# (skip for a single machine — the localhost defaults already work)
# 3. Start everything
docker compose up -dOpen SITE_URL and sign up. The app deploys its Convex functions to the backend on
first boot — there is no second "generate key / redeploy" step.
-
New resource → Docker Compose (empty) → paste this repo's
docker-compose.yml. -
Run
sh scripts/setup.shon any machine with Docker + openssl (it doesn't deploy anything — it just prints three portable secrets). Paste the three printed lines (BETTER_AUTH_SECRET,CONVEX_INSTANCE_SECRET,CONVEX_SELF_HOSTED_ADMIN_KEY) into Coolify's Environment UI, plus:CONVEX_PUBLIC_URL=https://convex.gigfin.meSITE_URL=https://gigfin.me
-
Assign one domain per service (Coolify allows one each — that's all you need):
gigfin.me→ gigfin service, port3000convex.gigfin.me→ convex service, port3210- (optional)
convex-dash.gigfin.me→ convex-dashboard service, port6791
Only the Convex cloud port (3210) is ever browser-facing; the site port (3211) stays internal, so a single domain per service is sufficient.
-
Deploy — Coolify pulls the prebuilt image from GHCR.
Dashboard access: the Convex dashboard requires your deployment URL +
CONVEX_SELF_HOSTED_ADMIN_KEYto log in, so it is not open to the public. Exposing it on a domain is optional — you can also leave it unmapped and reach it over an SSH tunnel. Either way, keep the admin key secret.
| Variable | Required | Purpose |
|---|---|---|
BETTER_AUTH_SECRET |
✓ | Session signing secret (from scripts/setup.sh) |
CONVEX_INSTANCE_SECRET |
✓ | Pins the backend instance so the admin key stays valid (from scripts/setup.sh) |
CONVEX_SELF_HOSTED_ADMIN_KEY |
✓ | Lets the app deploy Convex functions (from scripts/setup.sh) |
CONVEX_PUBLIC_URL |
✓ | Where the browser reaches the Convex backend (3210). Local default works. |
SITE_URL |
✓ | Public URL of the app. Local default works. |
RESEND_API_KEY |
— | Enables email (password reset/verification). Omit to disable. |
GIGFIN_DISABLE_SIGNUP |
— | Set to true to lock registration to existing users (enforced at the auth layer — the sign-up endpoint is closed, not just hidden) |
ANALYTICS_SCRIPT_URL / ANALYTICS_SITE_ID |
— | Privacy-friendly analytics. Omit to disable. |
POSTGRES_URL |
— | External Postgres for the backend instead of the bundled SQLite volume |
Advanced/internal overrides (the backend:* URLs, instance name, host ports) are
fixed in docker-compose.yml and normally untouched — see the comments there.
docker compose pull && docker compose up -d # or redeploy in Coolifydocker compose -f docker-compose.yml -f docker-compose.build.yml up -d --buildGigFin v2 is a ground-up rewrite and is not a drop-in upgrade from v1: v1 stored data in a local SQLite database (Drizzle), whereas v2 uses Convex. The two are not compatible — you don't point v2 at a v1 database, you import it.
To bring your v1 ledger across:
- Deploy v2 and create an account.
- Go to Settings → Data → Migrate from GigFin v1.
- Upload your v1
db.sqlitefile.
The file is parsed locally in your browser (nothing is uploaded anywhere) and your income, expenses, mileage, vehicles and charging vendors are imported into your account. Money is converted to integer minor units automatically.
Operators who prefer the command line can use scripts/migrate-sqlite.ts instead.
What does not carry over: accounts / passwords, two-factor, sessions, and app preferences (currency / units / tax jurisdiction) — set those up fresh in v2.
GigFin needs no internet at runtime. The backend's anonymous telemetry beacon is
disabled (DISABLE_BEACON=true) in the compose file. Files (receipts) are stored
locally in the backend volume. Note: self-service password reset requires email
(RESEND_API_KEY); without it there is no self-service reset yet, so on an
email-less instance an operator must help recover a locked-out account.
All data lives in the gigfin-convex-data Docker volume (SQLite + file storage). Back
it up with the Convex self-hosted snapshot export, or by snapshotting the volume while
the stack is stopped. See the
Convex self-hosting docs.
bun install
docker compose up -d convex # the Convex backend
# .env.local sets NEXT_PUBLIC_CONVEX_URL / CONVEX_PUBLIC_URL (= 127.0.0.1:3210),
# the admin key, and BETTER_AUTH_SECRET. Generate an admin key with:
# docker compose exec convex ./generate_admin_key.sh
bunx convex dev # terminal 1 — sync functions
bun run dev # terminal 2 — http://localhost:3000Quality checks: bun run lint, bunx tsc --noEmit, bun run test, bun run build.
Next.js 16 (App Router) · self-hosted Convex · Better Auth · Tailwind v4 · shadcn/ui (Base UI) · Biome · bun.
