a webapp version of the one and only homebank app
See the code
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.
.xhb, QIF, OFX/QFX, CSV, ISO 20022 CAMT.053 — with an import assistant. Export: HomeBank .xhb, QIF, CSV.12.40 or 12,40 and both are read as decimals (toggleable per user).CB_SECRET_KEY to encrypt bank credentials, AI keys and other secrets in the database (see Configuration).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 setup
That 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.
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:
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.
/api/docs (the
OpenAPI spec is at /api/openapi.yaml).| 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.
Go
48.6%
TypeScript
46.9%
JavaScript
2.7%
CSS
1.6%
a webapp version of the one and only homebank app
See the code
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.
.xhb, QIF, OFX/QFX, CSV, ISO 20022 CAMT.053 — with an import assistant. Export: HomeBank .xhb, QIF, CSV.12.40 or 12,40 and both are read as decimals (toggleable per user).CB_SECRET_KEY to encrypt bank credentials, AI keys and other secrets in the database (see Configuration).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 setup
That 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.
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:
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.
/api/docs (the
OpenAPI spec is at /api/openapi.yaml).| 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.
Go
48.6%
TypeScript
46.9%
JavaScript
2.7%
CSS
1.6%