Developer

Decoding Syncthing .stignore: Which Rule Keeps a File Included?

Syncthing’s .stignore is an ordered pattern language, not a generic shell glob file. Its governing rule is simple: the first matching pattern decides whether an item is ignored or included. A later rule cannot reverse that decision (current Syncthing documentation).

This is a documentation-based explanation. No hands-on test or measurement of matching, traversal, watcher load, or platform behavior was performed. Versioned pages are cited where their documented syntax differs.

Read a match in five steps

For any file or directory:

  1. Express its path relative to the synchronized folder’s root.
  2. Read the patterns from top to bottom.
  3. Account for rooting, wildcards, case handling, and prefixes.
  4. Stop at the first matching pattern.
  5. Treat an ordinary match as ignored and a match beginning with ! as included.

The .stignore file must be UTF-8 and placed at the synchronized folder’s root; files with that name elsewhere do not apply. All patterns are relative to the synchronized-folder root. The .stignore file itself is not synchronized, although it can load other pattern files with #include (current Syncthing documentation).

Pattern examples

These examples follow the documented definitions:

Pattern Matches Does not match Meaning
foo foo, subdir/foo, any directory named foo foofoo An unrooted name can match at any depth
/foo Root-level foo subdir/foo / roots the pattern at the synchronized folder
te*ne telephone, subdir/telephone tele/phone * matches zero or more characters, but not a directory separator
te**ne telephone, subdir/telephone, tele/sub/dir/phone — ** can cross directory separators
te??st tebest test, teb/st Each ? matches one non-separator character
[a-z] One lowercase character — Brackets specify a character range
{banana,pineapple} banana or pineapple — Braces specify alternatives

The definitions and examples appear in the current Syncthing documentation.

A leading slash is not an operating-system absolute path. /foo means foo at the root of this synchronized folder. By contrast, unrooted foo also matches subdir/foo.

A trailing slash has another meaning: some/directory/ matches the directory’s contents but not the directory itself. To match both the directory and its contents, omit the trailing slash (current Syncthing documentation).

Negated rules must come first

A pattern beginning with ! includes matching items instead of ignoring them. Because the first match wins, an exception must precede any broader pattern that would match the same item (current Syncthing documentation).

Consider the documented rules:

!frobble
!quuz
foo
*2
qu*

For bar/quuz, !quuz is the first matching rule, so the file remains included; the later qu* cannot change that result.

The bar2/frobble case is subtler. The rule *2 matches the parent directory bar2, not the child name frobble. However, the earlier negated !frobble can match below the folder root, so Syncthing traverses the otherwise ignored directory and retains the matching child. The documented result shows bar2/baz ignored while bar2/frobble remains included (current Syncthing documentation; Syncthing v1.27.2 documentation).

More generally, the current documentation says a negated pattern capable of matching below the folder root causes traversal of otherwise ignored directories. If the watcher is enabled, those directories are watched too. Directories ignored before the first negated pattern can be skipped under first-match ordering. Top-level rooted negations such as !/foo are a documented exception to this broader traversal behavior (current Syncthing documentation). This describes behavior, not its performance cost.

Case handling, deletion, and comments

The (?i) prefix enables case-insensitive matching. For example, (?i)test matches test, TEST, and tEsT. The documentation says patterns are always case-insensitive on macOS and Windows. Prefixes can combine with negation: (?i)!picture*.png includes a matching name such as Picture1.PNG (current Syncthing documentation).

The (?d) prefix permits removal of an ignored file when it prevents directory deletion:

(?d).DS_Store

This is a narrow deletion allowance, not a general deletion-safety guarantee. Prefixes may appear separately in either order, such as (?d)(?i), but cannot be combined as (?di) (current Syncthing documentation).

Only a line beginning with // is a comment. Double slashes elsewhere are interpreted literally, so the documented syntax does not support inline // comments (current Syncthing documentation).

How #include works

An include directive loads patterns from another file:

#include more-patterns.txt

A missing file or including the same file more than once is an error. An included file may reside in a subdirectory, but its loaded patterns remain relative to the synchronized-folder root (current Syncthing documentation).

Nested include paths follow a different base. In the current and v1.27.2 documentation, an #include inside an included file is resolved relative to that file’s directory, not the synchronized-folder root (current Syncthing documentation; Syncthing v1.27.2 documentation). The supplied v1.24.0 page does not establish this nested-path rule.

Windows escaping depends on the version

In the current documentation, Windows uses | to escape special characters because \ is the path separator. Thus |{banana|} matches the literal name {banana} (current Syncthing documentation).

To use backslash escaping and / as the path separator, place this directive before any patterns:

#escape=\
/foo
/path/bar/\{banana\}

Comments and blank lines may precede #escape=\. Every file loaded through #include that needs this mode must declare its own directive. The current page labels the pipe escape and configurable #escape directive as added in version 2.0.0 (current Syncthing documentation).

The older pages differ: the v1.24.0 and v1.27.2 documentation says escaped characters are unsupported on Windows and suggests ? as a broader workaround for square or curly brackets (Syncthing v1.24.0 documentation; Syncthing v1.27.2 documentation). Do not assume the current #escape syntax applies to those documented versions.

A compact reading checklist

When evaluating .stignore, ask:

  • What is the item’s root-relative path?
  • Which pattern matches first?
  • Does / restrict the match to the folder root?
  • Must the wildcard cross a separator, requiring ** rather than *?
  • Does an earlier ! keep the item included?
  • Do case or deletion prefixes apply?
  • Is a trailing slash matching contents rather than the directory?
  • Are included patterns still interpreted from the synchronized-folder root?
  • On Windows, does the escaping syntax match the documented version?

For combinations not covered by the documented definitions and examples, consult the documentation for the exact Syncthing version rather than extrapolating from another ignore format.

Sources and further reading

Back to top ↑

Rody

Founder & CEO · RodyTech LLC

Founder of RodyTech LLC in Iowa. I write practical notes on automation, infrastructure, security, and software decisions for builders and business operators.

No comments yet

Leave a comment

Your email address will not be published. Required fields are marked *