Fix bind-mount data dir permissions via entrypoint
Docker creates ./data root-owned on the host, but the app runs as the unprivileged node user, causing SQLITE_CANTOPEN on first deploy. New entrypoint starts as root, chowns DATA_DIR, then drops privileges with setpriv. Compose keeps the SQLite database in ./data next to the compose file. Also trims scripts/ and playwright artifacts from the image.
This commit is contained in:
@@ -18,6 +18,8 @@ tests
|
|||||||
.swarm
|
.swarm
|
||||||
.mcp.json
|
.mcp.json
|
||||||
ruvector.db
|
ruvector.db
|
||||||
|
.playwright-mcp
|
||||||
|
scripts
|
||||||
|
|
||||||
# Local env files
|
# Local env files
|
||||||
.env
|
.env
|
||||||
|
|||||||
+5
-1
@@ -20,7 +20,11 @@ COPY public/ ./public/
|
|||||||
# Owned by the unprivileged `node` user that ships with the base image.
|
# Owned by the unprivileged `node` user that ships with the base image.
|
||||||
RUN mkdir -p /app/data && chown -R node:node /app
|
RUN mkdir -p /app/data && chown -R node:node /app
|
||||||
|
|
||||||
USER node
|
# The entrypoint starts as root only to chown a bind-mounted DATA_DIR
|
||||||
|
# (Docker creates host dirs root-owned), then drops to `node` via setpriv.
|
||||||
|
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||||
|
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
|
|||||||
@@ -32,13 +32,17 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
SESSION_SECRET: "<long random string — e.g. openssl rand -hex 32>"
|
SESSION_SECRET: "<long random string — e.g. openssl rand -hex 32>"
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/app/data
|
- ./data:/app/data # database lives next to this compose file
|
||||||
# No public port mapping needed when Caddy shares a Docker network with
|
# No public port mapping needed when Caddy shares a Docker network with
|
||||||
# the app (recommended). For a host-level Caddy, map localhost only:
|
# the app (recommended). For a host-level Caddy, map localhost only:
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:3000:3000"
|
- "127.0.0.1:3000:3000"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The entrypoint chowns `./data` to the app's unprivileged `node` user on
|
||||||
|
startup, so a root-created bind-mount directory works out of the box. (Images
|
||||||
|
older than this note need a one-time `sudo chown -R 1000:1000 ./data`.)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
@@ -108,7 +112,7 @@ npm test # API tests (node:test + supertest)
|
|||||||
|
|
||||||
## Data
|
## Data
|
||||||
|
|
||||||
All state lives in a single SQLite file under `DATA_DIR`. In Docker this is `/app/data`, mounted from `./data` on the host. No external database or services are required (geocoding calls OpenStreetMap's Nominatim, which needs no API key; airport lookups use a bundled offline dataset).
|
All state lives in a single SQLite file under `DATA_DIR`. In Docker this is `/app/data`, bind-mounted from `./data` next to the compose file — back up that directory to back up all trips. No external database or services are required (geocoding calls OpenStreetMap's Nominatim, which needs no API key; airport lookups use a bundled offline dataset).
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -4,7 +4,9 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
volumes:
|
volumes:
|
||||||
# SQLite database persists on the host across container rebuilds.
|
# SQLite database lives in ./data next to this compose file and
|
||||||
|
# persists across container rebuilds. The entrypoint fixes ownership
|
||||||
|
# automatically (the app itself runs as the unprivileged `node` user).
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
environment:
|
environment:
|
||||||
# CHANGE THIS: set a long random string before exposing the app.
|
# CHANGE THIS: set a long random string before exposing the app.
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Bind-mounted data directories (e.g. ./data next to the compose file) are
|
||||||
|
# created root-owned by Docker, but the app runs as the unprivileged `node`
|
||||||
|
# user. When we start as root: fix ownership of DATA_DIR, then drop
|
||||||
|
# privileges. When started with a custom --user, just run as-is.
|
||||||
|
DATA_DIR="${DATA_DIR:-/app/data}"
|
||||||
|
|
||||||
|
if [ "$(id -u)" = "0" ]; then
|
||||||
|
mkdir -p "$DATA_DIR"
|
||||||
|
chown -R node:node "$DATA_DIR"
|
||||||
|
exec setpriv --reuid=node --regid=node --init-groups "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$@"
|
||||||
Reference in New Issue
Block a user