Self-hosting guide

Setting up GnuCash Sync

This walks you through connecting 's optional GnuCash Sync feature to your own GnuCash ledger. Once set up, expenses you log on your phone show up as real transactions in your GnuCash desktop book.

Your data stays yours. never sees your GnuCash username/password or connects to your database directly. You run a small piece of software called PostgREST on your own server; your phone talks to that over HTTPS/HTTP, and PostgREST talks to your database internally. The only two things your phone ever stores are the address of your PostgREST server and a single access token (a JWT). Your real database credentials never leave your server.

Who this is for

You're comfortable with a command line, and you either already have GnuCash using a PostgreSQL-backed book (not the default local file), or you're willing to set one up. If your GnuCash book is currently a local .gnucash file, you'll need to migrate it to Postgres first — GnuCash's own documentation covers this (File → Save As, choosing a PostgreSQL connection).

What you'll need

Download the setup files

These four files are all you need besides PostgREST itself:

1Run the setup SQL script

Open self_hosting_setup.sql in a text editor and replace the three CHANGE_ME_... passwords near the top with strong passwords of your own — write them down somewhere safe, you'll need one of them again in Step 3.

Then run it against your GnuCash database, as a Postgres superuser:

psql -U postgres -d your_gnucash_database -f self_hosting_setup.sql

This creates three things:

If you'd rather point GnuCash desktop itself at this database using a role with full access (recommended, so GnuCash owns its own ledger the way it normally would), connect it using the gnucash_owner role and password you set above — Data Format postgres, your host/port, your database name, username gnucash_owner.

2Install PostgREST

PostgREST ships as a single static binary — no Docker or complex install needed.

  1. Download the release matching your server's architecture from the PostgREST GitHub releases page.
  2. Extract it and place the binary somewhere on your $PATH, e.g. /usr/local/bin/postgrest.
  3. Confirm it runs: postgrest --help

3Configure PostgREST

Copy postgrest.conf.example to somewhere like /etc/postgrest/postgrest.conf, and fill in its placeholders:

4Generate your access token (JWT)

python3 make_jwt.py "the-jwt-secret-from-step-3" gnucash_app_user

This prints a long string starting with eyJ... — that's your JWT. Copy it somewhere safe. This token doesn't expire, so treat it like a password: it's the only thing (besides your PostgREST URL) that lets a device write to your GnuCash ledger.

5Run PostgREST

For a quick test:

postgrest /etc/postgrest/postgrest.conf

For it to stay running (including after a server reboot), copy postgrest.service.example to /etc/systemd/system/postgrest.service, fill in the placeholders, then:

sudo systemctl daemon-reload
sudo systemctl enable --now postgrest
sudo systemctl status postgrest

Test it's working:

curl http://YOUR_SERVER:3000/accounts -H "Authorization: Bearer YOUR_JWT_FROM_STEP_4"

You should get back a JSON list of your GnuCash accounts. If you get an error, see Troubleshooting below.

6Deciding how to expose this

Your phone needs to be able to reach PostgREST's address. How you make that possible is entirely your call:

Whichever you choose, note the final URL (e.g. http://10.x.x.x:3000 for a VPN setup, or https://yourdomain.com for a reverse-proxied one) — you'll need it next.

7Connect the app

In : Settings → 🔗 GnuCash Sync → Configure GnuCash connection.

  1. Turn the sync toggle on.
  2. Enter your PostgREST URL from Step 6.
  3. Enter the JWT from Step 4.
  4. Tap Test Connection — you should see your account count confirmed.
  5. Tap Save Connection.

8Map your wallets and categories

The first time you save an expense with sync enabled, the app will prompt you to map the wallet and category involved to a matching GnuCash account (search by name — GnuCash's full account tree is available, with breadcrumb paths to disambiguate similarly-named accounts in different places). This mapping is saved per wallet/category, so you only do it once per pairing, not per transaction. You can also do this ahead of time from the GnuCash Sync settings screen's account list.

If you run a multi-currency GnuCash book, mapping a wallet to an account warns you if the wallet's currency doesn't match that account's actual currency — sync writes amounts as-is with no conversion, so a mismatched mapping (e.g. an IDR wallet mapped to a USD account) would otherwise silently record the wrong number. You can dismiss the warning and map anyway if that's genuinely what you want.

From here on, expenses you log will appear in your GnuCash ledger as normal, balanced transactions — indistinguishable from ones entered directly in GnuCash desktop.

One-way sync: what happens if you edit in GnuCash afterward

Sync only ever flows → GnuCash, never the other direction. That's fine — editing a transaction in GnuCash desktop after it's been synced (correcting the account, the amount, the date, whatever) won't break anything. But the app won't notice on its own; there's no live connection watching your ledger for changes.

What it does do: any time you open an already-synced, single-item transaction for editing in the app, it checks that transaction against GnuCash's current copy. If they've diverged, you'll see a warning banner with a "Pull values from GnuCash" option, which copies GnuCash's current amount/category/wallet/date/note into the local record. This only updates the app's own copy — it never writes anything back to GnuCash.

Multi-item receipts (individually editable items aren't supported yet) get a lighter version of this: opening one flags whether GnuCash's total for that receipt no longer matches what's saved locally, but without saying which item changed or offering anything to pull in — check GnuCash desktop directly if you need the specifics.

Troubleshooting

"Anonymous access is disabled" / 401 errors — you forgot the Authorization: Bearer ... header, or your JWT doesn't match the jwt-secret in postgrest.conf. Double check both.

PostgREST doesn't see a function/table you just created or changed — PostgREST caches its schema at startup. Run NOTIFY pgrst, 'reload schema'; via psql to refresh it live, or restart the postgrest service.

GnuCash desktop won't open the database ("could not obtain the lock") — check SELECT * FROM gnclock; in psql. A stale row from an earlier failed connection attempt can block new ones; if you're sure nothing else has the file open, DELETE FROM gnclock; clears it.

App can't connect over plain http:// — this is expected and already handled on Android for a VPN-only http:// PostgREST setup. If you're using a reverse proxy with HTTPS instead, use the https:// URL and this doesn't apply.

"permission denied for table ..." when calling sync_transaction — this actually means it's working as designed. gnucash_app_user has no direct table access; all its capability comes through the one function. If a different error like this shows up from GnuCash desktop itself, double check you connected desktop using gnucash_owner, not gnucash_app_user.

FAQ

Can I use the same PostgREST/database setup for multiple phones?

Yes — generate one JWT per device if you'd like to be able to revoke access to just one later (there's no built-in revocation besides rotating the jwt-secret, which invalidates every JWT at once), or share the same one, since this is designed for single-user/self-hosted use.

What if I want to stop syncing?

Turn the sync toggle off in Settings — the app falls back to local-only saving immediately, nothing about your GnuCash setup needs to change.

Does this ever sync income or budgets?

No — GnuCash Sync is expense-only by design. Income and budgets stay in the app only.

If I edit a synced transaction in GnuCash desktop, does it pick up the change automatically?

No — see "One-way sync" above. The app only checks for drift when you open that specific transaction for editing, and only offers to pull GnuCash's values in, never push the app's out.