Your money, self-hosted.
A free, web-based personal finance manager — a from-scratch web port of HomeBank.
Made-up data in a throwaway account, deleted when you stop using it. What the demo is.
CloudBank is a free, self-hosted, web-based personal finance manager — a from-scratch web port of the excellent HomeBank desktop application. It aims for feature parity with HomeBank while being built for the browser and the cloud: a single Docker container you run yourself, with your data living in a SQLite database on a volume you control.
Status: production-ready. 1.0 reached HomeBank parity; the 2.x line added deep personalization and interop, and the 3.x line the post-parity capabilities — automatic bank sync, 2FA, an installable PWA, and opt-in AI. 3.2 redesigned the whole interface, and 3.5 made it fit a phone. See the CHANGELOG.
Try it first: the live demo is one click, no sign-up, with a year of made-up data. It is deleted after two hours without use, so nothing you put in it stays — what the demo is.
HomeBank is a fantastic GTK desktop app, but it is desktop-only. CloudBank brings the same workflow — accounts, a powerful transaction register, scheduled transactions, budgets, rich reports, and multi-format import (including native HomeBank .xhb import and export) — to a web UI you can reach from any device, while keeping the data on your own server.
CloudBank is an independent, clean-room reimplementation. It does not copy or link any HomeBank source code; the original is referenced only for its documented behavior and file formats, and CloudBank tracks parity with current and future HomeBank releases. CloudBank is released under the AGPL-3.0 (HomeBank itself is GPL-2+).
| Dashboard | Register — bulk actions on a selection |
|---|---|
![]() |
![]() |
| Bills — last payment & next occurrence | Bank-sync review — resolve possible duplicates |
|---|---|
![]() |
![]() |
| Interactive reports | Appearance — theme, accent, sidebar |
|---|---|
![]() |
![]() |
| Customizable, free-form dashboard | Import & export — HomeBank .xhb, backup & restore |
|---|---|
![]() |
![]() |
The screenshots use the demo's made-up data. e2e/screenshots.mjs retakes them all.
- Accounts of every HomeBank type (bank, cash, checking, savings, credit card, liability, asset, investment) with per-account currency, a default payment mode, and the full set of flags, showing both today's balance and a projected future balance.
- Transactions with 12 payment types, the cleared/reconciled status lifecycle, category splits, free tags, internal transfers (including cross-currency), a right-click context menu, and multi-selection bulk actions — set category / payee / payment mode / status / tags (add or replace) or delete across many rows at once, with shift-click range selection — plus duplicate detection.
- Register view with running balance, rich filtering (including a show/hide future transactions toggle), columns you can show, hide, resize and reorder, a selection total (net + income/expense split of the currently selected rows), a reconciliation workflow that marks where the reconciled rows sit when a status filter hides them, double-click-to-edit, a privacy switch that blurs names and amounts for a screenshot, and full-text search across memo, payee, category and info.
- Entering a transaction happens in a sheet beside the ledger, so the rows and the running balance stay in view. You choose which fields it shows and which wait under More details, and Save and keep carries the fields over to the next entry.
- Scheduled transactions with automatic posting (optionally pre-registering up to 3 months ahead, HomeBank style), a per-week/month/year income & expense summary, the recurring amount shown in the schedules grid, templates (a dedicated management area, and offered when entering a transaction), and assignment rules for auto-categorization.
- Budgets and reports that answer first: where the money went, what came in and went out, what your accounts hold and will hold with what is scheduled, and what a car costs — for any month, quarter or year, with saved views and CSV/PNG downloads.
- Savings goals — manual piggy-bank goals with contribute / withdraw, a progress bar and an optional target date (included in wallet backup/restore).
- Bills — a focused "what's due" view showing one row per bill with its last successful payment and its next occurrence (due / overdue), one-click posting, and a quick "add a bill" form (name, amount, account, day of month → a monthly scheduled outflow).
- Import: HomeBank
.xhb, QIF, OFX/QFX, CSV, ISO 20022 CAMT.053 — with an import assistant (how to import). Export: HomeBank.xhb, QIF, CSV. - Automatic bank sync — pull new transactions straight from your bank, run through your assignment rules and reconciled against what you already have: a bank row that matches an existing manual or scheduled transaction (amount + nearby date) is merged into it instead of duplicated, imported with the right status (booked → reconciled, pending → cleared) and a sensible default payment mode. Three providers, all bring-your-own-credentials so CloudBank never sees your bank login: SimpleFIN (worldwide; a ~$15/year SimpleFIN Bridge subscription) and Enable Banking (EU/EEA + UK via PSD2 — a free sandbox to test, your own production application for real accounts). Plus Pluggy (Latin America — free for personal use via Meu Pluggy, where you link the banks and CloudBank just reads them). Pluggy is experimental and needs real-world testing — it follows the published API but has not been run against a live Latin American bank by the maintainers, so if you use it, please open an issue with anything that looks wrong, however small. A personal (restricted) Enable Banking production app can only sync accounts you link to it in its panel — the bank-sync guide walks through it.
- Review — lists imported transactions still needing a category (set it inline) and finds possible duplicates that slipped through, with per-pair merge, edit, delete, or "not a duplicate" (remembered, so it isn't shown again). An account with something to review says so in its register ("3 to review"), and the button opens Review on that account; a linked account also shows when it last synced, with a Sync button. The connections themselves are set up in Settings → Bank sync & AI.
- Multi-currency with manual and online (ECB / frankfurter.app) exchange rates.
- Multi-user (managed by the admin from Settings → People; each user changes their own password), responsive UI, English and Italian.
- A dashboard that opens with what needs doing — overdue bills, possible duplicates and other things waiting on you sit above the widgets, and one period control (month to all time) sets the span for the whole page.
- Fully customizable, free-form dashboard — place and resize widgets anywhere on a grid, and add multiple instances of any widget from a palette, each with its own settings. A varied widget library covers the standard cases (base-currency totals, quick-add, income/expense, accounts, spending donut, budget gauge, upcoming) plus building blocks for a custom layout (single account balance, recent transactions, a key-figure big-number, and free-text notes). Pick an account and Add opens the entry sheet; the Upcoming panel splits into Recurring / Future / Reminders with post / skip / edit. In edit mode, Tidy re-packs the widgets with no gaps and Reset restores the default layout. Existing dashboards migrate automatically, and the grid stacks to one column on phones.
- Themes — light / dark / auto plus an accent-colour picker; your choice persists per user across devices.
- Collapsible sidebar and a reorderable navigation — rename or add groups, hide pages, and optionally show up to three account balances below it.
- Smart amount entry (HomeBank style) — type
12.40or12,40and both are read as decimals (toggleable per user). - Dates rendered everywhere in your configured format.
- Page tours — each main page offers a short tour the first time you open it, and the ? in its header replays it. Settings → General can turn the offers off or reset them.
- Two-factor authentication (TOTP) — optional per-user 2FA with authenticator apps (QR enrolment + one-time recovery codes) and a two-step login.
- Personal API tokens — scoped, revocable bearer tokens for programmatic access.
- Installable PWA — a web-app manifest, icons and an offline app shell, so CloudBank installs to your phone's home screen or your desktop.
- Web push notifications — opt-in browser push for schedule / bill due reminders.
- Encrypted secrets at rest — set
CB_SECRET_KEYto encrypt bank credentials, AI keys and other secrets in the database (see Configuration).
- Category suggestions — provider-agnostic; suggests a category for a transaction from its description.
- Natural-language entry — type "coffee 3.50 yesterday" and get a pre-filled transaction to review. Your API key is stored server-side, is write-only, and is never returned to the browser.
You need Docker with the Compose plugin.
Create a docker-compose.yml (or copy the one in this repo):
services:
cloudbank:
image: ghcr.io/easly1989/cloudbank:main # latest stable release (see tags below)
container_name: cloudbank
restart: unless-stopped
ports:
- "8080:8080"
environment:
# Set to "false" only for a plain-HTTP LAN install without TLS in front.
CB_SECURE_COOKIES: "false"
volumes:
- cloudbank-data:/data
volumes:
cloudbank-data:Then start it and open the app:
docker compose up -d
# open http://localhost:8080 and complete the first-run admin setupThat is the whole install — one container, no external database. Your data lives
in the cloudbank-data volume (a SQLite database under /data). Back it up by
copying that volume, or use the in-app wallet backup (Settings → Import &
export) and the admin full-database backup: docs/backup.md
says what each one holds and how to restore it.
Then follow Getting started: the administrator, a wallet, your accounts and categories, and your first transactions.
Running behind HTTPS (recommended for anything beyond a trusted LAN)? See docs/reverse-proxy.md. Coming from the HomeBank desktop app? See docs/migrate-from-homebank.md.
CloudBank is an installable Progressive Web App — there is nothing extra to build, generate or host. The web-app manifest, the service worker (offline app shell) and the icons are produced automatically when the frontend is built and are baked into the same container image; the running app just serves them.
To install, open your CloudBank URL in a browser and:
- Chrome / Edge (desktop) — click the install icon in the address bar (or menu → Install CloudBank…).
- Android (Chrome) — menu → Add to Home screen / Install app.
- iOS/iPadOS (Safari) — Share → Add to Home Screen.
It then launches in its own window like a native app, and updates itself to the latest version on the next launch after you deploy a new image.
Requires HTTPS. Browsers only register a service worker and offer installation over a secure context — i.e. HTTPS (plain
http://localhostis the only exception, for local testing). Put CloudBank behind TLS (docs/reverse-proxy.md) and keepCB_SECURE_COOKIESat its default; over plain HTTP the browser won't offer "Install" and offline support / push notifications won't work.
Every guide, grouped by what you are trying to do: docs/README.md.
- Getting started — from a fresh install to your first transactions: docs/getting-started.md.
- API: interactive Swagger UI is served by the app at
/api/docs(the OpenAPI spec is at/api/openapi.yaml). - The live demo — what it is, what it switches off, when it forgets you: docs/demo.md.
- Backups, upgrades and restoring — what each backup holds, a routine that works, upgrading, and getting it all back: docs/backup.md.
- Reverse proxy / HTTPS: docs/reverse-proxy.md.
- Using CloudBank, page by page — what each page is for and what it can do: docs/pages.md.
- Importing transactions (bank CSV, QIF, OFX, CAMT.053), with the column mapping and what to do when rows come in wrong: docs/import.md.
- Migrating from HomeBank: docs/migrate-from-homebank.md.
- Automatic bank sync (SimpleFIN, Enable Banking & Pluggy): docs/bank-sync.md.
- Writing a bank import plugin: docs/import-plugins.md.
- Contributing / running from source: CONTRIBUTING.md.
| Env var | Default | Description |
|---|---|---|
CB_ADDR |
:8080 |
Address the HTTP server listens on. |
CB_DATA_DIR |
/data |
Directory holding the SQLite database and backups. |
CB_LOG_LEVEL |
info |
debug, info, warn, or error. |
CB_SECURE_COOKIES |
true |
Set false for plain-HTTP LAN installs (no TLS). |
CB_RATE_URL |
(frankfurter.app) | Override the online exchange-rate API root (e.g. a mirror). |
CB_VAPID_SUBJECT |
mailto:cloudbank@localhost |
Contact sent to browser push services with each web-push message. Set it to a mailto: or https: address of yours. |
CB_SECRET_KEY |
(none) | If set, encrypts secrets at rest (bank credentials, AI keys, 2FA & push keys). Use a strong, high-entropy value — generate one with openssl rand -base64 48 rather than a hand-picked passphrase. Keep it stable — losing it makes encrypted secrets unrecoverable. |
CB_BANK_SYNC_INTERVAL |
1h |
How often the background job checks for connections due to sync. Each connection has its own interval (default daily, configurable per connection), so this only bounds how promptly a due one is picked up. Set 0/off to disable background sync (manual "Sync now" still works). |
CB_ENABLEBANKING_BASE_URL |
(Enable Banking's API) | Diagnostics and testing only: points Enable Banking calls at another API root. |
CB_BANK_SYNC_DEBUG_PENDING |
(off) | Diagnostics only. When set, each auto-sync makes one extra transaction_status=PDNG call per account and logs the provider's exact response, to investigate why an ASPSP returns no pending transactions. Leave off in normal use — the extra call spends the PSD2 daily budget. |
CB_OIDC_ISSUER |
(none) | OIDC/SSO issuer URL (e.g. https://auth.example.com/realms/main). Setting issuer + client id + client secret + redirect URL enables a "Sign in with …" button alongside local login. |
CB_OIDC_CLIENT_ID |
(none) | OIDC client id registered with the provider. |
CB_OIDC_CLIENT_SECRET |
(none) | OIDC client secret. |
CB_OIDC_REDIRECT_URL |
(none) | Absolute callback URL registered with the provider: https://<your-host>/api/v1/auth/oidc/callback. |
CB_OIDC_SCOPES |
openid profile email |
Space-separated scopes requested (must include openid). |
CB_OIDC_NAME |
SSO |
Label shown on the sign-in button ("Sign in with <name>"). |
CB_OIDC_AUTO_PROVISION |
false |
When true, first SSO login for an unknown identity creates a local (non-admin) account; otherwise the account must already exist (matched by verified email) or login is refused. |
The :demo build reads a few more, all prefixed CB_DEMO_ (idle timeout, user and
transaction limits, the per-address rate, proxy hops); they are listed with their
defaults in server/internal/config and do
nothing in an ordinary build.
Images are published to GHCR: ghcr.io/easly1989/cloudbank.
⚠️ Read this — the tag scheme is intentional and unconventional:
Tag Meaning :mainLatest stable release — use this for a stable self-hosted install. :latestNightly build from the mainbranch — bleeding edge, may break.:vX.Y.ZA specific released version (e.g. :v3.1.5). Also:vX.Y.:demoThe public demo — throwaway accounts, never for real money. In other words,
:latestis the development nightly, and:mainis the stable release. This is the opposite of the usual Docker convention, so pin deliberately.
Availability: :latest is published on every push to main (nightly
workflow). :main and the version tags are published by the Release
workflow — either by publishing a GitHub Release, or by running that workflow
manually from the Actions tab (it has a workflow_dispatch trigger, with an
optional version input). If a pull fails with unauthorized, the GHCR package is
private: make it public (package → Settings → Change visibility) or
docker login ghcr.io with a token that has read:packages.
:demo is a different program, built with --build-arg DEMO=1 and published by the Docker
demo workflow: with every published release (not prereleases), from the release's tag, and when the
workflow is run by hand. There is no setup and no login: one button makes an account
with a year of made-up data. Accounts are deleted after two hours without use and every night at 03:00
UTC. Admin, restore, attachments, AI, push, API tokens, two-factor and OIDC are switched off, and bank
sync talks to a pretend bank. Run it without a volume, so a redeploy starts empty; it refuses to
start on a data directory that holds real accounts. It is tuned with CB_DEMO_* variables (see
server/internal/config); behind a proxy, set CB_DEMO_PROXY_HOPS so the per-address limit sees
the visitor and not the proxy. What a visitor sees is described in docs/demo.md.
CloudBank is licensed under the GNU Affero General Public License v3.0 — see LICENSE. If you run a modified version as a network service, the AGPL requires you to offer your modified source to its users.
CloudBank is an open-source labour of love. If it's useful to you, consider a donation — it genuinely helps and is much appreciated. ♥ The easiest way is Buy Me a Coffee. If you prefer another, the donation page lists them all: PayPal, Stripe, Liberapay or GitHub Sponsors.
Inspired by and aiming for parity with HomeBank by Maxime Doyen. CloudBank is built and maintained by Carlo Ruggiero (@easly1989) and is not affiliated with or endorsed by the HomeBank project.







