# cp .env.example .env — and fill it in. Docker Compose reads .env from this # directory, next to docker-compose.yml; nothing looks for it anywhere else. # # Back the finished .env up together with the database: without ENCRYPTION_KEY # the stored notes cannot be read again, and a backup of one without the other # is worth nothing. # Signs session tokens. Changing it logs everyone out. # openssl rand -hex 32 JWT_SECRET= # Encrypts note bodies at rest. LOSING THIS MEANS LOSING THE NOTES. # Must be exactly 32 bytes as hex. # openssl rand -hex 32 ENCRYPTION_KEY= # The first administrator. # # There is no public sign-up — accounts are created by an admin inside the app # under Account → Administration. These two settle who the first one is: # # empty database → the account is created from both values # existing users, → INKNOTE_ADMIN_USER names which of them becomes admin. # no admin yet Without it, the oldest account is promoted. # an admin exists → both are ignored entirely # # So they can never reset an existing password or take rights away; the worst # they can do on a running instance is nothing. INKNOTE_ADMIN_USER= INKNOTE_ADMIN_PASSWORD= # ── Where the data lives ── # A plain directory on the Docker host — a bind mount, not a Docker volume, so # that whatever backs up the host also backs up InkNote. Create it first; the # container runs unprivileged and cannot create it for you: # # mkdir -p /srv/inknote/{db,uploads} && chown -R 1000:1000 /srv/inknote # # INKNOTE_DATA_DIR=/srv/inknote # dev compose defaults to ./data # INKNOTE_UID=1000 # must own the directory above # INKNOTE_GID=1000 # # The path must start with / or ./ — anything else Docker reads as the name of # a named volume, and a name containing a slash simply fails. # # ── Putting it under /root ── # /root is normally mode 0700, and uid 1000 cannot even pass through it, so the # server would fail to write however the directory itself is owned. Two ways # out; the first keeps the unprivileged container and is the better trade: # # 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 still refused # 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: # INKNOTE_DATA_DIR=/root/inknote # INKNOTE_UID=0 # INKNOTE_GID=0 # # Neither is needed under /srv or /opt, which is why those are the default. # ── Optional tuning ── # Leave unset to keep the defaults shown. # MAX_VERSIONS_PER_NOTE=50 # versions kept per note # VERSION_RETENTION_DAYS=90 # and for how long # VERSION_COALESCE_MINUTES=5 # edits within this window share a version # # Only if the UI is served from a different host than the API. With a reverse # proxy in front, or the bundled nginx, leave it empty — same-origin needs none. # ALLOWED_ORIGINS=https://notes.example.com # # PORT is deliberately not here: the bundled nginx proxies to port 4000 by # name, so changing it would only break that.