LedgerFly
Self-hosting guide

Install LedgerFly on your own server

From a fresh Linux server to a running LedgerFly with HTTPS, backups and updates. Plan for about fifteen minutes.

Requirements

  • A Linux server you can reach over SSH. Ubuntu 24.04 or Debian 12 on a VPS with 2 GB of RAM is plenty for a household.
  • A domain or subdomain pointing at the server, for example money.example.com, with an A record to the server's IP address.
  • Ports 80 and 443 open to the internet, so HTTPS certificates can be issued.

Everything runs in Docker: the web app, the API, MySQL, a queue worker for notifications and a scheduler for bills, budgets and recurring transactions.

1. Install Docker

Docker's convenience script installs Docker Engine and the Compose plugin. Run it as a user with sudo:

curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER

Log out and back in so the group change applies, then check that it works with docker compose version.

2. Download LedgerFly

git clone https://github.com/nayeemdev/ledgerfly.git ledgerfly
cd ledgerfly

3. Configure

There are two settings files. The one in the project root holds database passwords for Docker; api/.env holds what the app needs before it can read its database.

cp .env.example .env
cp api/.env.example api/.env
openssl rand -base64 24   # run twice, for the two database passwords
echo "base64:$(openssl rand -base64 32)"   # the app key

In .env, set:

SettingValue
DB_PASSWORDA long random password
DB_ROOT_PASSWORDA different long random password
APP_PORT127.0.0.1:8000, so only Caddy on the same server can reach the app

In api/.env, set:

SettingValue
APP_KEYThe base64:... key printed above
APP_URLYour address, for example https://money.example.com
APP_ENVproduction
APP_DEBUGfalse
DEFAULT_TIMEZONEYour time zone, for example Asia/Dhaka

You do not need to set the database host or user in api/.env: Docker Compose passes them in from the root .env. Email, sign-ups and alert settings are managed from the app later.

4. Start the stack

docker compose up -d --build
docker compose exec api php artisan db:seed --force

The first build takes a few minutes. Database migrations run automatically when the API starts, and the seed step loads the currency list and built-in link types. It is safe to run again.

Check that everything is up with docker compose ps. LedgerFly now answers on http://127.0.0.1:8000 on the server itself.

5. Add HTTPS

Install Caddy, which gets and renews certificates on its own. Then replace /etc/caddy/Caddyfile with your domain:

money.example.com {
	encode zstd gzip
	reverse_proxy 127.0.0.1:8000
}
sudo systemctl reload caddy

Open https://money.example.com. If the certificate is not ready yet, check that the DNS record points at the server and ports 80 and 443 are open.

Prefer Cloudflare Tunnel, Nginx Proxy Manager or Traefik? Point them at 127.0.0.1:8000 the same way.

6. Create the admin account

Open your address and sign up. The first account on a new server becomes its admin. After that, sign-ups are closed unless you open them.

As the admin, open Settings, System in the web app to set up:

  • Whether new people can sign up, and how long invitations stay valid
  • Outgoing email, with a test button
  • Automatic exchange rates
  • Bill reminder and budget warning thresholds
  • The suggested ntfy server and the upload size limit

To share a ledger with family, invite them from the ledger's settings as an owner, editor or viewer.

Notifications

Alerts for budgets, bills, low balances and large spending can go out by email, browser push, ntfy or the mobile app. Each person picks channels and quiet hours under Settings, Notifications.

  • Email works once the admin sets up outgoing mail in Settings, System.
  • Browser push needs keys, created once:
docker compose exec api php artisan webpush:vapid --show

Copy the printed values into api/.env and restart with docker compose up -d.

  • ntfy needs nothing on the server: each person picks a topic in the app and subscribes to it in the ntfy app.

Backups

Back up the database and the uploaded files (receipts and attachments). From the ledgerfly folder:

docker compose exec -T mysql sh -c 'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction ledgerfly' | gzip > ledgerfly-$(date +%F).sql.gz
docker run --rm -v ledgerfly_storage:/data -v "$PWD":/backup alpine tar czf /backup/storage-$(date +%F).tar.gz -C /data .

Run these from cron every night and copy the files somewhere off the server. To restore the database into a running stack:

gunzip -c ledgerfly-2026-01-31.sql.gz | docker compose exec -T mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" ledgerfly'

Each ledger can also be exported to a JSON backup from the web app.

Updates

cd ledgerfly
git pull
docker compose up -d --build

New migrations run automatically when the API container starts. Take a backup first.

Troubleshooting

  • See what is happening: docker compose logs -f api, or queue for notifications and scheduler for scheduled jobs.
  • Emails or alerts never arrive: check that the queue container is running and use the test buttons in Settings.
  • Sign in fails behind a proxy: make sure APP_URL matches the address in the browser exactly, including https.
  • Still stuck? Open an issue on GitHub.