No description https://marcelleismann.de
  • Python 45.5%
  • HTML 28%
  • CSS 17.3%
  • JavaScript 5.3%
  • Shell 3%
  • Other 0.9%
Find a file
Marcel Leismann 22d20c3b01
All checks were successful
build-and-push / build (push) Successful in 59s
Release 0.5.1
2026-04-20 22:31:56 +02:00
.forgejo/workflows Drop the LEDGER_VERSION build-arg from CI 2026-04-16 23:56:03 +02:00
core Wire up /healthz URL route 2026-04-20 22:31:08 +02:00
ledger Release 0.5.1 2026-04-20 22:31:56 +02:00
locale Add German and French translations for new validation and error messages 2026-04-20 01:13:48 +02:00
scripts release.sh promotes [Unreleased] to versioned heading at release time 2026-04-17 03:10:04 +02:00
.dockerignore Add Docker build pipeline 2026-04-15 01:44:27 +02:00
.env.example Update commented default for LEDGER_UPDATE_CHECK_URL to the packages API 2026-04-16 20:21:11 +02:00
.gitignore Ignore the local media directory (dev avatar uploads) 2026-04-16 23:43:51 +02:00
CHANGELOG.md Release 0.5.1 2026-04-20 22:31:56 +02:00
docker-compose.yml Make SQLite path configurable, mount /data volume 2026-04-15 02:43:30 +02:00
Dockerfile Remove the LEDGER_VERSION build arg and runtime env 2026-04-16 23:56:03 +02:00
entrypoint.sh Delegate all boot logic to startup.py 2026-04-17 00:45:41 +02:00
LICENSE initial commit 2026-04-15 22:43:02 +02:00
manage.py Add Docker build pipeline 2026-04-15 01:44:27 +02:00
README.md Mention health endpoint in README features list 2026-04-20 22:31:09 +02:00
requirements.txt Add django-axes dependency for login rate limiting 2026-04-20 01:13:30 +02:00
startup.py Seed default categories and create UserProfile for env-var superuser 2026-04-17 00:50:04 +02:00

Ledger

A self-hosted personal finance tracker. Create multiple ledgers (e.g. per bank account, per currency), record income and expenses, and see your running balance.

Built with Django. Small, self-hosted, deployable as a single Docker container. SQLite for simple setups, Postgres when you want it.

Features

  • Multiple ledgers per user, each with its own currency and starting balance
  • Income and expense transactions with automatic running balance
  • Per-user, customizable categories (seeded with a sensible default set)
  • Ledger sharing — share a ledger with other users, optionally granting edit rights; shared ledgers appear with a "Shared" badge on the dashboard and an overlapping avatar stack in the ledger header; the share list shows user avatars and updates without closing the settings modal
  • Dashboard totals — currency-grouped sum across all visible ledgers
  • Modal forms for quick ledger edits and transaction entry
  • Toast notifications for feedback instead of static banners
  • Custom confirm dialogs for all destructive actions (no browser confirm())
  • 12 built-in themes — 6 dark + 6 light (Midnight, Ocean Breeze, Forest, Sunset, Lavender, Monochrome) with a theme-aware favicon
  • Internationalization: English, German, French — selectable per user
  • Regional formatting profiles (Europe / US / UK) for dates, numbers, currencies, powered by Babel
  • Profile pictures — upload an avatar (center-cropped, resized to 200×200) displayed in the navbar
  • Instance branding — admins can set a custom name shown in the header, footer, and browser tab
  • User roles and admin panel — admins get Settings tabs for General (branding) and Users (add/block/unblock users, reset passwords)
  • Version display and update check — footer shows the running version; a badge appears when a newer tag exists in the container registry (configurable, 6h refresh)
  • Startup diagnostics — clear, actionable error messages on boot when the database is unreachable, credentials are wrong, or migrations fail
  • Security hardening — HSTS, secure cookies, required secret key in production, login rate limiting (django-axes: 5 attempts / 30min lockout), strengthened password validators
  • Health endpointGET /healthz for reverse-proxy and orchestrator liveness checks (returns 200/503, no auth)

Quick start (Docker)

