Installation
This chapter is for people running KakePhooa themselves (the Self-hosted Edition): from download to your first login, plus upgrades and recovery. Settings are covered in the "Configuration" chapter.
The Self-hosted Edition is free for a single person to use, with no time limit.
Support scope
It's maintained by one person, so replies may be slow. Not covered: your own Docker/Podman environment, reverse proxy, TLS certificates, domain setup and database tuning. Those are part of your hosting environment.
Problems with the software itself (it won't start after following these steps, or a feature doesn't match this Manual) are welcome. Please include the version number (see "Version" below).
Requirements
- A 64-bit Linux machine (bare metal, VM, cloud VPS, NAS or Raspberry Pi): use the
linux-x86_64tarball on x86_64 (linux/amd64) and thelinux-aarch64tarball on 64-bit ARM (linux/arm64, for example a Raspberry Pi 3 or later with a 64-bit OS). Or a 64-bit x86 Windows machine for the Windows build. 32-bit systems aren't supported. - On Linux: run the binary from the tarball directly, or run the ready-made container image in Docker or Podman with compose. On Windows: run the binary from the zip. On Windows and on Linux desktops (GNOME, XFCE) it can also keep running in the system tray (see "Keep it running in the system tray"). On macOS: run the same image in Docker Desktop (see "Install with Docker or Podman"); there's no native macOS build.
- Nothing else to install for the database: by default the app runs its own PostgreSQL 16 (Embedded Database Mode). To use a database you already run, see "Using an external database" in the "Configuration" chapter.
- The Linux tarball needs no extra libraries: everything the built-in PostgreSQL uses is packed into the binary. The system must be Debian 12, Ubuntu 22.04 or newer (glibc 2.34 or later) and have time zone data (the
tzdatapackage). Regular installs always have it; only minimal container base images may not. Without it, startup fails withinvalid value for parameter "TimeZone"; install it withsudo apt install tzdata.
Keep it running in the system tray
If you don't want to keep a terminal or console window open, start it with kakephooa-tray (kakephooa-tray.exe on Windows), included in the tarball and the zip. It starts the app in the background, puts an icon in the system tray, and opens your default browser. Closing the browser doesn't stop the app.
It works on Windows and on the GNOME and XFCE desktops on Linux.
- Download, verify and unpack the release as in step 1 of "Install from the Linux tarball" or "Install on Windows".
- Double-click
kakephooa-tray(kakephooa-tray.exeon Windows) in the unpacked directory. If there's noconfig.tomlthere yet, it first copiesconfig.toml.exampletoconfig.toml: the embedded database, accepting connections from this computer only. To change settings, edit that file as described in the "Configuration" chapter, then press "Restart" in the menu. - The browser opens a "Starting KakePhooa" page, which moves on to the login page once the app is ready. The first start sets up the database, so the page says "Setting up the database" and it takes longer than usual, depending on the computer. If it takes too long, the page suggests choosing "View log" in the tray menu to see why.
Left-click the icon to open the app. The right-click menu:
| Item | What it does |
|---|---|
| Status | Starting, Running, Stopped or Failed to start |
| Version | The version that's running |
| Copy URL | A submenu of addresses to copy to the clipboard, for pasting on your phone or another device: "This computer" (public_url; shown as "Public URL" when it's a domain or Tailscale address), "Local network" (appears once "Allow phone connections" is ticked) and "Tailscale" (appears when Tailscale is installed and signed in on this computer and your phone can reach the app) |
| Open | Opens the app in your default browser |
| Connect a phone | Opens a page with the address for your phone and its QR code; see "Connecting a phone" below |
| View log | Opens logs/server.log, where all of the app's messages go |
| Allow phone connections | When ticked, other devices on the same network can reach the app too; see "Connecting a phone" below |
| Start at sign-in | When ticked, it starts when you sign in to the computer. When started this way it stays in the tray without opening the browser |
| Restart | Stops and starts the app again, for example after editing config.toml |
| Quit | Stops the app and removes the icon |
- Use either
kakephooa-trayor./server/start.bat, not both at once: whichever starts second fails because the database or the port is already in use. Double-clickingkakephooa-trayagain doesn't start a second copy; it only opens the browser. - When the status shows "Failed to start" or "Stopped", open the log and read the last few lines. The app doesn't restart by itself; fix the problem and press "Restart".
- "Start at sign-in" remembers this folder's location. After you move the folder or switch to a new version's directory, the menu shows it unticked; tick it again from the new location.
- The first time on Windows you may see "Windows protected your PC": press "More info", then "Run anyway". If your antivirus blocks
kakephooa-tray.exe, check the file as in "Verify the release" below, then add it to the allow list. - GNOME on Linux: Ubuntu works out of the box. Distributions without built-in tray support, such as Fedora and Debian, need the GNOME extension "AppIndicator and KStatusNotifierItem Support" first. Without a system tray the app still starts and opens the browser, just without an icon; to stop it, quit
kakephooa-tray(for example with Ctrl+C in the terminal).
Connecting a phone
By default the app only accepts connections from this computer, so your phone can't reach it.
- Tick "Allow phone connections" in the menu. It sets
[server] ipinconfig.tomlto0.0.0.0and restarts the app, then opens the "Connect a phone" page once it's ready. Unticking sets it back to127.0.0.1. - Put your phone on the same network as this computer (for example your home Wi-Fi) and scan the QR code on the page. The page follows your browser's language; switch between "中文" and "English" in the top-right corner.
- When Windows Firewall asks whether
server.exemay use the network, allow it.
Sign up for your account on this computer before ticking it: while the system has no accounts, the first person on the network to sign up becomes its User.
This connection uses http, so you can log in, record Expenses and check your budget, but you can't add it to your home screen, record offline or use Passkeys. For those, switch to an HTTPS address as described in the "Connecting from your phone" chapter; once public_url is an HTTPS address, the "Connect a phone" page shows that address instead.
If Tailscale is installed and signed in on this computer, the page also shows a Tailscale address: with Tailscale on, your phone can connect even when it's not on the same network. Once you've set up tailscale serve as described in the "Connecting from your phone" chapter, the page shows only its HTTPS address, and you don't need to tick "Allow phone connections". Not having Tailscale doesn't affect starting the app.
When IP is set as an environment variable (including in .env), it takes precedence and "Allow phone connections" can't be clicked.
Install with Docker or Podman
The ready-made image is ghcr.io/single9/kakephooa, for linux/amd64 and linux/arm64; it contains the binary from the Linux tarball. NAS apps such as Container Manager (Synology) and Container Station (QNAP) can pull it directly. The compose file and settings below are starting points, not a setup guaranteed to work everywhere; adapting them to your machine is part of hosting it yourself.
-
Create a directory (for example
kakephooa) and createconfig.tomlin it, withpublic_urlset to the address this machine will actually be reached at:[database] mode = "embedded" [server] public_url = "http://192.168.1.10:8080"Create
config.tomlbefore the first start: if the file doesn't exist, Docker creates a directory of that name and the container fails withIs a directory. To recover, delete that directory, create the file, and rundocker compose downbefore starting again. Every setting is listed inconfig.toml.examplein the Linux tarball and in the Configuration chapter. -
In the same directory, create
docker-compose.yml(see "Embedded database" under "Docker Compose examples" below), replacing1.1.1with the version to install (see the releases page). -
Start it:
docker compose up -d(orpodman-compose up -d). -
Wait a few seconds for the database migrations to finish, then open
http://<this machine's address>:8080. -
Sign up as usual to create your User (see "Your User" below).
Permissions on config.toml
The container runs as a non-root user, so config.toml has to be readable by other users. When it isn't, the container keeps restarting and its log shows config.toml 讀取失敗:Permission denied (os error 13). On this machine, run:
chmod 644 config.toml
Synology and some other NAS devices give files created or copied into a shared folder the folder's ACL, which only allows specific accounts, so the user inside the container can't read them. Sign in to the NAS over SSH and run the command above in the directory that holds config.toml to switch it back to ordinary file permissions.
Building the image yourself
If you'd rather not use the ready-made image, the Linux tarball includes a Dockerfile and a docker-compose.yml that wrap the tarball's binary in an image on your own machine, with nothing to compile. In the unpacked tarball's directory, copy config.toml.example to config.toml and run docker compose up -d --build. Without compose, run docker build -t kakephooa:<version> . to build the image. The Dockerfile does only this: start from debian:bookworm-slim, install ca-certificates, tzdata and wget, copy server and public/ to /app, and run as a non-root user listening on 0.0.0.0:8080. Change it as you like, for example to use another base image. Building the image downloads packages, so where containers can't reach the network (as on some NAS devices), use the ready-made image instead.
On macOS, install Docker Desktop and follow the same steps; Docker pulls the image for your Mac's architecture.
A reverse proxy and TLS aren't part of this compose file. You don't need TLS when you only use the system from a computer. To add it to a phone's home screen, record Expenses offline or use Passkeys, the address has to be an HTTPS domain; see the Connecting from your phone chapter.
Docker Compose examples
Both examples below work as a starting point; they differ only in where the database lives. Put the two files in the same directory, replace 1.1.1 with the version to install, set public_url to the address this machine will actually be reached at, and run docker compose up -d.
name: kakephooa fixes the compose project name. When the directory is renamed or moved and you start it again, it keeps using the same volume (for example kakephooa_data) instead of a new, empty one.
Embedded database (default)
The database runs inside the app's container, and its data lives in the data volume. This is the setup of the docker-compose.yml in the tarball.
docker-compose.yml:
name: kakephooa
services:
app:
image: ghcr.io/single9/kakephooa:1.1.1
restart: unless-stopped
volumes:
- ./config.toml:/app/config.toml:ro
- data:/app/data
ports:
- "8080:8080"
volumes:
data:
config.toml:
[database]
mode = "embedded"
[server]
public_url = "http://192.168.1.10:8080"
To back up, back up the data volume (see "Backing up the whole database" in the "Configuration" chapter).
External database
The database runs in a separate PostgreSQL container. This suits you if you'd rather manage PostgreSQL yourself, for example to back it up or tune it with tools you already use.
docker-compose.yml:
name: kakephooa
services:
app:
image: ghcr.io/single9/kakephooa:1.1.1
restart: unless-stopped
volumes:
- ./config.toml:/app/config.toml:ro
ports:
- "8080:8080"
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: kakephooa
POSTGRES_PASSWORD: change-me
POSTGRES_DB: kakephooa
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U kakephooa -d kakephooa"]
interval: 5s
timeout: 5s
retries: 10
volumes:
pgdata:
config.toml:
[database]
url = "postgres://kakephooa:change-me@db:5432/kakephooa"
[server]
public_url = "http://192.168.1.10:8080"
-
Replace
change-mein both files with the same password of your own. -
Don't keep
mode = "embedded"inconfig.toml: only one ofmodeandurlmay be set (see "Using an external database" in the "Configuration" chapter). -
dbdoesn't publish a port on the host; onlyappin the same compose file can reach it. -
Keep the
-h 127.0.0.1in the healthcheck: on first start PostgreSQL runs a temporary server that only accepts local socket connections while it initializes, sopg_isreadywithout-hreports ready too early andappfails to connect before the database is really up. -
To back up the whole database, use
pg_dump:docker compose exec -T db pg_dump -U kakephooa -d kakephooa --format=custom > kakephooa.dump
Install from the Linux tarball
-
Download the tarball for your machine's architecture from the releases page (
kakephooa-<version>-linux-<arch>.tar.gz, where<arch>isx86_64oraarch64; rununame -mif unsure), along withSHA256SUMSandSHA256SUMS.asc, and verify them (see "Verify the release" below). Then extract it:tar xzf kakephooa-<version>-linux-<arch>.tar.gz cd kakephooa-<version>-linux-<arch>The layout is fixed:
server,kakephooa-tray,public/,config.toml.example,LICENSE,README-install.md,README-install.en.md,THIRD-PARTY-NOTICES.md.serverlooks forpublic/in the directory you run it from, so keep the layout as it is. -
Copy
config.toml.exampletoconfig.tomland setpublic_url(or use environment variables instead; see the "Configuration" chapter). -
Start it:
./serverThe database migrations run on first start. The embedded database lives in a
data/directory next to where you runserver; it must be on a local Linux filesystem, not a Windows drive or a network share (see "The embedded database's data directory" in the "Configuration" chapter).
Install on Windows
-
Download
kakephooa-<version>-windows-x86_64.zipfrom the releases page, along withSHA256SUMSandSHA256SUMS.asc, verify them, and extract the zip. The layout is the same as the Linux tarball, withserver.exeinstead ofserver. -
Copy
config.toml.exampletoconfig.tomland setpublic_url. -
Double-click
start.batin the extracted folder. The window stays open afterserver.exeexits, so the error message remains readable. To start it from PowerShell instead:.\server.exeThe first time, Windows Firewall may ask whether
server.exemay use the network; allow it if other devices need to reach the app. The embedded database lives indata\in the folder you run it from. Wherever this chapter says./server, use.\server.exe.
Verify the release
Every tarball and zip comes with SHA256SUMS (checksums) and SHA256SUMS.asc (the maintainer's GPG signature), so you can confirm the download isn't corrupted and really comes from this project.
The release-signing key:
KakePhooa Releases <[email protected]>
Fingerprint: ACC6 7AF4 2A28 A134 C82C 9A1A E27C A53B 662A 823F
The public key is published alongside the releases as kakephooa-release-signing-pubkey.asc. Import it, then verify:
gpg --import kakephooa-release-signing-pubkey.asc
gpg --verify SHA256SUMS.asc SHA256SUMS
sha256sum -c --ignore-missing SHA256SUMS
On Windows without sha256sum, run Get-FileHash kakephooa-<version>-windows-x86_64.zip in PowerShell and compare the hash with the matching line in SHA256SUMS (gpg --verify works the same with Gpg4win).
The first time, gpg: WARNING: This key is not certified with a trusted signature is normal: it only means you imported the key without certifying it. What matters is Good signature from "KakePhooa Releases <[email protected]>" in the output, with a fingerprint matching the one above.
Your User
Sign up through the normal sign-up page. While there are no Users yet, the first sign-up is always accepted, whatever signup_policy says. That User is yours, and it's the only one a Self-hosted Edition deployment supports: further sign-ups are refused.
Running the software as a service that people outside your household or organization can sign up for isn't permitted by its license (clause 4 of LICENSE). If that's what you want, email [email protected].
Version
The current version (v<x.y.z>) is shown at the bottom of the account Settings page. Include it when reporting a problem.
Forgot password (Mail-less Mode)
By default the Self-hosted Edition doesn't send email (Mail-less Mode), so "Forgot password" can't send you a link. Set a new password with the built-in command instead:
# Docker/Podman:
docker compose exec app ./server set-password [email protected]
# Tarball:
./server set-password [email protected]
The password is read from standard input and isn't shown as you type. Once it's set, every existing session of that User is logged out; log in again with the new password.
Turning off Two-Step Verification for a User
If a User has Two-Step Verification on and has lost their authenticator app, their Passkeys and their Recovery Codes, turn it off for them with the built-in command:
# Docker/Podman:
docker compose exec app ./server reset-mfa [email protected]
# Tarball:
./server reset-mfa [email protected]
This removes the User's authenticator app and every Passkey, cancels all Recovery Codes and logs out every session of that User. They can then log in with their password; ask them to turn Two-Step Verification on again. If no User has that email, the command exits with status 1.
Upgrading
When a newer version is out, a notice appears at the top of the page after you log in (see "Checking for new versions" in the "Configuration" chapter).
- Back up the whole database first (see "Backing up the whole database" in the "Configuration" chapter).
- Download the new tarball or zip from the releases page and restart. With Docker/Podman, change the image version in
docker-compose.ymlto the new version and rundocker compose up -d. If you build the image yourself, copyconfig.tomlinto the new version's unpacked directory and rundocker compose up -d --buildthere. Database migrations run automatically on start. If you start it withkakephooa-tray, press "Quit" in its menu first; after starting from the new version's directory, tick "Start at sign-in" again. - Downgrading isn't supported. Data written by a newer version isn't guaranteed to be readable by an older one. The backup you took before upgrading is your way back.
Health check
GET /healthz reports whether the database is reachable. The healthcheck in docker-compose.yml uses it, and your own monitoring can call it too.
Usage statistics
"Usage Statistics" on the account settings page shows how many times this deployment is used each day:
- Home page views: how many times Home was opened.
- Expenses added on the web: how many Expenses were added in the web app, including ones recorded offline and synced later.
You can switch between the last 7, 30 and 90 days, and "Daily breakdown" lists the numbers for each day. Dates are in UTC, so a new day starts at 8 a.m. in Taiwan.
These numbers stay in your own database. Only counts are kept, not who did what, and nothing is sent anywhere.