Shorten .env.example; move the long explanations to the readme

76 lines of commentary for five settings was more wall than help. The
file now says what to set; the reasoning behind the data directory, and
the two ways of putting it under /root, live in the readme where there is
room for them.

Also brought the deployment section up to date: it still told people to
export secrets by hand and run docker-compose, with no word about the
first admin, which is now the step that decides whether you can log in at
all.

INKNOTE_DATA_DIR stays commented out — one .env serves both compose
files, so setting it would drag local development off ./data too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-08 13:17:30 +02:00
1 parent 4e93259c04
commit 6dcb77fa73
2 files changed
+71 -75

No files matched your search

+50 -9
View File
@@ -85,9 +85,42 @@ $INKNOTE_DATA_DIR/
```
Default is `./data` for development and `/srv/inknote` in production; override
with `INKNOTE_DATA_DIR`. The container runs as uid/gid `1000` so the files stay
owned by a normal user — set `INKNOTE_UID` / `INKNOTE_GID` if yours differ, and
create the directory with matching ownership before the first start.
with `INKNOTE_DATA_DIR`. It is a default, not an extra path — set it and the
default location is never touched.
**Create the directory before the first start.** Docker will happily create a
missing bind-mount source itself, but it creates it as `root:root`, and the
container runs as uid 1000 and then cannot write:
```bash
mkdir -p /srv/inknote/{db,uploads} && chown -R 1000:1000 /srv/inknote
```
The path has to start with `/` or `./`. Anything else Docker reads as the name
of a named volume, and a name containing a slash fails outright.
#### Keeping it under `/root`
`/root` is mode `0700`, so uid 1000 cannot pass through it — the server fails to
write no matter who owns the directory underneath. Two ways out:
```bash
# A) let uid 1000 traverse /root, without making it readable
mkdir -p /root/inknote/{db,uploads}
chown -R 1000:1000 /root/inknote
chmod o+x /root # traverse only — `ls /root` is still refused
# .env: INKNOTE_DATA_DIR=/root/inknote
# B) run the container as root instead — no chown, no chmod, but a break-out
# lands on root rather than on an unprivileged user
# .env: INKNOTE_DATA_DIR=/root/inknote
# INKNOTE_UID=0
# INKNOTE_GID=0
```
A is the better trade: it keeps the unprivileged container, which is the layer
that matters if the app is ever exploited. Neither is needed under `/srv` or
`/opt`, which is why those are the default.
> Back up the `.env` holding `ENCRYPTION_KEY` too. Note content is encrypted at
> rest, so a copy of the database without that key cannot be read.
@@ -111,14 +144,22 @@ touches production data.
### 2. Docker Deployment
```bash
# Generate a secure encryption key
export ENCRYPTION_KEY=$(openssl rand -hex 32)
export JWT_SECRET=$(openssl rand -base64 32)
docker-compose up -d
mkdir -p /srv/inknote/{db,uploads} && chown -R 1000:1000 /srv/inknote
cp .env.example .env # then fill it in, see below
docker login gitea.honeyy.duckdns.org
docker compose pull && docker compose up -d
docker compose logs inknote-server | head # says which account became admin
```
The server runs on port `4000`, the web UI on port `3000`.
`.env` needs `JWT_SECRET`, `ENCRYPTION_KEY` and — on a fresh install —
`INKNOTE_ADMIN_USER` plus `INKNOTE_ADMIN_PASSWORD`, since there is no sign-up to
create the first account with. On an install that already has accounts,
`INKNOTE_ADMIN_USER` alone decides which of them is promoted; without it the
oldest one is, which is rarely the one you meant.
The web UI listens on port `3000`, the API only inside the compose network. If a
reverse proxy sits in front, bind the port to localhost (`"127.0.0.1:3000:80"`),
otherwise the app is reachable on the host's address without TLS.
---