From 0298c62ec76bb5f68c083e81604956b05c845401 Mon Sep 17 00:00:00 2001 From: perf3ct Date: Tue, 13 May 2025 18:51:18 +0000 Subject: [PATCH 01/15] feat(ci): add rootless dockerfiles --- apps/server/Dockerfile.alpine.rootless | 65 +++++++++++++++++++++++++ apps/server/Dockerfile.rootless | 66 ++++++++++++++++++++++++++ docker-compose.rootless.yml | 31 ++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 apps/server/Dockerfile.alpine.rootless create mode 100644 apps/server/Dockerfile.rootless create mode 100644 docker-compose.rootless.yml diff --git a/apps/server/Dockerfile.alpine.rootless b/apps/server/Dockerfile.alpine.rootless new file mode 100644 index 000000000..ebafbabff --- /dev/null +++ b/apps/server/Dockerfile.alpine.rootless @@ -0,0 +1,65 @@ +FROM node:22.15.0-alpine AS builder +RUN corepack enable + +# Install native dependencies since we might be building cross-platform. +WORKDIR /usr/src/app +COPY ./dist/package.json ./dist/pnpm-lock.yaml ./docker/pnpm-workspace.yaml /usr/src/app/ +# We have to use --no-frozen-lockfile due to CKEditor patches +RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild + +FROM node:22.15.0-alpine +# Create a non-root user with configurable UID/GID +ARG USER=trilium +ARG UID=1001 +ARG GID=1001 +ENV USER=${USER} +ENV UID=${UID} +ENV GID=${GID} + +# Install runtime dependencies and create user with specific UID/GID +RUN apk add --no-cache dumb-init && \ + # Alpine uses addgroup/adduser (from busybox) instead of groupadd/useradd + addgroup -g ${GID} ${USER} && \ + adduser -u ${UID} -G ${USER} -s /bin/sh -D -h /home/${USER} ${USER} + +WORKDIR /home/${USER}/app +COPY ./dist /home/${USER}/app +RUN rm -rf /home/${USER}/app/node_modules/better-sqlite3 +COPY --from=builder /usr/src/app/node_modules/better-sqlite3 /home/${USER}/app/node_modules/better-sqlite3 +RUN chown -R ${USER}:${USER} /home/${USER} + +# Configure container +USER ${USER} +EXPOSE 8080 + +# By default, use UID/GID that was set during build +# These can be overridden at runtime +ENV TRILIUM_UID=${UID} +ENV TRILIUM_GID=${GID} +ENV TRILIUM_DATA_DIR=/home/${USER}/trilium-data + +# Use dumb-init as entrypoint to handle signals properly +ENTRYPOINT ["/usr/bin/dumb-init", "--"] + +# This script will handle UID/GID checks and start the app +CMD [ "sh", "-c", "\ +if [ \"${TRILIUM_UID}\" != \"$(id -u)\" ] || [ \"${TRILIUM_GID}\" != \"$(id -g)\" ]; then \ + echo \"Detected UID:GID mismatch\"; \ + if [ \"${TRILIUM_GID}\" != \"$(id -g)\" ]; then \ + echo \"ERROR: Cannot change GID at runtime in rootless mode.\"; \ + echo \" Please use docker run with --user ${TRILIUM_UID}:${TRILIUM_GID} instead.\"; \ + exit 1; \ + fi; \ + if [ \"${TRILIUM_UID}\" != \"$(id -u)\" ]; then \ + echo \"ERROR: Cannot change UID at runtime in rootless mode.\"; \ + echo \" Please use docker run with --user ${TRILIUM_UID}:${TRILIUM_GID} instead.\"; \ + exit 1; \ + fi; \ +fi; \ +# Make sure data directory has correct permissions \ +mkdir -p \"${TRILIUM_DATA_DIR}\"; \ +# Start the app \ +exec node ./main \ +" ] + +HEALTHCHECK --start-period=10s CMD node /home/${USER}/app/docker_healthcheck.js diff --git a/apps/server/Dockerfile.rootless b/apps/server/Dockerfile.rootless new file mode 100644 index 000000000..40f8cda23 --- /dev/null +++ b/apps/server/Dockerfile.rootless @@ -0,0 +1,66 @@ +FROM node:22.15.0-bullseye-slim AS builder +RUN corepack enable + +# Install native dependencies since we might be building cross-platform. +WORKDIR /usr/src/app/build +COPY ./dist/package.json ./dist/pnpm-lock.yaml ./docker/pnpm-workspace.yaml /usr/src/app/ +# We have to use --no-frozen-lockfile due to CKEditor patches +RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild + +FROM node:22.15.0-bullseye-slim +# Create a non-root user with configurable UID/GID +ARG USER=trilium +ARG UID=1001 +ARG GID=1001 +ENV USER=${USER} +ENV UID=${UID} +ENV GID=${GID} + +# Install only runtime dependencies +RUN rm -rf \ + /var/lib/apt/lists/* \ + /var/cache/apt/* && \ + # Create the user/group with the default UID/GID + groupadd -g ${GID} ${USER} && \ + useradd -u ${UID} -g ${USER} -s /bin/sh -m ${USER} + +WORKDIR /home/${USER}/app +COPY ./dist /home/${USER}/app +RUN rm -rf /home/${USER}/app/node_modules/better-sqlite3 +COPY --from=builder /usr/src/app/node_modules/better-sqlite3 /home/${USER}/app/node_modules/better-sqlite3 +RUN chown -R ${USER}:${USER} /home/${USER} + +# Configure container +USER ${USER} +EXPOSE 8080 + +# By default, use UID/GID that was set during build +# These can be overridden at runtime +ENV TRILIUM_UID=${UID} +ENV TRILIUM_GID=${GID} +ENV TRILIUM_DATA_DIR=/home/${USER}/trilium-data + +# This script will handle UID/GID remapping if needed and then start the app +CMD [ "sh", "-c", "\ +if [ \"${TRILIUM_UID}\" != \"$(id -u)\" ] || [ \"${TRILIUM_GID}\" != \"$(id -g)\" ]; then \ + echo \"Remapping user ${USER} to UID:GID ${TRILIUM_UID}:${TRILIUM_GID}\"; \ + # Use 'id -u' and 'id -g' to get current UID and GID \ + if [ \"${TRILIUM_GID}\" != \"$(id -g)\" ]; then \ + # Need root to modify user/group, but we can't use sudo, so we need to exit \ + echo \"ERROR: Cannot change GID at runtime in rootless mode.\"; \ + echo \" Please use docker run with --user ${TRILIUM_UID}:${TRILIUM_GID} instead.\"; \ + exit 1; \ + fi; \ + if [ \"${TRILIUM_UID}\" != \"$(id -u)\" ]; then \ + echo \"ERROR: Cannot change UID at runtime in rootless mode.\"; \ + echo \" Please use docker run with --user ${TRILIUM_UID}:${TRILIUM_GID} instead.\"; \ + exit 1; \ + fi; \ +fi; \ +# Make sure data directory has correct permissions \ +mkdir -p \"${TRILIUM_DATA_DIR}\"; \ +# Start the app \ +exec node ./main \ +" ] + +HEALTHCHECK --start-period=10s CMD node /home/${USER}/app/docker_healthcheck.js diff --git a/docker-compose.rootless.yml b/docker-compose.rootless.yml new file mode 100644 index 000000000..7d4afd545 --- /dev/null +++ b/docker-compose.rootless.yml @@ -0,0 +1,31 @@ +version: '3.8' + +# Running `docker-compose -f docker-compose.rootless.yml up` will create/use the "trilium-data" directory in the user home +# Run `TRILIUM_DATA_DIR=/path/of/your/choice docker-compose -f docker-compose.rootless.yml up` to set a different directory +# To run in the background, use `docker-compose -f docker-compose.rootless.yml up -d` +# To use the Alpine-based image, run with `TRILIUM_VARIANT=alpine docker-compose -f docker-compose.rootless.yml up` +services: + trilium: + # Optionally, replace `latest` with a version tag like `v0.90.3` + # Using `latest` may cause unintended updates to the container + image: triliumnext/notes:rootless + restart: unless-stopped + environment: + - TRILIUM_DATA_DIR=/home/trilium/trilium-data + # Set the desired UID/GID for the Trilium process. Will be used during docker run + # These should match the owner of your data directory on the host + - TRILIUM_UID=${TRILIUM_UID:-1001} + - TRILIUM_GID=${TRILIUM_GID:-1001} + # Use the specified UID/GID for the container process + user: ${TRILIUM_UID:-1001}:${TRILIUM_GID:-1001} + ports: + # By default, Trilium will be available at http://localhost:8080 + # It will also be accessible at http://:8080 + # You might want to limit this with something like Docker Networks, reverse proxies, or firewall rules + - '8080:8080' + volumes: + # Unless TRILIUM_DATA_DIR is set, the data will be stored in the "trilium-data" directory in the home directory. + # This can also be changed by replacing the line below with `- /path/of/your/choice:/home/trilium/trilium-data + - ${TRILIUM_DATA_DIR:-~/trilium-data}:/home/trilium/trilium-data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro From a05e126d7d0edc348f687450a4e98978824f3242 Mon Sep 17 00:00:00 2001 From: perf3ct Date: Wed, 21 May 2025 13:21:32 -0700 Subject: [PATCH 02/15] feat(docs): add documentation around using the rootless Docker image --- .../Using Docker.html | 109 +++++++++++++++- .../1. Installing the server/Using Docker.md | 118 +++++++++++++++++- 2 files changed, 225 insertions(+), 2 deletions(-) diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.html index 26e837d8b..a44d1454e 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.html @@ -76,4 +76,111 @@ docker inspect [container_name]