mkdir ledger && cd ledger
curl -O https://forgejo.marcelleismann.de/lecram345/ledger/raw/branch/main/docker-compose.yml
curl -O https://forgejo.marcelleismann.de/lecram345/ledger/raw/branch/main/.env.example
mv .env.example .env
# edit .env — set DJANGO_SECRET_KEY, DJANGO_ALLOWED_HOSTS, admin credentials
docker compose pull
docker compose up -d

The app listens on 127.0.0.1:8000 by default — point your reverse proxy (nginx, Caddy, Traefik, …) at it.

Generating a secret key

python -c 'import secrets; print(secrets.token_urlsafe(64))'

Admin user

Set DJANGO_SUPERUSER_USERNAME, DJANGO_SUPERUSER_EMAIL, and DJANGO_SUPERUSER_PASSWORD in .env. On first startup the entrypoint creates the user if it doesn't already exist. Leaving the vars unset skips auto-creation — you can always create one manually:

docker compose exec ledger python manage.py createsuperuser

Configuration

All configuration is via environment variables, loaded from .env.

Variable Required Default Description
DJANGO_SECRET_KEY yes Long random string. Never commit.
DJANGO_DEBUG no false Set to true only for local development.
DJANGO_ALLOWED_HOSTS yes Comma-separated list, e.g. ledger.home.
DJANGO_CSRF_TRUSTED_ORIGINS yes Comma-separated, scheme included, e.g. https://ledger.home.
SQLITE_PATH no /data/db.sqlite3 Path inside the container. Persisted via the ledger-data volume.
POSTGRES_DB no If set, Postgres is used and SQLITE_PATH is ignored.
POSTGRES_USER iff Postgres
POSTGRES_PASSWORD iff Postgres
POSTGRES_HOST no localhost Use host.docker.internal to reach a Postgres on the Docker host, or the container/service name if both are on the same Docker network.
POSTGRES_PORT no 5432
DJANGO_SUPERUSER_USERNAME no If set with password, creates admin on first boot (idempotent).
DJANGO_SUPERUSER_EMAIL no ""
DJANGO_SUPERUSER_PASSWORD no Required together with username for auto-creation.
LEDGER_TAG no latest Image tag pulled by compose.
LEDGER_UPDATE_CHECK no true When true, the app queries the registry for newer semver tags every 6h and shows an "available" badge in the footer to signed-in users. Set false to disable outbound checks.
LEDGER_UPDATE_CHECK_URL no Forgejo packages API for lecram345/ledger Override if you host the image elsewhere. The default uses the packages REST API, which doesn't require auth for public packages (the Docker Registry /v2/ endpoint does).
LEDGER_UPDATE_CHECK_PACKAGE no ledger Package name to match in the response (used when the API returns entries for multiple packages).
LEDGER_UPDATE_CHECK_TOKEN no Bearer token if you point at an auth-required endpoint. Leave unset for public.

Switching to Postgres

Uncomment and fill the POSTGRES_* block in .env. The app uses Postgres whenever POSTGRES_DB is set; otherwise it falls back to SQLite. No code change required.

If Postgres runs in a separate Docker stack, create a shared external network and add it to both compose files so the Ledger container can resolve the Postgres hostname.

Local development

python -m venv .venv
source .venv/bin/activate     # fish: source .venv/bin/activate.fish
pip install -r requirements.txt
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver

Then open http://127.0.0.1:8000.

Building the image locally

docker build -t ledger:dev .
docker run --rm -p 8000:8000 \
    -e DJANGO_SECRET_KEY=dev \
    -e DJANGO_ALLOWED_HOSTS=localhost \
    -e DJANGO_CSRF_TRUSTED_ORIGINS=http://localhost:8000 \
    ledger:dev

CI (Forgejo Actions, .forgejo/workflows/build.yml) builds and pushes to the Forgejo registry on every push to main and on v* tags.

Data & backups

The SQLite database lives in the ledger-data named volume at /data/db.sqlite3. Back it up with:

docker compose exec ledger sqlite3 /data/db.sqlite3 ".backup '/data/backup.sqlite3'"
docker compose cp ledger:/data/backup.sqlite3 ./backup-$(date +%F).sqlite3

For Postgres deployments, back up using your usual pg_dump workflow against the Postgres instance.

License

Copyright (C) 2026 Marcel Leismann.

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License v3.0 as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file for the full text.

AGPL-3.0 is copyleft and covers network use: if you run a modified version of this software as a service, you must offer the modified source code to its users.