From 0298c62ec76bb5f68c083e81604956b05c845401 Mon Sep 17 00:00:00 2001
From: perf3ct If you are having timezone issues and you are not using docker-compose,
you may need to add a Note on timezones
TZ environment variable with the TZ identifier of
- your local timezone.
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
+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.
+The rootless Trilium image:
+trilium) during build time--user flagentrypoint script# 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
+
+# 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
+
+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)If you encounter permission issues with the data volume, ensure that:
+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
+
+usermod/groupmod commandsTwo rootless variants are provided:
+apps/server/Dockerfile.rootless
+ apps/server/Dockerfile.alpine.rootless
+ 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
TZ environment variable with the TZ identifier of
your local timezone.
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