Easily manage your personal and family finances. Free and open source.
Caution
We are in alpha version. So be aware of possible bugs.
Warning
This project was created with help of AI. I know this is a barrier for many people, so please be aware.
-
‼️ Open Source -
‼️ Privacy: we don't collect any data - 2FA authentication
- Dark mode
- Bank accounts
- Credit cards
- Individual financial transactions
- Financial transactions of a group/family
- Recurrence financial transaction
- Financial transaction with installment purchase
- Dashboard and charts
- Multilanguage support (for now only english and brazilian portuguese)
- Financial goals
- Simulation of a new expense can show if you can afford it
- Support of many currencies, including cryptocurrencies. (You can see the list here)
- Import transactions from CSV/OFX
- Export transactions AS CSV (⌛ Planned)
- PDF report (⌛ Planned)
- Email before due date (✖️ Not planned)
You can self-host this project using docker compose. First pick your version at GitHub Packages.
First you need to choose if you just want the only Shared Finances or Shared Finances and their dependencies.
- Choose only Shared Finances if you have experience with Docker and prefers to adjust things your way.
- Choose Shared Finances and their dependencies if you want a simpler setup.
This configuration parameters are common between only Shared Finances and Shared Finances and their dependencies.
| Parameter | Type | Required | Default | Sample | Description |
|---|---|---|---|---|---|
SF_APP_POSTGRES_CONNECTION_STRING |
string | X | postgresql://localhost:5432/shared_finances |
Postgres connection string | |
SF_APP_POSTGRES_USER |
string | X | shared_finances_user |
Postgres user | |
SF_APP_POSTGRES_PASSWORD |
string | X | shared_finances_pass |
Postgres password | |
SF_APP_REDIS_HOST |
string | X | localhost |
Redis host | |
SF_APP_REDIS_PASSWORD |
string | redis_password |
Redis password | ||
SF_APP_REDIS_PORT |
number | X | 6379 |
Redis port | |
SF_APP_NATS_HOST |
string | X | localhost |
Nats host | |
SF_APP_NATS_PORT |
number | X | 4222 |
Nats port | |
SF_APP_NATS_USER |
string | X | client_user |
Nats user | |
SF_APP_NATS_PASSWORD |
string | X | client_pass |
Nats password | |
SF_APP_FILE_STORAGE_PATH |
string | X | /var/lib/shared-finances/files |
Writable directory used to persist application files. It must be backed up and mounted as a persistent volume when running in a container. | |
SF_APP_PORT |
number | 80 |
Shared Finances HTTP public port (not exists in backend-only image) | ||
SF_APP_API_PORT |
number | 8081 |
Shared FInances API port (internal in combined image; exposed in backend-only image; not exists in frontend-only image) | ||
SF_APP_INVITATION_EXPIRATION_MINUTES |
number | 15 |
Time, in minutes, before expiration of group invitation | ||
SF_APP_REGISTRATION_ENABLED |
boolean | true |
false |
Enables new user registrations. When disabled, registration is blocked even for users arriving through an invitation; existing-user login remains available. | |
SF_APP_LEGAL_DOCUMENTS_ENABLED |
boolean | false |
true |
Presents the shipped terms of use and privacy policy together and records their versions at registration. The shipped text describes the maintainer's public instance; an operator enabling this for another instance must review and replace the content with documents for that operation. | |
SF_APP_ENABLE_LIMITS |
boolean | false |
true |
Enables the plan model. When false, limits and quotas are not enforced or presented, limits pages and plan badges are hidden, and dormant-account deletion is disabled. | |
SF_APP_DEFAULT_ROLE |
user, pro, or administrator |
pro |
user |
Plan role assigned only to accounts created after this value is set. Existing accounts keep their stored role. | |
SF_APP_JWT_PUBLIC_KEY |
string | X | Public key for JWT authentication. Click here to learn how to generate this |
||
SF_APP_JWT_PRIVATE_KEY |
string | X | Private key for JWT authentication. Click here to learn how to generate this |
||
SF_APP_JWT_KID |
number | 1 |
Rotation number of JWT key | ||
SF_APP_JWT_ISSUER |
string | shared_finances |
The JWT issuer name | ||
SF_APP_JWT_ACCESS_TOKEN_TTL_MINUTES |
number | 15 |
Time, in minutes, before user login token expires | ||
SF_APP_JWT_REFRESH_TOKEN_TTL_MINUTES |
number | 43200 |
Time, in minutes, before user login refresh token expires | ||
SF_APP_WRONG_PASSWORD_BLOCK |
number | 10 |
How many times user can get your password wrong before block for SF_APP_WRONG_PASSWORD_TTL_MINUTES minutes. |
||
SF_APP_WRONG_PASSWORD_TTL_MINUTES |
number | 120 |
Time, in minutes, that user will cannot try login again after SF_APP_WRONG_PASSWORD_BLOCK wrong attempts. |
||
SF_APP_SECURE_COOKIE |
boolean | false |
Defines whether refresh cookie will only be sent by the browser on HTTPS connections. If you have https please change this to true |
||
SF_APP_MFA_TOTP_CRYPTO_PASSWORD |
string | changeme |
Password used to safe store MFA | ||
SF_APP_MFA_TOTP_CRYPTO_SALT_HEX |
string | 8e69438719bb1ebbd72c650e1b3262eb |
Password-like used to safe store MFA You can generate with openssl rand -hex 16 |
||
SF_APP_SERVICE_SECRET |
string | X | service-secret |
Password used to authenticate as server | |
SF_APP_PUBLIC_WEB_BASE_URL |
string | X | http://localhost:4200 |
Public URL of shared finances. Used on email links. | |
SF_APP_TURNSTILE_SECRET_KEY |
string | 1x0000000000000000000000000000000AA |
Cloudflare captcha. If you want to use this you'll need to build frontend and change environment turnstileSiteKey |
||
SF_APP_TURNSTILE_VERIFY_URL |
string | https://challenges.cloudflare.com/turnstile/v0/siteverify |
|||
SF_APP_EMAIL_CONFIRMATION_ENABLED |
boolean | false |
Whether it is needed confirm account via email before login. | ||
SF_APP_PASSWORD_RECOVERY_ENABLED |
boolean | false |
Wwhether it is possible to recover the password via email. | ||
SF_APP_TX_MAIL_PROVIDER_PRIORITY |
list of string | brevo,smtp |
Email servers, separated by commas, should be used to send emails. The further to the left, the higher the priority. If empty, emails will never be sent. | ||
SF_APP_BREVO_DAILY_QUOTA |
number | 300 |
Brevo daily quota for sending email. When quota is over is tried the next email server (if exists). | ||
SF_APP_BREVO_API_KEY |
string | Brevo api key | |||
SF_APP_BREVO_FROM_ADDRESS |
string | sample@domain.com |
Email on 'from' used in Brevo | ||
SF_APP_BREVO_FROM_NAME |
string | Shared Finances |
Name on 'from' used in Brevo | ||
SF_APP_SMTP_HOST |
string | localhost |
SMTP host | ||
SF_APP_SMTP_PORT |
number | 1025 |
SMTP port | ||
SF_APP_SMTP_USERNAME |
string | SMTP username | |||
SF_APP_SMTP_PASSWORD |
string | SMTP password | |||
SF_APP_SMTP_AUTH |
boolean | false |
If SMTP needs authentication | ||
SF_APP_SMTP_STARTTLS |
boolean | false |
SMTP STARTTLS | ||
SF_APP_TX_MAIL_SMTP_FROM |
string | noreply@localhost |
Email on 'from' used in SMTP | ||
SF_APP_TX_MAIL_SMTP_FROM_NAME |
string | Shared Finances |
Name on 'from' used in SMTP | ||
SF_APP_LEGAL_TERMS_VERSION |
date (yyyy-mm-dd) | 2026-08-10 |
Date of terms of use | ||
SF_APP_LEGAL_PRIVACY_VERSION |
date (yyyy-mm-dd) | 2026-08-10 |
Date of terms of privacy | ||
SF_APP_EXCHANGE_RATES_PROVIDER_URL |
string | https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@ |
Provider of exchange rates. Note: for now only fawazahmed0/exchange-api is supported. |
||
SF_APP_REFRESH_EXCHANGE_RATES_CRON |
string (cron) | 0 0 0/12 * * * |
Cron to get new exchange rates | ||
SF_APP_MATERIALIZE_GOAL_CRON |
string (cron) | 0 0 0 * * * |
Cron to generate scheduled financial goals. | ||
SF_APP_GENERATE_ENTRY_CRON |
string (cron) | 0 0 0 * * * |
Cron to generate scheduled financial transactions. | ||
SF_APP_EXPIRE_INVITES_CRON |
string (cron) | 0 0/30 * * * * |
Cron to delete expired group invites. | ||
SF_APP_UNCONFIRMED_ACCOUNT_CLEANUP_CRON |
string (cron) | 0 0 0 * * * |
Cron to delete not confirmed accounts. | ||
SF_APP_SIMULATION_RECONCILE_CRON_ENABLED |
boolean | false |
|||
SF_APP_SIMULATION_RECONCILE_CRON |
string (cron) | 0 */2 * * * * |
|||
SF_APP_SIMULATION_PURGE_CRON |
string (cron) | 0 30 3 * * * |
Cron to delete old financial simulations. | ||
SF_APP_IMPORT_WORKER_ENABLED |
boolean | true |
Enables the durable JetStream consumer for asynchronous CSV imports. | ||
SF_APP_IMPORT_RECONCILE_CRON_ENABLED |
boolean | true |
Enables reconciliation of queued imports and expired worker leases. | ||
SF_APP_IMPORT_RECONCILE_CRON |
string (cron) | 0 */1 * * * * |
Cron for asynchronous CSV import reconciliation. | ||
SF_APP_INACTIVE_ACCOUNT_DELETION_ENABLED |
boolean | false |
Setting it to true enables warning e-mails and permanently deletes inactive accounts and their financial records after the retention period configured for their plan. Owned groups are deleted with the account. |
||
SF_APP_INACTIVE_ACCOUNT_DELETION_CRON |
string (cron) | 0 0 2 * * * |
Cron used to delete inactive accounts. Only if SF_APP_INACTIVE_ACCOUNT_DELETION_ENABLED is true. |
SF_APP_REGISTRATION_ENABLED, SF_APP_LEGAL_DOCUMENTS_ENABLED, and SF_APP_ENABLE_LIMITS accept true or false, and
SF_APP_DEFAULT_ROLE accepts user, pro, or administrator. Invalid values prevent the backend from starting. These settings are read
at startup, so restart every application replica after changing them. SF_APP_DEFAULT_ROLE applies only to accounts created after it is
set; changing it never reassigns existing accounts. The maximum parsed rows in one import comes from the user's plan and is unbounded when
the plan model is disabled. Limits are off unless SF_APP_ENABLE_LIMITS=true; enabling it activates stored plan limits, quota enforcement,
plan/usage presentation, and permits dormant-account deletion when that feature's own switch is also enabled.
The legal documents are disabled unless SF_APP_LEGAL_DOCUMENTS_ENABLED=true. The bundled English and Brazilian Portuguese text is
written for the public instance operated by this project's maintainer. A self-hosting operator who wants to present legal documents must
replace that content with terms and a privacy policy that identify and describe their own operation before enabling the switch.
See Async CSV import operations for rollout, monitoring, and rollback procedures.
- Postgres 18+
- Redis 8+
- Nats
- A writable persistent directory for application files
-
create
docker-compose.ymlfile.name: shared-finances services: shared-finances: image: ghcr.io/ynixt/shared-finances:main-20260423-200705-f54e237 restart: unless-stopped stop_grace_period: 45s env_file: - .env environment: SF_APP_PORT: ${SF_APP_PORT:-80} SF_APP_FILE_STORAGE_PATH: /var/lib/shared-finances/files ports: - "${SF_APP_PORT:-80}:${SF_APP_PORT:-80}" volumes: - file_data:/var/lib/shared-finances/files depends_on: db: condition: service_healthy redis: condition: service_healthy nats: condition: service_healthy healthcheck: test: ["CMD-SHELL", "curl -fsS http://localhost:$${SF_APP_PORT:-80}/api/open/actuator/health >/dev/null || exit 1"] interval: 20s timeout: 5s retries: 6 start_period: 20s volumes: file_data:
-
Create
.envfile. (remember to change user/password/et.)So here's a sample:
## Postgres SF_APP_POSTGRES_CONNECTION_STRING=postgresql://localhost:5432/shared_finances SF_APP_POSTGRES_USER=shared_finances_user SF_APP_POSTGRES_PASSWORD=shared_finances_pass ## Redis SF_APP_REDIS_HOST=localhost SF_APP_REDIS_PASSWORD=redis_password SF_APP_REDIS_PORT=6379 ## NATS SF_APP_NATS_HOST=localhost SF_APP_NATS_PORT=4222 SF_APP_NATS_USER=client_user SF_APP_NATS_PASSWORD=client_pass ## File storage SF_APP_FILE_STORAGE_PATH=/var/lib/shared-finances/files ## APP SF_APP_INVITATION_EXPIRATION_MINUTES=15 SF_APP_JWT_PUBLIC_KEY=nice_token_here SF_APP_JWT_PRIVATE_KEY=nice_private_token_here SF_APP_JWT_KID=k1 SF_APP_JWT_ISSUER=self_hosted_shared_finances SF_APP_JWT_ACCESS_TOKEN_TTL_MINUTES=15 SF_APP_JWT_REFRESH_TOKEN_TTL_MINUTES=43200 SF_APP_WRONG_PASSWORD_TTL_MINUTES=120 SF_APP_MFA_TOTP_CRYPTO_PASSWORD=changeme SF_APP_MFA_TOTP_CRYPTO_SALT_HEX=8e69438719bb1ebbd72c650e1b3262eb SF_APP_SERVICE_SECRET=service-secret
-
Run
docker compose up -d -
Open in your browser
http://localhost
All application files are stored below SF_APP_FILE_STORAGE_PATH. The application validates this directory at startup and refuses to start
if it cannot create, read, and write files there.
- Back up the file-storage volume together with Postgres. Losing either one can leave file references or content incomplete.
- When running more than one application replica, every replica must mount the same shared filesystem at
SF_APP_FILE_STORAGE_PATH. Independent local disks are not supported. - The development backend uses
./data/filesfromdev.env; this directory is created automatically and should remain outside source control.
Coming soon
- fawazahmed0 for creating the API to obtain exchange rates
GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE.


