diff --git a/.editorconfig b/.editorconfig index c0aba9b74d..c965ea8c07 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 e9d6407216..2f8b47a8fe 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 diff --git a/apps/server/Dockerfile b/apps/server/Dockerfile index 1e357f6fe3..dbb021f58b 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 24d0dcb12f..18bc42bf52 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 diff --git a/apps/server/Dockerfile.alpine.rootless b/apps/server/Dockerfile.alpine.rootless new file mode 100644 index 0000000000..45fe711563 --- /dev/null +++ b/apps/server/Dockerfile.alpine.rootless @@ -0,0 +1,50 @@ +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 ./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.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 && \ + 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} + +WORKDIR /home/${USER}/app +COPY ./dist /home/${USER}/app +# Also copy the rootless entrypoint script +COPY rootless-entrypoint.sh /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", "--"] + +# Use the entrypoint script +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 new file mode 100644 index 0000000000..ca77f85baf --- /dev/null +++ b/apps/server/Dockerfile.rootless @@ -0,0 +1,48 @@ +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 ./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.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 +# Also copy the rootless entrypoint script +COPY rootless-entrypoint.sh /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 the entrypoint script +CMD [ "bash", "./rootless-entrypoint.sh" ] + +HEALTHCHECK --start-period=10s CMD node /home/${USER}/app/docker_healthcheck.js diff --git a/apps/server/package.json b/apps/server/package.json index 585338e9f2..715fd3cd02 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -154,6 +154,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" } } }, @@ -169,6 +175,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" } } }, diff --git a/apps/server/rootless-entrypoint.sh b/apps/server/rootless-entrypoint.sh new file mode 100755 index 0000000000..4056df2a29 --- /dev/null +++ b/apps/server/rootless-entrypoint.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# 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 " 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 " Current UID: $(id -u), Expected UID: ${TRILIUM_UID}" + echo " Please use docker run with --user $(id -u):$(id -g) instead." + exit 1 + fi +fi + +# Make sure data directory has correct permissions +mkdir -p "${TRILIUM_DATA_DIR}" + +# Start the app +exec node ./main.cjs 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 26e837d8be..60e5d049e6 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,115 @@

Note on --user Directive

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/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/trilium/trilium-data triliumnext/notes:rootless
+
+

Environment Variables

+ +

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

+ +

Available Rootless Images

+

Two rootless variants are provided:

+
    +
  1. Debian-based (default): Uses the Debian Bullseye Slim + base image + +
  2. +
  3. Alpine-based: Uses the Alpine base image for smaller + size + +
  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:

+ \ No newline at end of file diff --git a/docker-compose.rootless.yml b/docker-compose.rootless.yml new file mode 100644 index 0000000000..7d4afd5452 --- /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 diff --git a/docs/Release Notes/Release Notes/v0.94.0.md b/docs/Release Notes/Release Notes/v0.94.0.md index 41ca9dfe3a..66d88c302a 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 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 5384f5b870..0f711f89e2 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,123 @@ 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 + +> [!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: + +``` +# 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** + +``` +# 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/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/trilium/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 + +``` +# 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