Note on timezones

If you are having timezone issues and you are not using docker-compose, you may need to add a TZ environment variable with the TZ identifier of - your local timezone.

\ No newline at end of file + your local timezone.

+

Rootless Docker Image

+

If you would prefer to run Trilium without having to run the Docker container + as root, you can use either of the provided Debian (default) + and Alpine-based images with the rootless tag. 

+

If you're unsure, stick to the “rootful” Docker image referenced above. +

+

Below are some commands to pull the rootless images:

# For Debian-based image
+docker pull triliumnext/notes:rootless
+
+# For Alpine-based image
+docker pull triliumnext/notes:rootless-alpine
+

Why Rootless?

+

Running containers as non-root is a security best practice that reduces + the potential impact of container breakouts. If an attacker manages to + escape the container, they'll only have the permissions of the non-root + user instead of full root access to the host.

+

How It Works

+

The rootless Trilium image:

+
    +
  1. Creates a non-root user (trilium) during build time
  2. +
  3. Configures the application to run as this non-root user
  4. +
  5. Allows runtime customization of the user's UID/GID via Docker's --user flag
  6. +
  7. Does not require a separate Docker entrypoint script
  8. +
+

Usage

+

Using docker-compose (Recommended)

# Run with default UID/GID (1000:1000)
+docker-compose -f docker-compose.rootless.yml up -d
+
+# Run with custom UID/GID (e.g., match your host user)
+TRILIUM_UID=$(id -u) TRILIUM_GID=$(id -g) docker-compose -f docker-compose.rootless.yml up -d
+
+# Specify a custom data directory
+TRILIUM_DATA_DIR=/path/to/your/data TRILIUM_UID=$(id -u) TRILIUM_GID=$(id -g) docker-compose -f docker-compose.rootless.yml up -d
+
+

Using Docker CLI

# Build the image
+docker build -t triliumnext/notes:rootless -f apps/server/Dockerfile.rootless .
+
+# Run with default UID/GID (1000:1000)
+docker run -d --name trilium -p 8080:8080 -v ~/trilium-data:/home/node/trilium-data triliumnext/notes:rootless
+
+# Run with custom UID/GID
+docker run -d --name trilium -p 8080:8080 --user $(id -u):$(id -g) -v ~/trilium-data:/home/node/trilium-data triliumnext/notes:rootless
+
+

Environment Variables

