By default, all note and attachment content is stored within the database.
+ However, when external blob storage is enabled,
+ large notes and attachments are stored separately in the external-blobs directory
+ for better performance and backup flexibility.
When first starting Trilium, it will provide a set of notes to showcase
various features of the application.
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Advanced Usage/External Blob Storage.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Advanced Usage/External Blob Storage.html
new file mode 100644
index 0000000000..6c7614d0a2
--- /dev/null
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Advanced Usage/External Blob Storage.html
@@ -0,0 +1,59 @@
+External blob storage is an optional feature that stores large attachments
+ as separate files on the filesystem instead of embedding them in the SQLite
+ database. This feature is disabled by default to maintain backward compatibility
+ and simplicity for most users.
+When external blob storage is enabled and a note or attachment exceeds
+ the configured threshold size the content is saved to the file system in
+ the external-blobs directory within your data directory .
+ The files are organized in a partitioned directory structure to prevent
+ excessive files in a single directory. The database stores a reference
+ to the external file and any additional metadata.
+Attachments below the threshold, or when external storage is disabled,
+ continue to be stored in the database.
+When you enable external blob storage, existing attachments in the database
+ remain there. Only new attachments that exceed the threshold will be stored
+ externally. Automatically migrating existing attachments is currently not
+ supported.
+If you disable external blob storage after using it, existing external
+ blobs remain in the external-blobs directory and continue to
+ work. New attachments will be stored in the database regardless of size.
+When external blob storage is enabled, you must backup both the database
+ and the external-blobs directory.
+
Depending on the changes made, there is a risk that the note will not
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Attachments.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Attachments.html
index 537a7c70ca..4a0eb8e813 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Attachments.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Attachments.html
@@ -13,6 +13,11 @@
visuals in notes. It is important to link image attachments within the
text of the owning note; otherwise, they will be automatically deleted
after a configurable timeout period if not referenced.
+Storage
+By default, attachments are stored within the SQLite database. For installations
+ with many large attachments, Trilium supports external blob storage which
+ stores large files separately on the file system. This is optional and
+ disabled by default to maintain simplicity for most users.
Converting notes to attachments
File notes
can be easily converted to attachments of the parent note.
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Backup.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Backup.html
index de085dcc8c..bdc5e3a31c 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Backup.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Backup.html
@@ -17,6 +17,22 @@
Note that Synchronization provides
also some backup capabilities by its nature of distributing the data to
other computers.
+External Blob Storage and Backups
+If you have enabled external blob storage ,
+ you must backup both the database and the external-blobs directory.
+ Without backing up external-blobs separately, you would lose
+ large attachments if restoring from backup.
+By default, external blob storage is disabled, meaning all attachments
+ are stored in the database and the automatic backup described above is
+ sufficient.
+How to Backup with External Blobs
+When external blob storage is enabled, backup the complete data directory which
+ includes both the database and external-blobs. This is the
+ simplest approach and ensures nothing is missed.
+To ensure consistency, either stop Trilium before backing up, or use a
+ filesystem snapshot tool (e.g., LVM snapshots, ZFS snapshots, or volume
+ shadow copy on Windows) to capture both the database and external-blobs directory
+ at the same point in time.
Restoring backup
Let's assume you want to restore the weekly backup, here's how to do it:
@@ -37,6 +53,8 @@ Restoring backup
make sure that the file is writable, e.g. with chmod 600 document.db
+ if you have external blob storage enabled, also restore the external-blobs directory
+ from your backup
start Trilium again
If you have configured sync then you need to do it across all members
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Data directory.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Data directory.html
index 9f2de88530..b1a4875cd6 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Data directory.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Data directory.html
@@ -7,6 +7,8 @@
backup - contains automatically backup of
documents
log - contains application log files
+ external-blobs - contains large attachments when external blob storage is
+ enabled (optional, not present by default)
Location of the data directory
Easy way how to find out which data directory Trilium uses is to look
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Canvas.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Canvas.html
index a5dfa45b11..5be4d8ca0a 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Canvas.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Canvas.html
@@ -7,6 +7,7 @@
text and graphics input.
Interaction
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/File.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/File.html
index 80dad6f451..3a18e1e239 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/File.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/File.html
@@ -4,9 +4,8 @@ Uploading a file
Since these files come from an external source, it is not possible to
create a File note type directly:
- Drag a file into the Note Tree .
- Right click a note and select Import into note and point it to
+ Drag a file into the Note Tree .
+ Right click a note and select Import into note and point it to
one of the supported files.
Supported file types
@@ -18,20 +17,18 @@ PDFs
PDFs can be browsed directly from Trilium.
Interaction:
- Press the menu icon at the top-left to see a preview (thumbnail) of all
+ Press the menu icon at the top-left to see a preview (thumbnail) of all
the pages, as well as a table of contents (if the PDF has this information).
- See or edit the page number at the top.
- Adjust the zoom using the buttons at the top or manually editing the value.
- Rotate the document if it's in the wrong orientation.
- In the contextual menu:
-
- View two pages at once (great for books).
- Toggle annotations (if present in the document).
- View document properties.
-
-
+ See or edit the page number at the top.
+ Adjust the zoom using the buttons at the top or manually editing the value.
+ Rotate the document if it's in the wrong orientation.
+ In the contextual menu:
+
+ View two pages at once (great for books).
+ Toggle annotations (if present in the document).
+ View document properties.
+
+
Images
@@ -40,14 +37,14 @@ Images
Interaction:
- Copy reference to clipboard , for embedding the image within
+ Copy reference to clipboard , for embedding the image within
Text notes.
@@ -77,11 +74,11 @@ Audio
be used to play it.
Interactions:
- The audio can be played/paused using the dedicated button.
- Dragging the mouse across, or clicking the progress bar will seek through
+ The audio can be played/paused using the dedicated button.
+ Dragging the mouse across, or clicking the progress bar will seek through
the song.
- The volume can be set.
- The playback speed can be adjusted via the contextual menu next to the
+ The volume can be set.
+ The playback speed can be adjusted via the contextual menu next to the
volume.
Text files
@@ -114,35 +111,33 @@ Unknown file types
file externally, but there will be no preview of the content.
Interaction
- Regardless of the file type, a series of buttons will be displayed in
+ Regardless of the file type, a series of buttons will be displayed in
the Image or File tab in the Ribbon .
- Download , which will download the file for local use.
- Open , will will open the file with the system-default application.
- Upload new revision to replace the file with a new one.
+ Download , which will download the file for local use.
+ Open , will will open the file with the system-default application.
+ Upload new revision to replace the file with a new one.
-
- It is not possible to change the note type of a File note.
- Convert into an attachment from the note menu .
+
+ It is not possible to change the note type of a File note.
+ Convert into an attachment from the note menu .
Relation with other notes
-
+
Files are also displayed in the Note List based
on their type:
-
-
-
+
+
+
+ Non-image files can be embedded into text notes as read-only widgets via
+ the Include Note functionality.
+
+
+ Image files can be embedded into text notes like normal images via
+ Image references .
- Non-image files can be embedded into text notes as read-only widgets via
- the Include Note functionality.
- Image files can be embedded into text notes like normal images via
- Image references .
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mind Map.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mind Map.html
index b310466e98..988f1bcacd 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mind Map.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mind Map.html
@@ -6,42 +6,35 @@
a hierarchical fashion.
Terminology
- A node is a single idea, represented differently based
+ A node is a single idea, represented differently based
on depth (filled rounded rectangle for the root note, unfilled rectangles
for sub-ideas, lines only for sub-sub-ideas).
- The root node is the top-most node from which all other
+ The root node is the top-most node from which all other
nodes derive, displayed as a filled rectangle. There can only be a single
root node.
Interaction
- To create a new node at the same level as the current one, press Enter ,
+ To create a new node at the same level as the current one, press Enter ,
enter the desired text and then press Enter once again to confirm.
- Similarly, to create a sub-node, press Tab , enter the desired
+ Similarly, to create a sub-node, press Tab , enter the desired
text and then press Enter .
- To create a parent, use Ctrl +Enter instead.
- To remove a node, press Delete .
- To move a node up or down, press Page Up or Page Down .
- To adjust the font size, color of the text or background or to add a link,
- click on a node and use the floating panel that appears to the right.
- To select one or more notes, drag and drop across the map.
- Right click the node to bring a contextual menu with options such as creating
- new nodes, focusing on a particular notes or creating links between them.
- Use the buttons at the top-left to change the positioning of the nodes
- relative to the root node (to the left, to the right, or to both sides).
- In the Floating buttons area:
-
- An image reference can be copied, to paste
- the mind map in a text note.
- The diagram can be exported either as SVG (vectorial) or PNG (raster).
- The note can be togged read-only .
-
-
+ To create a parent, use Ctrl +Enter instead.
+ To remove a node, press Delete .
+ To move a node up or down, press Page Up or Page Down .
+ To adjust the font size, color of the text or background or to add a link,
+ click on a node and use the floating panel that appears to the right.
+ To select one or more notes, drag and drop across the map.
+ Right click the node to bring a contextual menu with options such as creating
+ new nodes, focusing on a particular notes or creating links between them.
+ Use the buttons at the top-left to change the positioning of the nodes
+ relative to the root node (to the left, to the right, or to both sides).
+ In the Floating buttons area:
+
+ An image reference can be copied, to paste
+ the mind map in a text note.
+ The diagram can be exported either as SVG (vectorial) or PNG (raster).
+ The note can be togged read-only .
+
+
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html
index 14a6cf28c7..5e5895b8d8 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html
@@ -7,13 +7,11 @@
via an attribute.
Creating a render note
- Create a Code note
+ Create a Code note
with the HTML language, with what needs to be displayed (for example <p>Hello world.</p>).
- Create a Render Note .
- Assign the renderNote relation to
- point at the previously created code note.
+ Create a Render Note .
+ Assign the renderNote relation to
+ point at the previously created code note.
Dynamic content
A static HTML is generally not enough for Dynamic content
Refreshing the note
It's possible to refresh the note via:
Examples
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/blobs.md b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/blobs.md
index 63bf8c24d9..3dc7f7ddee 100644
--- a/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/blobs.md
+++ b/docs/Developer Guide/Developer Guide/Architecture/Database/Database structure/blobs.md
@@ -1,2 +1,2 @@
# blobs
-
Column Name Data Type Nullity Default value Description blobIdText Non-null The unique ID of the blob (e.g. XXbfAJXqWrYnSXcelLFA).
The ID is actually a hash of the content, see AbstractBeccaEntity#saveBlob! It is a logic error to modify an existing blob.
contentText Nullable nullThe content of the blob, can be either:
text (for plain text notes or HTML notes). binary (for images and other types of attachments) dateModifiedText Non-null Creation date with timezone offset (e.g. 2023-11-08 18:43:44.204+0200) utcDateModifiedText Non-null Creation date in UTC format (e.g. 2023-11-08 16:43:44.204Z).
Blobs cannot be modified, so this timestamp specifies when the blob was created.
\ No newline at end of file
+Column Name Data Type Nullity Default value Description blobIdText Non-null The unique ID of the blob (e.g. XXbfAJXqWrYnSXcelLFA).
The ID is actually a hash of the content, see AbstractBeccaEntity#saveBlob! It is a logic error to modify an existing blob.
contentText Nullable nullThe content of the blob, can be either:
text (for plain text notes or HTML notes) binary (for images and other types of attachments) NULL when the content is stored externally (see contentLocation) contentLocationText Non-null internalLocation of the blob content (added in migration 234):
internal - content is stored in the content columnfile://<path> - content is stored externally in the file system at the specified path relative to external-blobs directorycontentLengthInteger Non-null 0Size of the blob content in bytes (added in migration 234). dateModifiedText Non-null Creation date with timezone offset (e.g. 2023-11-08 18:43:44.204+0200) utcDateModifiedText Non-null Creation date in UTC format (e.g. 2023-11-08 16:43:44.204Z).
Blobs cannot be modified, so this timestamp specifies when the blob was created.
\ No newline at end of file
diff --git a/docs/User Guide/!!!meta.json b/docs/User Guide/!!!meta.json
index b05f2ed21b..9d0912761c 100644
--- a/docs/User Guide/!!!meta.json
+++ b/docs/User Guide/!!!meta.json
@@ -1632,10 +1632,17 @@
{
"type": "relation",
"name": "internalLink",
- "value": "Gzjqa934BdH4",
+ "value": "xB9eL2mK8vWp",
"isInheritable": false,
"position": 40
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "Gzjqa934BdH4",
+ "isInheritable": false,
+ "position": 50
+ },
{
"type": "label",
"name": "shareAlias",
@@ -1687,24 +1694,31 @@
{
"type": "relation",
"name": "internalLink",
- "value": "bnyigUA2UK7s",
+ "value": "xB9eL2mK8vWp",
"isInheritable": false,
"position": 30
},
{
"type": "relation",
"name": "internalLink",
- "value": "x59R8J8KV5Bp",
+ "value": "bnyigUA2UK7s",
"isInheritable": false,
"position": 40
},
{
"type": "relation",
"name": "internalLink",
- "value": "Gzjqa934BdH4",
+ "value": "x59R8J8KV5Bp",
"isInheritable": false,
"position": 50
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "Gzjqa934BdH4",
+ "isInheritable": false,
+ "position": 60
+ },
{
"type": "label",
"name": "shareAlias",
@@ -3615,24 +3629,31 @@
{
"type": "relation",
"name": "internalLink",
- "value": "W8vYD3Q1zjCR",
+ "value": "xB9eL2mK8vWp",
"isInheritable": false,
"position": 40
},
{
"type": "relation",
"name": "internalLink",
- "value": "8YBEPzcpUgxw",
+ "value": "W8vYD3Q1zjCR",
"isInheritable": false,
"position": 50
},
{
"type": "relation",
"name": "internalLink",
- "value": "oPVyFC7WL2Lp",
+ "value": "8YBEPzcpUgxw",
"isInheritable": false,
"position": 60
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "oPVyFC7WL2Lp",
+ "isInheritable": false,
+ "position": 70
+ },
{
"type": "label",
"name": "shareAlias",
@@ -8864,17 +8885,31 @@
{
"type": "relation",
"name": "internalLink",
- "value": "R7abl2fc6Mxi",
+ "value": "XpOYSgsLkTJy",
"isInheritable": false,
"position": 50
},
{
"type": "relation",
"name": "internalLink",
- "value": "6tZeKvSHEUiB",
+ "value": "A9Oc6YKKc65v",
"isInheritable": false,
"position": 60
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "R7abl2fc6Mxi",
+ "isInheritable": false,
+ "position": 70
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "6tZeKvSHEUiB",
+ "isInheritable": false,
+ "position": 80
+ },
{
"type": "label",
"name": "iconClass",
@@ -8888,20 +8923,6 @@
"value": "render-note",
"isInheritable": false,
"position": 70
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "XpOYSgsLkTJy",
- "isInheritable": false,
- "position": 80
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "A9Oc6YKKc65v",
- "isInheritable": false,
- "position": 90
}
],
"format": "markdown",
@@ -9095,32 +9116,32 @@
"mime": "text/html",
"attributes": [
{
- "type": "label",
- "name": "iconClass",
- "value": "bx bx-pen",
+ "type": "relation",
+ "name": "internalLink",
+ "value": "CoFPLs3dRlXc",
"isInheritable": false,
"position": 10
},
{
- "type": "label",
- "name": "shareAlias",
- "value": "canvas",
+ "type": "relation",
+ "name": "internalLink",
+ "value": "XpOYSgsLkTJy",
"isInheritable": false,
"position": 20
},
{
- "type": "relation",
- "name": "internalLink",
- "value": "CoFPLs3dRlXc",
+ "type": "label",
+ "name": "iconClass",
+ "value": "bx bx-pen",
"isInheritable": false,
- "position": 30
+ "position": 10
},
{
- "type": "relation",
- "name": "internalLink",
- "value": "XpOYSgsLkTJy",
+ "type": "label",
+ "name": "shareAlias",
+ "value": "canvas",
"isInheritable": false,
- "position": 40
+ "position": 20
}
],
"format": "markdown",
@@ -9206,6 +9227,13 @@
"isInheritable": false,
"position": 20
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "CoFPLs3dRlXc",
+ "isInheritable": false,
+ "position": 30
+ },
{
"type": "label",
"name": "iconClass",
@@ -9219,13 +9247,6 @@
"value": "mindmap",
"isInheritable": false,
"position": 30
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "CoFPLs3dRlXc",
- "isInheritable": false,
- "position": 40
}
],
"format": "markdown",
@@ -9280,66 +9301,73 @@
{
"type": "relation",
"name": "internalLink",
- "value": "wX4HbRucYSDD",
+ "value": "XpOYSgsLkTJy",
"isInheritable": false,
"position": 40
},
{
"type": "relation",
"name": "internalLink",
- "value": "ODY7qQn5m2FT",
+ "value": "wX4HbRucYSDD",
"isInheritable": false,
"position": 50
},
{
"type": "relation",
"name": "internalLink",
- "value": "mHbBMPDPkVV5",
+ "value": "ODY7qQn5m2FT",
"isInheritable": false,
"position": 60
},
{
"type": "relation",
"name": "internalLink",
- "value": "6f9hih2hXXZk",
+ "value": "mHbBMPDPkVV5",
"isInheritable": false,
"position": 70
},
{
"type": "relation",
"name": "internalLink",
- "value": "BlN9DFI679QC",
+ "value": "6f9hih2hXXZk",
"isInheritable": false,
"position": 80
},
{
"type": "relation",
"name": "internalLink",
- "value": "0vhv7lsOLy82",
+ "value": "BlN9DFI679QC",
"isInheritable": false,
"position": 90
},
{
"type": "relation",
"name": "internalLink",
- "value": "8YBEPzcpUgxw",
+ "value": "0vhv7lsOLy82",
"isInheritable": false,
"position": 100
},
{
"type": "relation",
"name": "internalLink",
- "value": "0ESUbbAxVnoK",
+ "value": "8YBEPzcpUgxw",
"isInheritable": false,
"position": 110
},
{
"type": "relation",
"name": "internalLink",
- "value": "nBAXQFj20hS1",
+ "value": "0ESUbbAxVnoK",
"isInheritable": false,
"position": 120
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "nBAXQFj20hS1",
+ "isInheritable": false,
+ "position": 130
+ },
{
"type": "label",
"name": "shareAlias",
@@ -9353,13 +9381,6 @@
"value": "bx bx-file-blank",
"isInheritable": false,
"position": 140
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "XpOYSgsLkTJy",
- "isInheritable": false,
- "position": 150
}
],
"format": "markdown",
@@ -12808,6 +12829,61 @@
}
]
},
+ {
+ "isClone": false,
+ "noteId": "xB9eL2mK8vWp",
+ "notePath": [
+ "pOsGYCXsbNQG",
+ "tC7s2alapj8V",
+ "xB9eL2mK8vWp"
+ ],
+ "title": "External Blob Storage",
+ "notePosition": 135,
+ "prefix": null,
+ "isExpanded": false,
+ "type": "text",
+ "mime": "text/markdown",
+ "attributes": [
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "tAassRL4RSQL",
+ "isInheritable": false,
+ "position": 10
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "Gzjqa934BdH4",
+ "isInheritable": false,
+ "position": 20
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "ODY7qQn5m2FT",
+ "isInheritable": false,
+ "position": 30
+ },
+ {
+ "type": "label",
+ "name": "shareAlias",
+ "value": "external-blob-storage",
+ "isInheritable": false,
+ "position": 10
+ },
+ {
+ "type": "label",
+ "name": "iconClass",
+ "value": "bx bx-hdd",
+ "isInheritable": false,
+ "position": 20
+ }
+ ],
+ "format": "markdown",
+ "dataFileName": "External Blob Storage.md",
+ "attachments": []
+ },
{
"isClone": false,
"noteId": "47ZrP6FNuoG8",
@@ -12888,24 +12964,31 @@
{
"type": "relation",
"name": "internalLink",
- "value": "6tZeKvSHEUiB",
+ "value": "xB9eL2mK8vWp",
"isInheritable": false,
"position": 20
},
{
"type": "relation",
"name": "internalLink",
- "value": "oyIAJ9PvvwHX",
+ "value": "6tZeKvSHEUiB",
"isInheritable": false,
"position": 30
},
{
"type": "relation",
"name": "internalLink",
- "value": "Gzjqa934BdH4",
+ "value": "oyIAJ9PvvwHX",
"isInheritable": false,
"position": 40
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "Gzjqa934BdH4",
+ "isInheritable": false,
+ "position": 50
+ },
{
"type": "label",
"name": "shareAlias",
diff --git a/docs/User Guide/User Guide/Advanced Usage/Configuration (config.ini or e.md b/docs/User Guide/User Guide/Advanced Usage/Configuration (config.ini or e.md
index 936cc3c4e4..df997d97c3 100644
--- a/docs/User Guide/User Guide/Advanced Usage/Configuration (config.ini or e.md
+++ b/docs/User Guide/User Guide/Advanced Usage/Configuration (config.ini or e.md
@@ -81,6 +81,13 @@ Additionally, shorter aliases are available for common configurations (see Alter
| --- | --- | --- | --- |
| `TRILIUM_LOGGING_RETENTIONDAYS` | integer | 90 | Number of days to retain log files |
+### ExternalBlobStorage Section
+
+| Environment Variable | Type | Default | Description |
+| --- | --- | --- | --- |
+| `TRILIUM_EXTERNAL_BLOB_STORAGE_ENABLED` | boolean | false | Enable external blob storage for large attachments |
+| `TRILIUM_EXTERNAL_BLOB_STORAGE_THRESHOLD` | integer | 102400 | Size threshold in bytes (100KB default) for external storage |
+
## Alternative Environment Variables
The following alternative environment variable names are also supported and work identically to their longer counterparts:
diff --git a/docs/User Guide/User Guide/Advanced Usage/Database.md b/docs/User Guide/User Guide/Advanced Usage/Database.md
index a0a99228e0..1bed53c843 100644
--- a/docs/User Guide/User Guide/Advanced Usage/Database.md
+++ b/docs/User Guide/User Guide/Advanced Usage/Database.md
@@ -1,6 +1,8 @@
# Database
Your Trilium data is stored in a [SQLite](https://www.sqlite.org) database which contains all notes, tree structure, metadata, and most of the configuration. The database file is named `document.db` and is stored in the application's default [Data directory](../Installation%20%26%20Setup/Data%20directory.md).
+By default, all note and attachment content is stored within the database. However, when [external blob storage](External%20Blob%20Storage.md) is enabled, large notes and attachments are stored separately in the `external-blobs` directory for better performance and backup flexibility.
+
## Demo Notes
When first starting Trilium, it will provide a set of notes to showcase various features of the application.
diff --git a/docs/User Guide/User Guide/Advanced Usage/External Blob Storage.md b/docs/User Guide/User Guide/Advanced Usage/External Blob Storage.md
new file mode 100644
index 0000000000..a46ca84a86
--- /dev/null
+++ b/docs/User Guide/User Guide/Advanced Usage/External Blob Storage.md
@@ -0,0 +1,47 @@
+# External Blob Storage
+External blob storage is an optional feature that stores large attachments as separate files on the filesystem instead of embedding them in the SQLite database. This feature is disabled by default to maintain backward compatibility and simplicity for most users.
+
+## When to Enable External Blob Storage
+
+### Benefits of enabling external storage:
+
+* **Performance**: Large attachments don't bloat the database, which can improve query performance
+* **Backup flexibility**: Large files can be backed up separately from the database using different strategies or schedules
+* **Storage efficiency**: Easier to manage and migrate large files independently
+
+### When to keep it disabled (default):
+
+* **Simplicity**: All data in one database file makes backup and migration straightforward
+* **Small attachments**: If your attachments are mostly small, database storage is more efficient
+* **Portability**: Single-file database is easier to copy and move between systems
+
+## How It Works
+
+When external blob storage is enabled and a note or attachment exceeds the configured threshold size the content is saved to the file system in the `external-blobs` directory within your [data directory](../Installation%20%26%20Setup/Data%20directory.md). The files are organized in a partitioned directory structure to prevent excessive files in a single directory. The database stores a reference to the external file and any additional metadata.
+
+Attachments below the threshold, or when external storage is disabled, continue to be stored in the database.
+
+When you enable external blob storage, existing attachments in the database remain there. Only new attachments that exceed the threshold will be stored externally. Automatically migrating existing attachments is currently not supported.
+
+If you disable external blob storage after using it, existing external blobs remain in the `external-blobs` directory and continue to work. New attachments will be stored in the database regardless of size.
+
+## Configuration
+
+External blob storage can be configured via `config.ini` or environment variables. See the [Configuration](Configuration%20\(config.ini%20or%20e.md) documentation for all available options.
+
+## Threshold Size Recommendations
+
+The default threshold of 100KB (102400 bytes) is based on [SQLite's performance recommendations](https://www.sqlite.org/intern-v-extern-blob.html).
+
+You can adjust the threshold based on your needs:
+
+* **Lower threshold (e.g., 50KB)**: More files stored externally, smaller database
+* **Higher threshold (e.g., 1MB)**: Fewer external files, larger database but simpler backup
+
+## Important Backup Considerations
+
+When external blob storage is enabled, you must backup both the database and the `external-blobs` directory.
+
+Without backing up the `external-blobs` directory, you would lose large attachments if restoring from backup.
+
+See the [Backup](../Installation%20%26%20Setup/Backup.md) documentation for detailed backup strategies with external blob storage.
\ No newline at end of file
diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Attachments.md b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Attachments.md
index bb38e72f90..5c7dae9d3f 100644
--- a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Attachments.md
+++ b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Attachments.md
@@ -7,6 +7,10 @@ Each note exclusively owns its attachments, meaning attachments cannot be shared
Attachments, especially image files, are the recommended method for embedding visuals in notes. It is important to link image attachments within the text of the owning note; otherwise, they will be automatically deleted after a configurable timeout period if not referenced.
+## Storage
+
+By default, attachments are stored within the SQLite database. For installations with many large attachments, Trilium supports [external blob storage](../../Advanced%20Usage/External%20Blob%20Storage.md) which stores large files separately on the file system. This is optional and disabled by default to maintain simplicity for most users.
+
## Converting notes to attachments
File notes can be easily converted to attachments of the parent note.
diff --git a/docs/User Guide/User Guide/Installation & Setup/Backup.md b/docs/User Guide/User Guide/Installation & Setup/Backup.md
index 699ca0e009..42c437f435 100644
--- a/docs/User Guide/User Guide/Installation & Setup/Backup.md
+++ b/docs/User Guide/User Guide/Installation & Setup/Backup.md
@@ -12,6 +12,18 @@ This is only very basic backup solution, and you're encouraged to add some bette
Note that Synchronization provides also some backup capabilities by its nature of distributing the data to other computers.
+## External Blob Storage and Backups
+
+If you have enabled external blob storage , you must backup both the database and the `external-blobs` directory. Without backing up `external-blobs` separately, you would lose large attachments if restoring from backup.
+
+By default, external blob storage is disabled, meaning all attachments are stored in the database and the automatic backup described above is sufficient.
+
+### How to Backup with External Blobs
+
+When external blob storage is enabled, backup the complete [data directory](Data%20directory.md) which includes both the database and `external-blobs`. This is the simplest approach and ensures nothing is missed.
+
+To ensure consistency, either stop Trilium before backing up, or use a filesystem snapshot tool (e.g., LVM snapshots, ZFS snapshots, or volume shadow copy on Windows) to capture both the database and `external-blobs` directory at the same point in time.
+
## Restoring backup
Let's assume you want to restore the weekly backup, here's how to do it:
@@ -23,6 +35,7 @@ Let's assume you want to restore the weekly backup, here's how to do it:
* delete `~/trilium-data/document.db`, `~/trilium-data/document.db-wal` and `~/trilium-data/document.db-shm` (latter two files are auto generated)
* copy and rename this `~/trilium-data/backup/backup-weekly.db` to `~/trilium-data/document.db`
* make sure that the file is writable, e.g. with `chmod 600 document.db`
+* if you have external blob storage enabled, also restore the `external-blobs` directory from your backup
* start Trilium again
If you have configured sync then you need to do it across all members of the sync cluster, otherwise older version (restored backup) of the document will be detected and synced to the newer version.
diff --git a/docs/User Guide/User Guide/Installation & Setup/Data directory.md b/docs/User Guide/User Guide/Installation & Setup/Data directory.md
index 3d66bafe90..684657bf7c 100644
--- a/docs/User Guide/User Guide/Installation & Setup/Data directory.md
+++ b/docs/User Guide/User Guide/Installation & Setup/Data directory.md
@@ -5,6 +5,7 @@ Data directory contains:
* `config.ini` - instance level settings like port on which the Trilium application runs
* `backup` - contains automatically [backup](Backup.md) of documents
* `log` - contains application log files
+* `external-blobs` - contains large attachments when [external blob storage](../Advanced%20Usage/External%20Blob%20Storage.md) is enabled (optional, not present by default)
## Location of the data directory
From 72506f8ae49904bf336a32c64072121a1dc01b21 Mon Sep 17 00:00:00 2001
From: Alex Dachin
Date: Tue, 11 Nov 2025 09:41:45 +0700
Subject: [PATCH 4/4] Save external blobs during sync
---
apps/server/src/services/sync_update.ts | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/apps/server/src/services/sync_update.ts b/apps/server/src/services/sync_update.ts
index 9d4ff5c4c0..5ab3b46e5a 100644
--- a/apps/server/src/services/sync_update.ts
+++ b/apps/server/src/services/sync_update.ts
@@ -4,7 +4,8 @@ import entityChangesService from "./entity_changes.js";
import eventService from "./events.js";
import entityConstructor from "../becca/entity_constructor.js";
import ws from "./ws.js";
-import type { EntityChange, EntityChangeRecord, EntityRow } from "@triliumnext/commons";
+import blobStorageService from "./blob-storage.js";
+import type { EntityChange, EntityChangeRecord, EntityRow, BlobRow } from "@triliumnext/commons";
interface UpdateContext {
alreadyErased: number;
@@ -126,6 +127,14 @@ function preProcessContent(remoteEC: EntityChange, remoteEntityRow: EntityRow) {
remoteEntityRow.content = "";
}
}
+
+ // store external blobs on this instance too
+ const blobRow = remoteEntityRow as BlobRow;
+ if (blobRow.contentLocation && blobRow.contentLocation !== "internal" && remoteEntityRow.content) {
+ const newContentLocation = blobStorageService.saveExternal(blobRow.blobId, remoteEntityRow.content);
+ blobRow.contentLocation = newContentLocation;
+ blobRow.content = null;
+ }
}
}