ezBookkeeping 2.1.1
Overview
ezBookkeeping is a lightweight, self-hosted personal finance and bookkeeping application. The SecureAnyCloud ezBookkeeping 2.1.1 on Ubuntu 24.04 AMI gives you a hardened instance on Amazon EC2 that serves it over HTTPS, with a unique administrator account created on first boot. It is one version of the SecureAnyCloud accounting, HR and finance listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.
With ezBookkeeping you keep your cash, bank, credit card, savings and investment accounts in one place. You record income, expenses and transfers in any currency, organise them with two-level categories and tags, and read the results as an overview, a transaction calendar, and pie, bar and trend charts. It imports CSV, OFX, QIF and other bank and app exports, and it exports your data again at any time. It also supports scheduled transactions, transaction templates, receipt pictures and two-factor sign-in. It has a desktop and a mobile web interface that you can install as an app on a phone.
What you get:
- No default account. The image ships with no account and no database. On first boot your instance creates one administrator account with a 24-character random password, written to a root-only file. Self-registration is switched off, so nobody else can create an account on your instance.
- A session signing key for each instance. ezBookkeeping signs every login session with a secret key. Its sample configuration leaves that key empty, which falls back to a built-in default that anyone can look up. Your instance generates its own 48-character key on first boot, and the service refuses to start if the key is missing, still a placeholder or the default.
- HTTPS from the first boot. nginx serves ezBookkeeping on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS. Nothing answers until first boot has finished.
- ezBookkeeping listens on localhost only (
127.0.0.1:8080), so nginx is its only client. ezBookkeeping trusts the client address that nginx forwards only from nginx itself. - Sign-in is rate limited twice. nginx slows down repeated sign-in and two-factor attempts from one address. ezBookkeeping locks out an address or user for the rest of the minute after five failed sign-ins.
- Other web sites cannot act with your login. nginx refuses API requests that another web site's page sends in your browser, and any change whose browser
Originis not this instance. - No access to the instance metadata service. nginx and ezBookkeeping are blocked at the kernel level from the EC2 instance metadata service (IPv4 and IPv6). ezBookkeeping runs as its own unprivileged user with no Linux capabilities.
- No third-party services by default. The AI receipt recognition, the map and the MCP server are off and have no API keys. Avatars are served from the instance, and e-mail is off. The only outbound request ezBookkeeping makes is for currency exchange rates (Step 9).
- Built from source, with current fixes. ezBookkeeping 2.1.1 is built from its tagged release source with Go 1.27.3. Four of the Go libraries it uses received security fixes after 2.1.1 was released (
github.com/xuri/excelize/v2,golang.org/x/net,github.com/klauspost/compressandgithub.com/go-jose/go-jose/v4). The image uses the fixed releases, with no known reachable vulnerabilities at build time. There is no compiler on the image. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: ezBookkeeping 2.1.1 (/opt/ezbookkeeping, configuration in /etc/ezbookkeeping/ezbookkeeping.ini, database, pictures and log in /var/lib/ezbookkeeping), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu; Architecture: x86_64; Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet with internet access.
- A security group that allows TCP 22 (SSH), TCP 443 (ezBookkeeping) and TCP 80 (redirects to HTTPS) from the networks you and your users work from.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick ezBookkeeping 2.1.1 on Ubuntu 24.04 as the software version and select your Region, then choose Launch through EC2.
Choose m5.large, your key pair, a subnet and a security group as described above. ezBookkeeping itself needs very little, so a smaller instance such as t3.small also works.
Alternatively, launch with the AWS CLI. Replace the values in angle brackets:
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=ezbookkeeping}]'Connect to your instance
Connect over SSH as the ubuntu user, which is the SSH user for ezBookkeeping 2.1.1 on Ubuntu 24.04:
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot sets the host name and generates the certificate, the session signing key and the administrator account. Then ezBookkeeping and nginx start. Check the units and the first boot marker:
systemctl is-active ezbookkeeping-firstboot ezbookkeeping nginx
sudo test -f /var/lib/sac/data/ezbookkeeping-firstboot.done && echo FIRSTBOOT_DONEAll three units report active and the first boot marker is present:
active active active FIRSTBOOT_DONEOnly SSH and nginx listen on the network, and ezBookkeeping listens on localhost only. Confirm the listeners:
sudo ss -lntpH | awk '{print $4, $6}' | sed -E 's/users:\(\("([^"]+)".*/\1/' | grep -v '127.0.0.5' | sort -u0.0.0.0:22 sshd 0.0.0.0:443 nginx 0.0.0.0:80 nginx 127.0.0.1:8080 ezbookkeeping [::]:22 sshd [::]:443 nginx [::]:80 nginxGet your URL, user name and password
sudo cat /var/lib/sac/data/ezbookkeeping-credentials.txtOnly root can read the file. It holds the URL, the user name and the password of your instance's administrator account:
# ezBookkeeping 2.1.1 - generated on first boot by ezbookkeeping-firstboot.service
# This account is unique to this instance; none was baked into the image. Keep this file secret. EZBOOKKEEPING_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ EZBOOKKEEPING_USERNAME=admin EZBOOKKEEPING_PASSWORD=<EZBOOKKEEPING_PASSWORD>
#
# Open EZBOOKKEEPING_URL in a browser (self-signed certificate minted on this instance's first boot; port 80 redirects to # https) and sign in with EZBOOKKEEPING_USERNAME / EZBOOKKEEPING_PASSWORD. Change the password under User Settings. # Self-registration is off: this account owns the instance. The session signing key was generated for this instance too. # Self-test:
sudo ezbookkeeping-selftest Change the served name:
sudo ezbookkeeping-set-hostname <your-domain>
# User guide: https://secureanycloud.com/stacks/ezbookkeeping-2-1-1/The URL uses the instance's public DNS name from EC2 instance metadata. Opening https://<public-ip>/ redirects you to that name.
The image includes a self test. Through nginx, the self test:
- Signs in with the account, reads the profile, signs out and checks that the session is revoked.
- Checks registration, the request guards, the service's privileges and the metadata block.
Run it:
sudo ezbookkeeping-selftestSELFTEST-OK https 301 + healthz + healthz.json 2.1.1 + UI + profile API refused without a token + registration 404 + wrong password refused + per-instance account signed in, profile read, signed out, token revoked + 403 cross-site sign-in + 403 cross-origin POST + 444 unknown host + ezbookkeeping uid ezbookkeeping, no capabilities, NoNewPrivs, listening 127.0.0.1:8080 only + metadata 000 inside ezbookkeeping and nginx (control 200)Sign in
Open the EZBOOKKEEPING_URL in your browser. The certificate is self-signed, so your browser asks you to confirm it the first time. You can install your own certificate instead (Step 11). Sign in with EZBOOKKEEPING_USERNAME and EZBOOKKEEPING_PASSWORD. Create an account on the sign-in page does nothing, because self-registration is off.
After you sign in for the first time, open the user menu (top right), choose User Settings and change the password to one of your own. Under Two-Factor Authentication you can add an authenticator app. Under Basic Settings, set your default currency, language, time zone and the first day of the week.
The Overview shows this month's expense and income, your total assets, liabilities and net assets, a heatmap and calendar of your transactions, the expense category ranking and trend, and today's, this week's, this month's and this year's totals.
Accounts, categories and transactions
Start under Accounts (the card icon). Add your cash, bank, credit card, savings, investment and debt accounts, each with its currency and opening balance. A credit card or debt account counts as a liability. An account can hold sub-accounts, for example one bank account with several currencies.
Then set up Transaction Categories (user menu > Transaction Categories). ezBookkeeping offers a ready-made set of expense, income and transfer categories in two levels, or you can create your own. A transaction always uses a second-level category.
Record transactions with the + button. You can record an expense, income or transfer, with its time, category, amount, account, tags, description and, optionally, a picture of the receipt. Transaction List shows them by day, with filters for month, type, category, account and tag, and a search. The calendar and gallery views show the same transactions by date and by picture.
Import on the transaction list reads exports from banks and other apps: CSV, TSV and Excel files with a column mapping, OFX, QFX, QIF, IIF, Camt.052 and Camt.053, MT940, GnuCash, Firefly III, Beancount and others. To export all your transactions to CSV or TSV, use user menu > User Settings > Data Management.
Statistics and analysis
Statistics & Analysis (the pie chart icon) breaks income and expense down by account or by category for any period, as a pie, bar or radar chart. Trend Analysis and Asset Trends show them over time. Insights Explorer (the compass icon) lets you build your own queries over your transactions.
Use the API from a script
The web interface uses ezBookkeeping's JSON API, and your scripts can use it too. Sign in to /api/authorize.json for a session token, then send it as a bearer token. nginx's cross-site guard requires every API request to say where it comes from, so a script sends Origin with the instance's own URL.
This example signs in with the account from the credentials file, reads the profile and the number of accounts, and signs out again:
F=/var/lib/sac/data/ezbookkeeping-credentials.txt
H=$(sudo sed -n 's#^EZBOOKKEEPING_URL=https://\([^/]*\)/$#\1#p' "$F") API=(-sk --resolve "$H:443:127.0.0.1" -H "Origin: https://$H" -H 'Content-Type: application/json')
TOKEN=$(sudo python3 -c 'import json,sys kv=dict(l.rstrip("\n").split("=",1) for l in open(sys.argv[1]) if "=" in l and not l.startswith("#")) print(json.dumps({"loginName": kv["EZBOOKKEEPING_USERNAME"], "password": kv["EZBOOKKEEPING_PASSWORD"]}))' "$F" \
| curl "${API[@]}" --data-binary @- "https://$H/api/authorize.json" | python3 -c 'import json,sys; print(json.load(sys.stdin)["result"]["token"])') AUTH=(-H @/dev/stdin) curl "${API[@]}" "${AUTH[@]}" "https://$H/api/v1/users/profile/get.json" <<<"Authorization: Bearer $TOKEN" | python3 -c 'import json,sys; r=json.load(sys.stdin)["result"]; print("signed in as", r["username"], "-", r["defaultCurrency"])' curl "${API[@]}" "${AUTH[@]}" "https://$H/api/v1/accounts/list.json" <<<"Authorization: Bearer $TOKEN" | python3 -c 'import json,sys; print(len(json.load(sys.stdin)["result"]), "accounts")' curl "${API[@]}" "${AUTH[@]}" "https://$H/api/logout.json" <<<"Authorization: Bearer $TOKEN" | python3 -c 'import json,sys; print("signed out" if json.load(sys.stdin)["success"] else "sign-out failed")'signed in as admin - USD 4 accounts signed outThe password and the token travel on standard input, so they do not appear in the process list. A session token is valid for 30 days unless you sign out. To list and revoke sessions, use user menu > User Settings > Device & Sessions. The 4 accounts line counts the accounts you have created. On a new instance it reads 0 accounts.
Exchange rates and other outbound features
ezBookkeeping converts between currencies with daily exchange rates. When you open the exchange rates page or a total needs converting, the instance fetches the rates from the European Central Bank ([exchange_rates] data_source = euro_central_bank in /etc/ezbookkeeping/ezbookkeeping.ini). The instance therefore needs outbound HTTPS. You can set other sources, such as several central banks, in the same file. Restart ezBookkeeping after you edit the file (sudo systemctl restart ezbookkeeping).
These features are off on the image. Each one needs your own account with the provider before you switch it on in the same file:
- AI recognition of receipt pictures and text, and the AI coding assistant (
[llm_image_recognition],[llm_text_recognition],[llm_coding_assistant]):llm_provideris empty and no API key is set. - Maps for transaction locations (
[map] map_provider): empty. - The MCP server for AI assistants (
[mcp] enable_mcp = false) and API tokens ([security] enable_api_token = false). - E-mail (
[mail] enable_smtp = false), which password reset and e-mail verification need.
More users
Self-registration is off, so the administrator account owns the instance. To let household members create their own accounts, switch registration on for a short time, then switch it off again once they have registered:
sudo sed -i 's/^enable_register = false/enable_register = true/' /etc/ezbookkeeping/ezbookkeeping.ini
sudo systemctl restart ezbookkeeping
# ... once everyone has registered:
sudo sed -i 's/^enable_register = true/enable_register = false/' /etc/ezbookkeeping/ezbookkeeping.ini
sudo systemctl restart ezbookkeepingEvery account keeps its own books. Accounts do not see each other's data.
Use your own domain and certificate
In your DNS, point <your-domain> (an A record) at the instance's public IP address. An Elastic IP keeps that address stable.
Then tell the instance to use the domain. This sets the name nginx accepts, the request guards and ezBookkeeping's [server] domain, and it generates a new self-signed certificate for the domain:
sudo ezbookkeeping-set-hostname <your-domain>For a certificate that your browser trusts, put the certificate chain for <your-domain> in /etc/ssl/ezbookkeeping/ezbookkeeping.crt and its private key in /etc/ssl/ezbookkeeping/ezbookkeeping.key (root only, mode 600). Then run sudo systemctl reload nginx. Alternatively, put the instance behind an Application Load Balancer with an AWS Certificate Manager certificate that forwards to port 443 on the instance.
Data and backups
Everything ezBookkeeping keeps is in /var/lib/ezbookkeeping: data/ezbookkeeping.db (the SQLite database with your accounts and transactions), storage/ (avatars and receipt pictures), log/ and secret_key (the session signing key). To back it up, stop ezBookkeeping briefly:
sudo systemctl stop ezbookkeeping
sudo tar -C /var/lib -czf /root/ezbookkeeping-backup.tgz ezbookkeeping
sudo systemctl start ezbookkeepingTo restore on a new instance, stop ezBookkeeping, extract the archive over /var/lib/ezbookkeeping (owner ezbookkeeping, mode 700; secret_key mode 600) and start it again. Restoring secret_key keeps existing sessions valid. Without it, everyone signs in again. An EBS snapshot of the instance's volume is also a complete backup.
Security checks
Confirm that nginx and ezBookkeeping are blocked from the instance metadata service:
for u in nginx ezbookkeeping; do printf '%s ' "$u"; systemctl show -p IPAddressDeny --value "$u" | tr ' ' '\n' | sort | tr '\n' ' '; echo; donenginx 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 ezbookkeeping 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128Confirm that ezBookkeeping runs as ezbookkeeping with no Linux capabilities and cannot gain any:
P=$(systemctl show -p MainPID --value ezbookkeeping) ps -o user= -p "$P" grep -E '^(CapEff|CapBnd|CapAmb|NoNewPrivs)' /proc/"$P"/statusezbookkeeping CapEff: 0000000000000000 CapBnd: 0000000000000000 CapAmb: 0000000000000000 NoNewPrivs: 1Registration is closed. nginx refuses an API request with no same-origin signal, a sign-in that another web site's page sends, and a change from a foreign Origin. A request for a host name that the instance does not serve gets no answer at all:
H=$(sudo sed -n 's#^EZBOOKKEEPING_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/ezbookkeeping-credentials.txt)
curl -sk -o /dev/null -w 'registration: %{http_code}\n' --resolve "$H:443:127.0.0.1" -X POST -H 'Content-Type: application/json' -H "Origin: https://$H" --data '{}' "https://$H/api/register.json"
curl -sk -o /dev/null -w 'no same-origin signal: %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/api/v1/users/profile/get.json"
curl -sk -o /dev/null -w 'cross-site sign-in: %{http_code}\n' --resolve "$H:443:127.0.0.1" -X POST -H 'Content-Type: application/json' -H 'Sec-Fetch-Site: cross-site' --data '{}' "https://$H/api/authorize.json"
curl -sk -o /dev/null -w 'cross-origin POST: %{http_code}\n' --resolve "$H:443:127.0.0.1" -X POST -H 'Content-Type: application/json' -H 'Origin: https://evil.example' --data '{}' "https://$H/api/v1/accounts/add.json"
curl -sk -o /dev/null -w 'unknown host name: %{http_code}\n' --resolve evil.example:443:127.0.0.1 https://evil.example/ || trueregistration: 404 no same-origin signal: 403 cross-site sign-in: 403 cross-origin POST: 403 unknown host name: 000Only root can read the credentials, the session signing key, the TLS key and ezBookkeeping's data. The ezbookkeeping service user can also read the session signing key and the data:
sudo stat -c '%a %U:%G %n' /var/lib/sac/data/ezbookkeeping-credentials.txt /var/lib/ezbookkeeping/secret_key /etc/ssl/ezbookkeeping/ezbookkeeping.key /etc/ezbookkeeping/ezbookkeeping.ini /var/lib/ezbookkeeping /var/lib/ezbookkeeping/data600 root:root /var/lib/sac/data/ezbookkeeping-credentials.txt 600 ezbookkeeping:ezbookkeeping /var/lib/ezbookkeeping/secret_key 600 root:root /etc/ssl/ezbookkeeping/ezbookkeeping.key 640 root:ezbookkeeping /etc/ezbookkeeping/ezbookkeeping.ini 700 ezbookkeeping:ezbookkeeping /var/lib/ezbookkeeping 700 ezbookkeeping:ezbookkeeping /var/lib/ezbookkeeping/dataAlso note the following:
- The password is stored only as ezBookkeeping's salted PBKDF2-SHA256 hash in the database. Change it in the browser (user menu > User Settings > Security Settings). The credentials file keeps the first password, so update or delete it afterwards.
- Failed sign-ins. After five failed sign-ins in a minute from one address, or for one user, ezBookkeeping refuses further attempts until the minute has passed, even with the right password. nginx also answers
429to a burst of sign-in requests. - Who can reach it. Open ports 80 and 443 in the security group only to the networks your users work from.
- The build.
/usr/share/doc/sac-ezbookkeeping/BUILD-INFO.txtrecords the release, the Go toolchain and the four dependency updates.go.mod,go.sumandgo-version-m.txtnext to it list every library in the program, andthird-party-dependencies.jsonlists the web interface's libraries. - Logs. nginx logs are in
/var/log/nginx(without query strings). ezBookkeeping logs to the journal (journalctl -u ezbookkeeping) and to/var/lib/ezbookkeeping/log/.
Keep it up to date
Ubuntu security updates install automatically. To apply all updates now:
sudo apt-get update && sudo apt-get upgrade -yNew ezBookkeeping releases arrive as new versions of this listing, built and checked the same way. To move to a new version, back up /var/lib/ezbookkeeping (Step 12), launch the new version and restore the backup. ezBookkeeping updates its database on start. Read the ezBookkeeping release notes before you move.
Support and licensing
ezBookkeeping is open source under the MIT licence (MaysWind and contributors). The licence text is in /usr/share/doc/sac-ezbookkeeping/, and the source code is at github.com/mayswind/ezbookkeeping. The Go and web libraries it is built with are open source, mainly under the MIT, Apache 2.0 and BSD licences. go-version-m.txt and third-party-dependencies.json in the same directory list them.
There is no per-user licence fee. The AMI price covers SecureAnyCloud's build, hardening, first boot setup and support.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24 hour response SLA
The other versions on this listing are covered in the accounting, HR and finance guide.
Deploy on AWS
Launch ezBookkeeping 2.1.1 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com