+
    +
  • TRILIUM_UID: UID to use for the container process (passed + to Docker's --user flag)
  • +
  • TRILIUM_GID: GID to use for the container process (passed + to Docker's --user flag)
  • +
  • TRILIUM_DATA_DIR: Path to the data directory inside the container + (default: /home/node/trilium-data)
  • +
+

Volume Permissions

+

If you encounter permission issues with the data volume, ensure that:

+
    +
  1. The host directory has appropriate permissions for the UID/GID you're + using
  2. +
  3. You're setting both TRILIUM_UID and TRILIUM_GID to + match the owner of the host directory
  4. +
# For example, if your data directory is owned by UID 1001 and GID 1001:
+TRILIUM_UID=1001 TRILIUM_GID=1001 docker-compose -f docker-compose.rootless.yml up -d
+
+

Considerations

+
    +
  • The container starts with a specific UID/GID which can be customized at + runtime
  • +
  • Unlike the traditional setup, this approach does not use a separate entrypoint + script with usermod/groupmod commands
  • +
  • The container cannot modify its own UID/GID at runtime, which is a security + feature of rootless containers
  • +
+

Available Rootless Images

+

Two rootless variants are provided:

+
    +
  1. Debian-based (default): Uses the Debian Bullseye Slim + base image +
      +
    • Dockerfile: apps/server/Dockerfile.rootless +
    • +
    • Recommended for most users
    • +
    +
  2. +
  3. Alpine-based: Uses the Alpine base image for smaller + size +
      +
    • Dockerfile: apps/server/Dockerfile.alpine.rootless +
    • +
    • Smaller image size, but may have compatibility issues with some systems
    • +
    +
  4. +
+

Building Custom Rootless Images

+

If you would prefer, you can also customize the UID/GID at build time:

# For Debian-based image with custom UID/GID
+docker build --build-arg USER=myuser --build-arg UID=1001 --build-arg GID=1001 \
+  -t triliumnext/notes:rootless-custom -f apps/server/Dockerfile.rootless .
+
+# For Alpine-based image with custom UID/GID
+docker build --build-arg USER=myuser --build-arg UID=1001 --build-arg GID=1001 \
+  -t triliumnext/notes:alpine-rootless-custom -f apps/server/Dockerfile.alpine.rootless .
+
+

Available build arguments:

+
    +
  • USER: Username for the non-root user (default: trilium)
  • +
  • UID: User ID for the non-root user (default: 1000)
  • +
  • GID: Group ID for the non-root user (default: 1000)
  • +
+

 

\ No newline at end of file diff --git a/docs/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.md b/docs/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.md index 5384f5b87..5b4010e40 100644 --- a/docs/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.md +++ b/docs/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.md @@ -117,4 +117,120 @@ The `--user` directive is unsupported. Instead, use the `USER_UID` and `USER_GID ### Note on timezones -If you are having timezone issues and you are not using docker-compose, you may need to add a `TZ` environment variable with the [TZ identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) of your local timezone. \ No newline at end of file +If you are having timezone issues and you are not using docker-compose, you may need to add a `TZ` environment variable with the [TZ identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) of your local timezone. + +## Rootless Docker Image + +If you would prefer to run Trilium without having to run the Docker container as `root`, you can use either of the provided Debian (default) and Alpine-based images with the `rootless` tag.  + +_**If you're unsure, stick to the “rootful” Docker image referenced above.**_ + +Below are some commands to pull the rootless images: + +```sh +# For Debian-based image +docker pull triliumnext/notes:rootless + +# For Alpine-based image +docker pull triliumnext/notes:rootless-alpine +``` + +### Why Rootless? + +Running containers as non-root is a security best practice that reduces the potential impact of container breakouts. If an attacker manages to escape the container, they'll only have the permissions of the non-root user instead of full root access to the host. + +### How It Works + +The rootless Trilium image: + +1. Creates a non-root user (`trilium`) during build time +2. Configures the application to run as this non-root user +3. Allows runtime customization of the user's UID/GID via Docker's `--user` flag +4. Does not require a separate Docker `entrypoint` script + +### Usage + +#### **Using docker-compose (Recommended)** + +``` +# Run with default UID/GID (1000:1000) +docker-compose -f docker-compose.rootless.yml up -d + +# Run with custom UID/GID (e.g., match your host user) +TRILIUM_UID=$(id -u) TRILIUM_GID=$(id -g) docker-compose -f docker-compose.rootless.yml up -d + +# Specify a custom data directory +TRILIUM_DATA_DIR=/path/to/your/data TRILIUM_UID=$(id -u) TRILIUM_GID=$(id -g) docker-compose -f docker-compose.rootless.yml up -d + +``` + +#### **Using Docker CLI** + +```sh +# Build the image +docker build -t triliumnext/notes:rootless -f apps/server/Dockerfile.rootless . + +# Run with default UID/GID (1000:1000) +docker run -d --name trilium -p 8080:8080 -v ~/trilium-data:/home/node/trilium-data triliumnext/notes:rootless + +# Run with custom UID/GID +docker run -d --name trilium -p 8080:8080 --user $(id -u):$(id -g) -v ~/trilium-data:/home/node/trilium-data triliumnext/notes:rootless + +``` + +### Environment Variables + +* `TRILIUM_UID`: UID to use for the container process (passed to Docker's `--user` flag) +* `TRILIUM_GID`: GID to use for the container process (passed to Docker's `--user` flag) +* `TRILIUM_DATA_DIR`: Path to the data directory inside the container (default: `/home/node/trilium-data`) + +### Volume Permissions + +If you encounter permission issues with the data volume, ensure that: + +1. The host directory has appropriate permissions for the UID/GID you're using +2. You're setting both `TRILIUM_UID` and `TRILIUM_GID` to match the owner of the host directory + +```sh +# For example, if your data directory is owned by UID 1001 and GID 1001: +TRILIUM_UID=1001 TRILIUM_GID=1001 docker-compose -f docker-compose.rootless.yml up -d + +``` + +### Considerations + +* The container starts with a specific UID/GID which can be customized at runtime +* Unlike the traditional setup, this approach does not use a separate entrypoint script with `usermod`/`groupmod` commands +* The container cannot modify its own UID/GID at runtime, which is a security feature of rootless containers + +### Available Rootless Images + +Two rootless variants are provided: + +1. **Debian-based** (default): Uses the Debian Bullseye Slim base image + * Dockerfile: `apps/server/Dockerfile.rootless` + * Recommended for most users +2. **Alpine-based**: Uses the Alpine base image for smaller size + * Dockerfile: `apps/server/Dockerfile.alpine.rootless` + * Smaller image size, but may have compatibility issues with some systems + +### Building Custom Rootless Images + +If you would prefer, you can also customize the UID/GID at build time: + +``` +# For Debian-based image with custom UID/GID +docker build --build-arg USER=myuser --build-arg UID=1001 --build-arg GID=1001 \ + -t triliumnext/notes:rootless-custom -f apps/server/Dockerfile.rootless . + +# For Alpine-based image with custom UID/GID +docker build --build-arg USER=myuser --build-arg UID=1001 --build-arg GID=1001 \ + -t triliumnext/notes:alpine-rootless-custom -f apps/server/Dockerfile.alpine.rootless . + +``` + +Available build arguments: + +* `USER`: Username for the non-root user (default: trilium) +* `UID`: User ID for the non-root user (default: 1000) +* `GID`: Group ID for the non-root user (default: 1000) \ No newline at end of file From d73a289a05d0b3dfb8e64207e96d668ba788c09e Mon Sep 17 00:00:00 2001 From: perfectra1n Date: Wed, 21 May 2025 15:40:21 -0700 Subject: [PATCH 03/15] feat(docker): move from inline script to entrypoint --- apps/server/Dockerfile.alpine.rootless | 26 ++++++------------------ apps/server/Dockerfile.rootless | 28 ++++++-------------------- apps/server/rootless-entrypoint.sh | 26 ++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 42 deletions(-) create mode 100755 apps/server/rootless-entrypoint.sh diff --git a/apps/server/Dockerfile.alpine.rootless b/apps/server/Dockerfile.alpine.rootless index ebafbabff..11946e78b 100644 --- a/apps/server/Dockerfile.alpine.rootless +++ b/apps/server/Dockerfile.alpine.rootless @@ -41,25 +41,11 @@ ENV TRILIUM_DATA_DIR=/home/${USER}/trilium-data # Use dumb-init as entrypoint to handle signals properly ENTRYPOINT ["/usr/bin/dumb-init", "--"] -# This script will handle UID/GID checks and start the app -CMD [ "sh", "-c", "\ -if [ \"${TRILIUM_UID}\" != \"$(id -u)\" ] || [ \"${TRILIUM_GID}\" != \"$(id -g)\" ]; then \ - echo \"Detected UID:GID mismatch\"; \ - if [ \"${TRILIUM_GID}\" != \"$(id -g)\" ]; then \ - echo \"ERROR: Cannot change GID at runtime in rootless mode.\"; \ - echo \" Please use docker run with --user ${TRILIUM_UID}:${TRILIUM_GID} instead.\"; \ - exit 1; \ - fi; \ - if [ \"${TRILIUM_UID}\" != \"$(id -u)\" ]; then \ - echo \"ERROR: Cannot change UID at runtime in rootless mode.\"; \ - echo \" Please use docker run with --user ${TRILIUM_UID}:${TRILIUM_GID} instead.\"; \ - exit 1; \ - fi; \ -fi; \ -# Make sure data directory has correct permissions \ -mkdir -p \"${TRILIUM_DATA_DIR}\"; \ -# Start the app \ -exec node ./main \ -" ] +# Copy the entrypoint script +COPY rootless-entrypoint.sh /home/${USER}/app/ +RUN chmod +x /home/${USER}/app/rootless-entrypoint.sh + +# Use the entrypoint script +CMD ["/home/${USER}/app/rootless-entrypoint.sh"] HEALTHCHECK --start-period=10s CMD node /home/${USER}/app/docker_healthcheck.js diff --git a/apps/server/Dockerfile.rootless b/apps/server/Dockerfile.rootless index 40f8cda23..950ccee46 100644 --- a/apps/server/Dockerfile.rootless +++ b/apps/server/Dockerfile.rootless @@ -40,27 +40,11 @@ ENV TRILIUM_UID=${UID} ENV TRILIUM_GID=${GID} ENV TRILIUM_DATA_DIR=/home/${USER}/trilium-data -# This script will handle UID/GID remapping if needed and then start the app -CMD [ "sh", "-c", "\ -if [ \"${TRILIUM_UID}\" != \"$(id -u)\" ] || [ \"${TRILIUM_GID}\" != \"$(id -g)\" ]; then \ - echo \"Remapping user ${USER} to UID:GID ${TRILIUM_UID}:${TRILIUM_GID}\"; \ - # Use 'id -u' and 'id -g' to get current UID and GID \ - if [ \"${TRILIUM_GID}\" != \"$(id -g)\" ]; then \ - # Need root to modify user/group, but we can't use sudo, so we need to exit \ - echo \"ERROR: Cannot change GID at runtime in rootless mode.\"; \ - echo \" Please use docker run with --user ${TRILIUM_UID}:${TRILIUM_GID} instead.\"; \ - exit 1; \ - fi; \ - if [ \"${TRILIUM_UID}\" != \"$(id -u)\" ]; then \ - echo \"ERROR: Cannot change UID at runtime in rootless mode.\"; \ - echo \" Please use docker run with --user ${TRILIUM_UID}:${TRILIUM_GID} instead.\"; \ - exit 1; \ - fi; \ -fi; \ -# Make sure data directory has correct permissions \ -mkdir -p \"${TRILIUM_DATA_DIR}\"; \ -# Start the app \ -exec node ./main \ -" ] +# Copy the entrypoint script +COPY rootless-entrypoint.sh /home/${USER}/app/ +RUN chmod +x /home/${USER}/app/rootless-entrypoint.sh + +# Use the entrypoint script +CMD ["/home/${USER}/app/rootless-entrypoint.sh"] HEALTHCHECK --start-period=10s CMD node /home/${USER}/app/docker_healthcheck.js diff --git a/apps/server/rootless-entrypoint.sh b/apps/server/rootless-entrypoint.sh new file mode 100755 index 000000000..0828cf24f --- /dev/null +++ b/apps/server/rootless-entrypoint.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# Rootless entrypoint script for Trilium Notes +# Works with both Debian and Alpine-based images + +# Check if runtime UID/GID match the expected values +if [ "${TRILIUM_UID}" != "$(id -u)" ] || [ "${TRILIUM_GID}" != "$(id -g)" ]; then + echo "Detected UID:GID mismatch (current: $(id -u):$(id -g), expected: ${TRILIUM_UID}:${TRILIUM_GID})" + # Check GID mismatch + if [ "${TRILIUM_GID}" != "$(id -g)" ]; then + echo "ERROR: Cannot change GID at runtime in rootless mode." + echo " Please use docker run with --user ${TRILIUM_UID}:${TRILIUM_GID} instead." + exit 1 + fi + # Check UID mismatch + if [ "${TRILIUM_UID}" != "$(id -u)" ]; then + echo "ERROR: Cannot change UID at runtime in rootless mode." + echo " Please use docker run with --user ${TRILIUM_UID}:${TRILIUM_GID} instead." + exit 1 + fi +fi + +# Make sure data directory has correct permissions +mkdir -p "${TRILIUM_DATA_DIR}" + +# Start the app +exec node ./main From 436fc4c943b4cc17351dfa21d7c2ad43eba7eead Mon Sep 17 00:00:00 2001 From: perfectra1n Date: Wed, 21 May 2025 15:42:30 -0700 Subject: [PATCH 04/15] fix(docker): make the rootless entrypoint print more useful information if uid/gid are wrong --- apps/server/rootless-entrypoint.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/server/rootless-entrypoint.sh b/apps/server/rootless-entrypoint.sh index 0828cf24f..9f4898df3 100755 --- a/apps/server/rootless-entrypoint.sh +++ b/apps/server/rootless-entrypoint.sh @@ -8,13 +8,15 @@ if [ "${TRILIUM_UID}" != "$(id -u)" ] || [ "${TRILIUM_GID}" != "$(id -g)" ]; the # Check GID mismatch if [ "${TRILIUM_GID}" != "$(id -g)" ]; then echo "ERROR: Cannot change GID at runtime in rootless mode." - echo " Please use docker run with --user ${TRILIUM_UID}:${TRILIUM_GID} instead." + echo " Current GID: $(id -g), Expected GID: ${TRILIUM_GID}" + echo " Please use docker run with --user $(id -u):$(id -g) instead." exit 1 fi # Check UID mismatch if [ "${TRILIUM_UID}" != "$(id -u)" ]; then echo "ERROR: Cannot change UID at runtime in rootless mode." - echo " Please use docker run with --user ${TRILIUM_UID}:${TRILIUM_GID} instead." + echo " Current UID: $(id -u), Expected UID: ${TRILIUM_UID}" + echo " Please use docker run with --user $(id -u):$(id -g) instead." exit 1 fi fi From 093cd5c53fece9bd381b41051b42797d9052b486 Mon Sep 17 00:00:00 2001 From: perf3ct Date: Thu, 22 May 2025 12:49:38 -0700 Subject: [PATCH 05/15] fix(docker): fix when we copy and chmod the entrypoint for rootless images --- apps/server/Dockerfile.alpine.rootless | 7 +++---- apps/server/Dockerfile.rootless | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/server/Dockerfile.alpine.rootless b/apps/server/Dockerfile.alpine.rootless index 11946e78b..e161e5473 100644 --- a/apps/server/Dockerfile.alpine.rootless +++ b/apps/server/Dockerfile.alpine.rootless @@ -24,6 +24,9 @@ RUN apk add --no-cache dumb-init && \ WORKDIR /home/${USER}/app COPY ./dist /home/${USER}/app +# Also copy the rootless entrypoint script +COPY rootless-entrypoint.sh /home/${USER}/app/ +RUN chmod +x /home/${USER}/app/rootless-entrypoint.sh RUN rm -rf /home/${USER}/app/node_modules/better-sqlite3 COPY --from=builder /usr/src/app/node_modules/better-sqlite3 /home/${USER}/app/node_modules/better-sqlite3 RUN chown -R ${USER}:${USER} /home/${USER} @@ -41,10 +44,6 @@ ENV TRILIUM_DATA_DIR=/home/${USER}/trilium-data # Use dumb-init as entrypoint to handle signals properly ENTRYPOINT ["/usr/bin/dumb-init", "--"] -# Copy the entrypoint script -COPY rootless-entrypoint.sh /home/${USER}/app/ -RUN chmod +x /home/${USER}/app/rootless-entrypoint.sh - # Use the entrypoint script CMD ["/home/${USER}/app/rootless-entrypoint.sh"] diff --git a/apps/server/Dockerfile.rootless b/apps/server/Dockerfile.rootless index 950ccee46..a08f648c0 100644 --- a/apps/server/Dockerfile.rootless +++ b/apps/server/Dockerfile.rootless @@ -26,6 +26,9 @@ RUN rm -rf \ WORKDIR /home/${USER}/app COPY ./dist /home/${USER}/app +# Also copy the rootless entrypoint script +COPY rootless-entrypoint.sh /home/${USER}/app/ +RUN chmod +x /home/${USER}/app/rootless-entrypoint.sh RUN rm -rf /home/${USER}/app/node_modules/better-sqlite3 COPY --from=builder /usr/src/app/node_modules/better-sqlite3 /home/${USER}/app/node_modules/better-sqlite3 RUN chown -R ${USER}:${USER} /home/${USER} @@ -40,10 +43,6 @@ ENV TRILIUM_UID=${UID} ENV TRILIUM_GID=${GID} ENV TRILIUM_DATA_DIR=/home/${USER}/trilium-data -# Copy the entrypoint script -COPY rootless-entrypoint.sh /home/${USER}/app/ -RUN chmod +x /home/${USER}/app/rootless-entrypoint.sh - # Use the entrypoint script CMD ["/home/${USER}/app/rootless-entrypoint.sh"] From 6de074a976fbdb70abee3fb23731a3b2afc284a1 Mon Sep 17 00:00:00 2001 From: perf3ct Date: Thu, 22 May 2025 22:47:29 +0000 Subject: [PATCH 06/15] fix(docker): have the container fill in the user variable --- apps/server/Dockerfile.alpine.rootless | 2 +- apps/server/Dockerfile.rootless | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/server/Dockerfile.alpine.rootless b/apps/server/Dockerfile.alpine.rootless index e161e5473..7105d1609 100644 --- a/apps/server/Dockerfile.alpine.rootless +++ b/apps/server/Dockerfile.alpine.rootless @@ -45,6 +45,6 @@ ENV TRILIUM_DATA_DIR=/home/${USER}/trilium-data ENTRYPOINT ["/usr/bin/dumb-init", "--"] # Use the entrypoint script -CMD ["/home/${USER}/app/rootless-entrypoint.sh"] +CMD /home/${USER}/app/rootless-entrypoint.sh HEALTHCHECK --start-period=10s CMD node /home/${USER}/app/docker_healthcheck.js diff --git a/apps/server/Dockerfile.rootless b/apps/server/Dockerfile.rootless index a08f648c0..541e970e5 100644 --- a/apps/server/Dockerfile.rootless +++ b/apps/server/Dockerfile.rootless @@ -44,6 +44,6 @@ ENV TRILIUM_GID=${GID} ENV TRILIUM_DATA_DIR=/home/${USER}/trilium-data # Use the entrypoint script -CMD ["/home/${USER}/app/rootless-entrypoint.sh"] +CMD /home/${USER}/app/rootless-entrypoint.sh HEALTHCHECK --start-period=10s CMD node /home/${USER}/app/docker_healthcheck.js From aa8fd6dbd1c66d34bc6cd73835e3af0d0129fd87 Mon Sep 17 00:00:00 2001 From: perf3ct Date: Thu, 22 May 2025 15:54:10 -0700 Subject: [PATCH 07/15] feat(docker): add to the rootless docker docs --- .../Using Docker.html | 18 +++--- docs/User Guide/!!!meta.json | 59 ++++++++----------- .../1. Installing the server/Using Docker.md | 13 ++-- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.html index a44d1454e..60e5d049e 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.html @@ -78,12 +78,17 @@ docker inspect [container_name] you may need to add a TZ environment variable with the TZ identifier of your local timezone.

Rootless Docker Image

+

If you would prefer to run Trilium without having to run the Docker container as root, you can use either of the provided Debian (default) and Alpine-based images with the rootless tag. 

If you're unsure, stick to the “rootful” Docker image referenced above.

-

Below are some commands to pull the rootless images:

# For Debian-based image
+

Below are some commands to pull the rootless images:

# For Debian-based image
 docker pull triliumnext/notes:rootless
 
 # For Alpine-based image
@@ -111,14 +116,14 @@ TRILIUM_UID=$(id -u) TRILIUM_GID=$(id -g) docker-compose -f docker-compose.rootl
 # Specify a custom data directory
 TRILIUM_DATA_DIR=/path/to/your/data TRILIUM_UID=$(id -u) TRILIUM_GID=$(id -g) docker-compose -f docker-compose.rootless.yml up -d
 
-

Using Docker CLI

# Build the image
+

Using Docker CLI

# Build the image
 docker build -t triliumnext/notes:rootless -f apps/server/Dockerfile.rootless .
 
 # Run with default UID/GID (1000:1000)
-docker run -d --name trilium -p 8080:8080 -v ~/trilium-data:/home/node/trilium-data triliumnext/notes:rootless
+docker run -d --name trilium -p 8080:8080 -v ~/trilium-data:/home/trilium/trilium-data triliumnext/notes:rootless
 
 # Run with custom UID/GID
-docker run -d --name trilium -p 8080:8080 --user $(id -u):$(id -g) -v ~/trilium-data:/home/node/trilium-data triliumnext/notes:rootless
+docker run -d --name trilium -p 8080:8080 --user $(id -u):$(id -g) -v ~/trilium-data:/home/trilium/trilium-data triliumnext/notes:rootless
 

Environment Variables

    @@ -136,7 +141,7 @@ docker run -d --name trilium -p 8080:8080 --user $(id -u):$(id -g) -v ~/trilium- using
  • You're setting both TRILIUM_UID and TRILIUM_GID to match the owner of the host directory
  • -
    # For example, if your data directory is owned by UID 1001 and GID 1001:
    +
    # For example, if your data directory is owned by UID 1001 and GID 1001:
     TRILIUM_UID=1001 TRILIUM_GID=1001 docker-compose -f docker-compose.rootless.yml up -d
     

    Considerations

    @@ -182,5 +187,4 @@ docker build --build-arg USER=myuser --build-arg UID=1001 --build-arg GID=1001 \
  • USER: Username for the non-root user (default: trilium)
  • UID: User ID for the non-root user (default: 1000)
  • GID: Group ID for the non-root user (default: 1000)
  • -
-

 

\ No newline at end of file + \ No newline at end of file diff --git a/docs/User Guide/!!!meta.json b/docs/User Guide/!!!meta.json index 3acbe598c..f3594bb61 100644 --- a/docs/User Guide/!!!meta.json +++ b/docs/User Guide/!!!meta.json @@ -8521,191 +8521,184 @@ { "type": "relation", "name": "internalLink", - "value": "_help_YKWqdJhzi2VY", + "value": "OFXdgB2nNk1F", "isInheritable": false, "position": 50 }, { "type": "relation", "name": "internalLink", - "value": "OFXdgB2nNk1F", + "value": "BlN9DFI679QC", "isInheritable": false, "position": 60 }, { "type": "relation", "name": "internalLink", - "value": "BlN9DFI679QC", + "value": "vZWERwf8U3nx", "isInheritable": false, "position": 70 }, { "type": "relation", "name": "internalLink", - "value": "vZWERwf8U3nx", + "value": "oPVyFC7WL2Lp", "isInheritable": false, "position": 80 }, { "type": "relation", "name": "internalLink", - "value": "oPVyFC7WL2Lp", + "value": "GPERMystNGTB", "isInheritable": false, "position": 90 }, { "type": "relation", "name": "internalLink", - "value": "GPERMystNGTB", + "value": "CoFPLs3dRlXc", "isInheritable": false, "position": 100 }, { "type": "relation", "name": "internalLink", - "value": "CoFPLs3dRlXc", + "value": "AlhDUqhENtH7", "isInheritable": false, "position": 110 }, { "type": "relation", "name": "internalLink", - "value": "AlhDUqhENtH7", + "value": "pKK96zzmvBGf", "isInheritable": false, "position": 120 }, { "type": "relation", "name": "internalLink", - "value": "pKK96zzmvBGf", + "value": "WFGzWeUK6arS", "isInheritable": false, "position": 130 }, { "type": "relation", "name": "internalLink", - "value": "WFGzWeUK6arS", + "value": "0ESUbbAxVnoK", "isInheritable": false, "position": 140 }, { "type": "relation", "name": "internalLink", - "value": "0ESUbbAxVnoK", + "value": "J5Ex1ZrMbyJ6", "isInheritable": false, "position": 150 }, { "type": "relation", "name": "internalLink", - "value": "J5Ex1ZrMbyJ6", + "value": "d3fAXQ2diepH", "isInheritable": false, "position": 160 }, { "type": "relation", "name": "internalLink", - "value": "d3fAXQ2diepH", + "value": "MgibgPcfeuGz", "isInheritable": false, "position": 170 }, { "type": "relation", "name": "internalLink", - "value": "MgibgPcfeuGz", + "value": "m523cpzocqaD", "isInheritable": false, "position": 180 }, { "type": "relation", "name": "internalLink", - "value": "m523cpzocqaD", + "value": "9sRHySam5fXb", "isInheritable": false, "position": 190 }, { "type": "relation", "name": "internalLink", - "value": "9sRHySam5fXb", + "value": "u3YFHC9tQlpm", "isInheritable": false, "position": 200 }, { "type": "relation", "name": "internalLink", - "value": "u3YFHC9tQlpm", + "value": "R9pX4DGra2Vt", "isInheritable": false, "position": 210 }, { "type": "relation", "name": "internalLink", - "value": "R9pX4DGra2Vt", + "value": "iRwzGnHPzonm", "isInheritable": false, "position": 220 }, { "type": "relation", "name": "internalLink", - "value": "iRwzGnHPzonm", + "value": "BCkXAVs63Ttv", "isInheritable": false, "position": 230 }, { "type": "relation", "name": "internalLink", - "value": "BCkXAVs63Ttv", + "value": "47ZrP6FNuoG8", "isInheritable": false, "position": 240 }, { "type": "relation", "name": "internalLink", - "value": "47ZrP6FNuoG8", + "value": "KC1HB96bqqHX", "isInheritable": false, "position": 250 }, { "type": "relation", "name": "internalLink", - "value": "KC1HB96bqqHX", + "value": "BFvAtE74rbP6", "isInheritable": false, "position": 260 }, { "type": "relation", "name": "internalLink", - "value": "BFvAtE74rbP6", + "value": "bdUJEHsAPYQR", "isInheritable": false, "position": 270 }, { "type": "relation", "name": "internalLink", - "value": "bdUJEHsAPYQR", + "value": "AxshuNRegLAv", "isInheritable": false, "position": 280 }, { "type": "relation", "name": "internalLink", - "value": "AxshuNRegLAv", + "value": "81SGnPGMk7Xc", "isInheritable": false, "position": 290 }, - { - "type": "relation", - "name": "internalLink", - "value": "81SGnPGMk7Xc", - "isInheritable": false, - "position": 300 - }, { "type": "relation", "name": "internalLink", "value": "xWbu3jpNWapp", "isInheritable": false, - "position": 310 + "position": 300 }, { "type": "label", diff --git a/docs/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.md b/docs/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.md index 5b4010e40..0f711f89e 100644 --- a/docs/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.md +++ b/docs/User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker.md @@ -121,13 +121,16 @@ If you are having timezone issues and you are not using docker-compose, you may ## Rootless Docker Image +> [!NOTE] +> Please keep in mind that the data directory is at `/home/trilium/trilium-data` instead of the typical `/home/node/trilium-data`. This is because a new user is created and used to run Trilium within the rootless containers. + If you would prefer to run Trilium without having to run the Docker container as `root`, you can use either of the provided Debian (default) and Alpine-based images with the `rootless` tag.  _**If you're unsure, stick to the “rootful” Docker image referenced above.**_ Below are some commands to pull the rootless images: -```sh +``` # For Debian-based image docker pull triliumnext/notes:rootless @@ -166,15 +169,15 @@ TRILIUM_DATA_DIR=/path/to/your/data TRILIUM_UID=$(id -u) TRILIUM_GID=$(id -g) do #### **Using Docker CLI** -```sh +``` # Build the image docker build -t triliumnext/notes:rootless -f apps/server/Dockerfile.rootless . # Run with default UID/GID (1000:1000) -docker run -d --name trilium -p 8080:8080 -v ~/trilium-data:/home/node/trilium-data triliumnext/notes:rootless +docker run -d --name trilium -p 8080:8080 -v ~/trilium-data:/home/trilium/trilium-data triliumnext/notes:rootless # Run with custom UID/GID -docker run -d --name trilium -p 8080:8080 --user $(id -u):$(id -g) -v ~/trilium-data:/home/node/trilium-data triliumnext/notes:rootless +docker run -d --name trilium -p 8080:8080 --user $(id -u):$(id -g) -v ~/trilium-data:/home/trilium/trilium-data triliumnext/notes:rootless ``` @@ -191,7 +194,7 @@ If you encounter permission issues with the data volume, ensure that: 1. The host directory has appropriate permissions for the UID/GID you're using 2. You're setting both `TRILIUM_UID` and `TRILIUM_GID` to match the owner of the host directory -```sh +``` # For example, if your data directory is owned by UID 1001 and GID 1001: TRILIUM_UID=1001 TRILIUM_GID=1001 docker-compose -f docker-compose.rootless.yml up -d From aa10638fd80b8b8ab441c933b33a97456e94565d Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 25 May 2025 21:25:43 +0300 Subject: [PATCH 08/15] feat(nx/server): add build/run scripts for docker rootless --- apps/server/package.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/server/package.json b/apps/server/package.json index df48848cc..65d9cdbd2 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -149,6 +149,12 @@ }, "alpine": { "command": "docker build . -t triliumnext-alpine -f Dockerfile.alpine" + }, + "rootless-debian": { + "command": "docker build . -t triliumnext-rootless-debian -f Dockerfile.rootless" + }, + "rootless-alpine": { + "command": "docker build . -t triliumnext-rootless-alpine -f Dockerfile.alpine.rootless" } } }, @@ -164,6 +170,12 @@ }, "alpine": { "command": "docker run -p 8081:8080 triliumnext-alpine" + }, + "rootless-debian": { + "command": "docker run -p 8081:8080 triliumnext-rootless-debian" + }, + "rootless-alpine": { + "command": "docker run -p 8081:8080 triliumnext-rootless-alpine" } } }, From 84ab4dcb8b20aa0f51672eb0f8585e75823310dc Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 25 May 2025 21:45:42 +0300 Subject: [PATCH 09/15] chore(docker): format Dockerfiles --- apps/server/Dockerfile | 44 +++++++++++++++++------------------ apps/server/Dockerfile.alpine | 38 +++++++++++++++--------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/apps/server/Dockerfile b/apps/server/Dockerfile index 1e357f6fe..dbb021f58 100644 --- a/apps/server/Dockerfile +++ b/apps/server/Dockerfile @@ -1,28 +1,28 @@ FROM node:22.16.0-bullseye-slim AS builder - RUN corepack enable +RUN corepack enable - # Install native dependencies since we might be building cross-platform. - WORKDIR /usr/src/app/build - COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/ - # We have to use --no-frozen-lockfile due to CKEditor patches - RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild +# Install native dependencies since we might be building cross-platform. +WORKDIR /usr/src/app/build +COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/ +# We have to use --no-frozen-lockfile due to CKEditor patches +RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild FROM node:22.16.0-bullseye-slim - # Install only runtime dependencies - RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gosu && \ - rm -rf \ - /var/lib/apt/lists/* \ - /var/cache/apt/* +# Install only runtime dependencies +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + gosu && \ + rm -rf \ + /var/lib/apt/lists/* \ + /var/cache/apt/* - WORKDIR /usr/src/app - COPY ./dist /usr/src/app - RUN rm -rf /usr/src/app/node_modules/better-sqlite3 - COPY --from=builder /usr/src/app/node_modules/better-sqlite3 /usr/src/app/node_modules/better-sqlite3 - COPY ./start-docker.sh /usr/src/app +WORKDIR /usr/src/app +COPY ./dist /usr/src/app +RUN rm -rf /usr/src/app/node_modules/better-sqlite3 +COPY --from=builder /usr/src/app/node_modules/better-sqlite3 /usr/src/app/node_modules/better-sqlite3 +COPY ./start-docker.sh /usr/src/app - # Configure container - EXPOSE 8080 - CMD [ "sh", "./start-docker.sh" ] - HEALTHCHECK --start-period=10s CMD exec gosu node node /usr/src/app/docker_healthcheck.cjs \ No newline at end of file +# Configure container +EXPOSE 8080 +CMD [ "sh", "./start-docker.sh" ] +HEALTHCHECK --start-period=10s CMD exec gosu node node /usr/src/app/docker_healthcheck.cjs \ No newline at end of file diff --git a/apps/server/Dockerfile.alpine b/apps/server/Dockerfile.alpine index 24d0dcb12..3b96950f1 100644 --- a/apps/server/Dockerfile.alpine +++ b/apps/server/Dockerfile.alpine @@ -1,26 +1,26 @@ FROM node:22.16.0-alpine AS builder - RUN corepack enable +RUN corepack enable - # Install native dependencies since we might be building cross-platform. - WORKDIR /usr/src/app - COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/ - # We have to use --no-frozen-lockfile due to CKEditor patches - RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild +# Install native dependencies since we might be building cross-platform. +WORKDIR /usr/src/app +COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/ +# We have to use --no-frozen-lockfile due to CKEditor patches +RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild FROM node:22.16.0-alpine - # Install runtime dependencies - RUN apk add --no-cache su-exec shadow +# Install runtime dependencies +RUN apk add --no-cache su-exec shadow - WORKDIR /usr/src/app - COPY ./dist /usr/src/app - RUN rm -rf /usr/src/app/node_modules/better-sqlite3 - COPY --from=builder /usr/src/app/node_modules/better-sqlite3 /usr/src/app/node_modules/better-sqlite3 - COPY ./start-docker.sh /usr/src/app +WORKDIR /usr/src/app +COPY ./dist /usr/src/app +RUN rm -rf /usr/src/app/node_modules/better-sqlite3 +COPY --from=builder /usr/src/app/node_modules/better-sqlite3 /usr/src/app/node_modules/better-sqlite3 +COPY ./start-docker.sh /usr/src/app - # Add application user - RUN adduser -s /bin/false node; exit 0 +# Add application user +RUN adduser -s /bin/false node; exit 0 - # Configure container - EXPOSE 8080 - CMD [ "sh", "./start-docker.sh" ] - HEALTHCHECK --start-period=10s CMD exec su-exec node node /usr/src/app/docker_healthcheck.cjs \ No newline at end of file +# Configure container +EXPOSE 8080 +CMD [ "sh", "./start-docker.sh" ] +HEALTHCHECK --start-period=10s CMD exec su-exec node node /usr/src/app/docker_healthcheck.cjs \ No newline at end of file From b635c74d014dc7832d25e95bf104b991f34e6139 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 25 May 2025 21:47:35 +0300 Subject: [PATCH 10/15] fix(docker/rootless): copy sequence after switch to esbuild --- apps/server/Dockerfile.alpine | 2 +- apps/server/Dockerfile.alpine.rootless | 2 +- apps/server/Dockerfile.rootless | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/server/Dockerfile.alpine b/apps/server/Dockerfile.alpine index 3b96950f1..18bc42bf5 100644 --- a/apps/server/Dockerfile.alpine +++ b/apps/server/Dockerfile.alpine @@ -3,7 +3,7 @@ RUN corepack enable # Install native dependencies since we might be building cross-platform. WORKDIR /usr/src/app -COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/ +COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/ # We have to use --no-frozen-lockfile due to CKEditor patches RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild diff --git a/apps/server/Dockerfile.alpine.rootless b/apps/server/Dockerfile.alpine.rootless index 7105d1609..5e6cba525 100644 --- a/apps/server/Dockerfile.alpine.rootless +++ b/apps/server/Dockerfile.alpine.rootless @@ -3,7 +3,7 @@ RUN corepack enable # Install native dependencies since we might be building cross-platform. WORKDIR /usr/src/app -COPY ./dist/package.json ./dist/pnpm-lock.yaml ./docker/pnpm-workspace.yaml /usr/src/app/ +COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/ # We have to use --no-frozen-lockfile due to CKEditor patches RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild diff --git a/apps/server/Dockerfile.rootless b/apps/server/Dockerfile.rootless index 541e970e5..df94ddb90 100644 --- a/apps/server/Dockerfile.rootless +++ b/apps/server/Dockerfile.rootless @@ -3,7 +3,7 @@ RUN corepack enable # Install native dependencies since we might be building cross-platform. WORKDIR /usr/src/app/build -COPY ./dist/package.json ./dist/pnpm-lock.yaml ./docker/pnpm-workspace.yaml /usr/src/app/ +COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/ # We have to use --no-frozen-lockfile due to CKEditor patches RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild From 93c939bf08c9432f2a350f118f6287af7407c5b1 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 25 May 2025 21:48:12 +0300 Subject: [PATCH 11/15] fix(docker/rootless): main entry point extension --- apps/server/rootless-entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/rootless-entrypoint.sh b/apps/server/rootless-entrypoint.sh index 9f4898df3..0d580285f 100755 --- a/apps/server/rootless-entrypoint.sh +++ b/apps/server/rootless-entrypoint.sh @@ -25,4 +25,4 @@ fi mkdir -p "${TRILIUM_DATA_DIR}" # Start the app -exec node ./main +exec node ./main.cjs From 02fc5214a3bf0e30991e8894f0089ec84d8b20e8 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 27 May 2025 19:52:30 +0300 Subject: [PATCH 12/15] fix(docker/rootless): entrypoint if executable bit is not set --- apps/server/Dockerfile.alpine.rootless | 3 +-- apps/server/Dockerfile.rootless | 3 +-- apps/server/rootless-entrypoint.sh | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/server/Dockerfile.alpine.rootless b/apps/server/Dockerfile.alpine.rootless index 5e6cba525..e8bd0461f 100644 --- a/apps/server/Dockerfile.alpine.rootless +++ b/apps/server/Dockerfile.alpine.rootless @@ -26,7 +26,6 @@ WORKDIR /home/${USER}/app COPY ./dist /home/${USER}/app # Also copy the rootless entrypoint script COPY rootless-entrypoint.sh /home/${USER}/app/ -RUN chmod +x /home/${USER}/app/rootless-entrypoint.sh RUN rm -rf /home/${USER}/app/node_modules/better-sqlite3 COPY --from=builder /usr/src/app/node_modules/better-sqlite3 /home/${USER}/app/node_modules/better-sqlite3 RUN chown -R ${USER}:${USER} /home/${USER} @@ -45,6 +44,6 @@ ENV TRILIUM_DATA_DIR=/home/${USER}/trilium-data ENTRYPOINT ["/usr/bin/dumb-init", "--"] # Use the entrypoint script -CMD /home/${USER}/app/rootless-entrypoint.sh +CMD [ "bash", "./rootless-entrypoint.sh" ] HEALTHCHECK --start-period=10s CMD node /home/${USER}/app/docker_healthcheck.js diff --git a/apps/server/Dockerfile.rootless b/apps/server/Dockerfile.rootless index df94ddb90..ca77f85ba 100644 --- a/apps/server/Dockerfile.rootless +++ b/apps/server/Dockerfile.rootless @@ -28,7 +28,6 @@ WORKDIR /home/${USER}/app COPY ./dist /home/${USER}/app # Also copy the rootless entrypoint script COPY rootless-entrypoint.sh /home/${USER}/app/ -RUN chmod +x /home/${USER}/app/rootless-entrypoint.sh RUN rm -rf /home/${USER}/app/node_modules/better-sqlite3 COPY --from=builder /usr/src/app/node_modules/better-sqlite3 /home/${USER}/app/node_modules/better-sqlite3 RUN chown -R ${USER}:${USER} /home/${USER} @@ -44,6 +43,6 @@ ENV TRILIUM_GID=${GID} ENV TRILIUM_DATA_DIR=/home/${USER}/trilium-data # Use the entrypoint script -CMD /home/${USER}/app/rootless-entrypoint.sh +CMD [ "bash", "./rootless-entrypoint.sh" ] HEALTHCHECK --start-period=10s CMD node /home/${USER}/app/docker_healthcheck.js diff --git a/apps/server/rootless-entrypoint.sh b/apps/server/rootless-entrypoint.sh index 0d580285f..4056df2a2 100755 --- a/apps/server/rootless-entrypoint.sh +++ b/apps/server/rootless-entrypoint.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Rootless entrypoint script for Trilium Notes # Works with both Debian and Alpine-based images From c46d5cc9e9bfef9b07df5b66b9c1c5a1704b6354 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 27 May 2025 19:57:18 +0300 Subject: [PATCH 13/15] fix(docker/rootless): CRLF issues on Windows --- .editorconfig | 3 +++ .gitattributes | 2 ++ 2 files changed, 5 insertions(+) diff --git a/.editorconfig b/.editorconfig index c0aba9b74..c965ea8c0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,6 +8,9 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true +[*.sh] +end_of_line = lf + [{server,translation}.json] charset = utf-8 end_of_line = lf diff --git a/.gitattributes b/.gitattributes index e9d640721..2f8b47a8f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14,4 +14,6 @@ demo/**/*.txt eol=lf demo/**/*.js eol=lf demo/**/*.css eol=lf +*.sh eol=lf + apps/client/src/libraries/** linguist-vendored \ No newline at end of file From 061e238a8e4199f3a64624869028f6bb8aea6ed5 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 27 May 2025 20:05:08 +0300 Subject: [PATCH 14/15] fix(docker/rootless): missing bash under alpine --- apps/server/Dockerfile.alpine.rootless | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/server/Dockerfile.alpine.rootless b/apps/server/Dockerfile.alpine.rootless index e8bd0461f..45fe71156 100644 --- a/apps/server/Dockerfile.alpine.rootless +++ b/apps/server/Dockerfile.alpine.rootless @@ -18,6 +18,7 @@ ENV GID=${GID} # Install runtime dependencies and create user with specific UID/GID RUN apk add --no-cache dumb-init && \ + apk add --no-cache bash && \ # Alpine uses addgroup/adduser (from busybox) instead of groupadd/useradd addgroup -g ${GID} ${USER} && \ adduser -u ${UID} -G ${USER} -s /bin/sh -D -h /home/${USER} ${USER} From 521f4c2410fb91589eba71c407c8a13372bbcccf Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 27 May 2025 20:07:33 +0300 Subject: [PATCH 15/15] docs(release): mention rootless Docker mode --- docs/Release Notes/Release Notes/v0.94.0.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Release Notes/Release Notes/v0.94.0.md b/docs/Release Notes/Release Notes/v0.94.0.md index 41ca9dfe3..66d88c302 100644 --- a/docs/Release Notes/Release Notes/v0.94.0.md +++ b/docs/Release Notes/Release Notes/v0.94.0.md @@ -54,6 +54,7 @@ * For read-only notes, a floating button allows copying the code snippet to clipboard. * [Math in text notes: equations can now be displayed on multiple lines](https://github.com/TriliumNext/Notes/pull/2003) by @SiriusXT * [Metrics endpoint](https://github.com/TriliumNext/Notes/pull/2024) by @perfectra1n +* Docker: Rootless [Dockerfiles are now available](https://github.com/TriliumNext/Notes/pull/1923/files) by @perfectra1n ## 📖 Documentation