mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
The TrueNAS page now walks through the catalog install field by field, how to generate the two secrets, where TrueNAS keeps the key and why that is not a backup, a dump and restore from the postgres container shell, and updates with forward-only migrations. The listing is marked as pending review, and the install-via-YAML values stay for the time until it is live. The deploy notes list what needs a matching truenas/apps pull request in the same release cycle: a new or stricter required variable, a port or health check change, a Postgres major or extension, and any other compose-shape change.
235 lines
13 KiB
Markdown
235 lines
13 KiB
Markdown
# HealthLog on TrueNAS
|
||
|
||
HealthLog runs on TrueNAS Community Edition (formerly SCALE) as a Docker app:
|
||
one container from `ghcr.io/mbombeck/healthlog` and one PostgreSQL database.
|
||
The app container is stateless. Everything you care about lives in the
|
||
database dataset and in one value you must keep yourself: the encryption key.
|
||
|
||
> **Status: pending catalog review.** A HealthLog app for the TrueNAS
|
||
> Community train has been prepared but is not listed yet. Until it is, use
|
||
> [Install via YAML](#before-the-listing-install-via-yaml) at the end of this
|
||
> page. This note goes away once the app appears under Apps, Discover Apps.
|
||
|
||
## Install from the Community train
|
||
|
||
1. Generate two secrets. Open the TrueNAS shell (System, Shell) and run the
|
||
command twice, once for each field:
|
||
|
||
```bash
|
||
openssl rand -hex 32
|
||
```
|
||
|
||
Each run prints 64 hex characters. Put both in your password manager
|
||
before you continue; see [Back up your encryption key](#back-up-your-encryption-key).
|
||
|
||
2. Open Apps, Discover Apps, search for **HealthLog** and choose Install.
|
||
3. Fill in the form:
|
||
|
||
| Field | What to enter |
|
||
| --------------------- | -------------------------------------------------------------------------------------------------------------- |
|
||
| Timezone | your timezone |
|
||
| Database Password | any strong password; only the two containers use it |
|
||
| Encryption Key | the first `openssl rand -hex 32` output |
|
||
| Token Signing Key | the second output. Changing it later signs everyone out |
|
||
| App URL | the address you open HealthLog at, with scheme and port, for example `http://192.168.1.10:30536` |
|
||
| Secure Cookies | leave off when you open HealthLog over `http://` on the LAN; turn on behind an HTTPS reverse proxy (see below) |
|
||
| WebUI Port | `30536` unless that port is taken |
|
||
| Postgres Data Storage | the default ixVolume, or a host path on a dataset you snapshot |
|
||
| Resources | keep memory at 1536 MB or more; the default of 4096 MB is fine |
|
||
|
||
4. Install. The first start applies the database migrations before the web
|
||
server listens, so give it a minute. The app shows as running once
|
||
`/api/health` answers.
|
||
5. Open the App URL. The first account you register becomes the admin.
|
||
|
||
The App URL has to match the address in the browser: passkeys, sign-in
|
||
redirects and the links in notifications are built from it. If you later put
|
||
HealthLog behind a reverse proxy with its own domain, change App URL to that
|
||
`https://` address.
|
||
|
||
Settings the form does not show go under Additional Environment Variables, for
|
||
example `UPDATE_CHECK_DISABLED=1`. The full list is in
|
||
[environment.md](environment.md). To use your own GeoLite2 databases, add the
|
||
directory under Additional Storage and point `GEOLITE2_DIR` at its mount path
|
||
([geolite2.md](geolite2.md)).
|
||
|
||
## Plain HTTP on the LAN
|
||
|
||
Most NAS installs are reached over `http://`. Leave **Secure Cookies** off for
|
||
that. With it on, the browser drops the sign-in cookie on a plain-HTTP page,
|
||
and the login page says so before you type a password: "Signing in cannot
|
||
complete on this page". Turn it on only when HealthLog is served over
|
||
`https://`, and set App URL to that `https://` address.
|
||
|
||
## Back up your encryption key
|
||
|
||
HealthLog encrypts tokens, notes, documents and in-database backups with the
|
||
Encryption Key. The key is not stored in the database. If the app is deleted
|
||
and installed again with a new key over the old database dataset, the data is
|
||
still there but cannot be read.
|
||
|
||
TrueNAS keeps the values you typed into the form with the app's configuration
|
||
on the apps pool. Treat that as a convenience, not as your copy: deleting the
|
||
app removes it, and a reinstall starts with empty fields. Keep the key in a
|
||
password manager or another place outside the NAS.
|
||
|
||
1. Open Apps, select HealthLog and choose Edit. The key is the Encryption Key
|
||
field under HealthLog Configuration.
|
||
2. Sign in as the admin and open Admin, Encryption. The **Back up your
|
||
encryption key** step shows the active key id and a fingerprint, the first
|
||
12 hex characters of SHA-256 over the key. Compare it with your copy:
|
||
|
||
```bash
|
||
echo -n "<your key>" | xxd -r -p | sha256sum | cut -c1-12
|
||
```
|
||
|
||
or paste the copy into **Check my copy**. It is compared and dropped, never
|
||
stored or logged.
|
||
|
||
3. Press **I have backed up this key**. Until you do, admins see a reminder on
|
||
the dashboard. After a key rotation the step comes back for the new key.
|
||
|
||
The catalog app sets `HEALTHLOG_PLATFORM=truenas`, so the step opens on these
|
||
instructions.
|
||
|
||
## Backups and restore
|
||
|
||
A restore needs two things: a backup of the database **and** the Encryption
|
||
Key that was active when it was taken. One without the other gets you nothing
|
||
back.
|
||
|
||
- **Snapshots.** Snapshot the Postgres Data Storage dataset (the ixVolume
|
||
lives under the apps pool, a host path wherever you put it). Stop the app
|
||
first, or the snapshot holds a database that was mid-write.
|
||
- **A dump.** Open a shell on the `postgres` container (Apps, HealthLog,
|
||
the container's Shell) and run:
|
||
|
||
```bash
|
||
pg_dump -U healthlog healthlog > /var/lib/postgresql/healthlog-$(date +%F).sql
|
||
```
|
||
|
||
The file lands in the Postgres Data Storage dataset, next to the database
|
||
files, so copy it off the NAS as well.
|
||
|
||
- **Off-host.** HealthLog can push encrypted nightly backups to any
|
||
S3-compatible bucket; see [backup-restore.md](../ops/backup-restore.md).
|
||
|
||
To restore a dump, run the app with the Encryption Key the dump was taken
|
||
under, put the dump file in the Postgres Data Storage dataset, and from the
|
||
`postgres` container shell replace the database with it:
|
||
|
||
```bash
|
||
psql -U healthlog -d postgres \
|
||
-c "DROP DATABASE healthlog WITH (FORCE);" \
|
||
-c "CREATE DATABASE healthlog OWNER healthlog;"
|
||
psql -v ON_ERROR_STOP=1 -U healthlog healthlog < /var/lib/postgresql/healthlog-YYYY-MM-DD.sql
|
||
```
|
||
|
||
Then restart the app. Restoring over the existing database without dropping
|
||
it first does not work: the tables the background queue creates on its own
|
||
refuse a plain overwrite.
|
||
|
||
## When the key does not match the database
|
||
|
||
At every start HealthLog checks that its key opens the data already in the
|
||
database. A missing or malformed key stops the container at once with
|
||
`HealthLog: ERROR — encryption configuration is invalid.` in its log. A
|
||
well-formed key that does not open the stored data is different: HealthLog
|
||
refuses to serve instead of failing on every encrypted value. Pages show
|
||
"HealthLog cannot open its data", API requests answer `503` with
|
||
`meta.errorCode` `encryption.key_mismatch`, `/api/health` answers `503` with
|
||
`reason: "encryption_key_mismatch"`, and no background jobs run. TrueNAS
|
||
shows the app as unhealthy rather than crashed, so the reason stays visible in
|
||
the app's logs.
|
||
|
||
Fix it one of three ways, then restart the app:
|
||
|
||
1. Put the original Encryption Key back (Apps, HealthLog, Edit).
|
||
2. Point the app at the database that belongs to this key, for example by
|
||
restoring that dataset.
|
||
3. Start fresh with an empty database. The old data and its backups need the
|
||
old key.
|
||
|
||
Before it records a key as the right one, the check reads the oldest
|
||
encrypted values in the database and records the key only if they open (or if
|
||
the database holds no encrypted value under that key id yet). When the data
|
||
proves neither, the app keeps serving, records nothing, writes a warning
|
||
starting with `[boot] Encryption key check inconclusive` to the log, and
|
||
checks again at the next start.
|
||
|
||
### Resetting the check
|
||
|
||
If you are certain the configured key is the one the data was written with,
|
||
and the app still refuses because the check recorded a different key earlier,
|
||
remove that record and restart. Open a shell on the `postgres` container and
|
||
run, with the key id the log names (`v1` unless you use `ENCRYPTION_KEYS`):
|
||
|
||
```bash
|
||
psql -U healthlog healthlog \
|
||
-c "DELETE FROM encryption_key_canaries WHERE key_id = 'v1';"
|
||
```
|
||
|
||
Then restart the app. It probes the stored data again and records the key
|
||
only if that data opens with it. If the key is in fact wrong, the app then
|
||
fails on every encrypted value instead of refusing clearly, so only do this
|
||
when you are sure. After a key rotation you never need this for the old key
|
||
id: the rotation script removes its record once no values remain under it.
|
||
|
||
On a first start there is no record to remove. If the check refuses there and
|
||
you are certain the key is right, add `ENCRYPTION_KEY_CHECK` = `warn` under
|
||
Additional Environment Variables as a last resort. The check still runs and
|
||
logs, `/api/health` reports `warning: "encryption_key_mismatch"` without
|
||
failing, the app serves, and no key is recorded until the check passes. This
|
||
switches off a safety check: with a wrong key every encrypted value fails and
|
||
new data is written under that key. Remove the setting once the key is
|
||
confirmed.
|
||
|
||
## Updates
|
||
|
||
The catalog follows HealthLog releases on its own: when a new image is
|
||
published, the TrueNAS catalog picks it up and the app shows an update. Take a
|
||
snapshot or a dump first, then update.
|
||
|
||
Database migrations run at every start, before the web server listens, and
|
||
only go forward. The start log says how many are pending. Going back to an
|
||
older version with Roll Back does not undo them, and an older version may not
|
||
start against a database a newer one has migrated. The way back is the
|
||
database backup you took before the update, restored together with the older
|
||
version.
|
||
|
||
The Postgres Image field offers the Postgres major versions the catalog
|
||
supports. Changing it runs a one-way upgrade of the database files, so back up
|
||
before you touch it.
|
||
|
||
## Before the listing: install via YAML
|
||
|
||
Until the catalog app is listed, install HealthLog with Apps, Discover Apps,
|
||
the menu next to Custom App, Install via YAML, using these values:
|
||
|
||
| Setting | Value |
|
||
| ------------ | ---------------------------------------------------------------------------------------- |
|
||
| Image | `ghcr.io/mbombeck/healthlog`, tag `X.Y.Z` (amd64 and arm64) |
|
||
| Port | `3000` (HTTP) |
|
||
| User | runs as uid `1001` by default; no extra capabilities, `no-new-privileges` is fine |
|
||
| Health check | `GET /api/health`, `200` when ready |
|
||
| Start period | at least 60 s, with more retries than the image's 5 × 30 s: migrations run at every boot |
|
||
| Database | PostgreSQL 16 or 18, no extensions, in its own container with its own dataset |
|
||
| Volumes | none for the app; the database gets its own dataset |
|
||
|
||
| Variable | Required | Notes |
|
||
| ----------------------- | -------- | ---------------------------------------------------------------------------------------- |
|
||
| `DATABASE_URL` | yes | `postgresql://<user>:<password>@<host>:5432/<db>` |
|
||
| `ENCRYPTION_KEY` | yes | 64 hex characters: `openssl rand -hex 32`. Keep a copy outside the NAS. |
|
||
| `API_TOKEN_HMAC_KEY` | yes | 64 hex characters, generated the same way. Changing it signs everyone out. |
|
||
| `NEXT_PUBLIC_APP_URL` | yes | the address people open, for example `http://truenas.local:30180` |
|
||
| `APP_URL` | yes | the same address |
|
||
| `SESSION_COOKIE_SECURE` | no | `false` for a LAN install served over plain `http://`; leave unset behind an HTTPS proxy |
|
||
| `HEALTHLOG_PLATFORM` | no | `truenas`: the admin's key backup step opens on the TrueNAS instructions |
|
||
|
||
`HEALTHLOG_PROCESS_TYPE` stays unset (`all`): one container serves the web app
|
||
and runs the background jobs. In a YAML install the key sits in the
|
||
environment block, so the place to copy it from is `ENCRYPTION_KEY` there.
|
||
To move from a YAML install to the catalog app later, take a dump, install the
|
||
catalog app with the same key, and restore the dump as described under
|
||
[Backups and restore](#backups-and-restore).
|