Hierarchical .dropboxignore for Dropbox on Windows (NTFS ADS), Linux (user.* xattrs), and macOS (xattrs)
Project description
dbxignore
Hierarchical .dropboxignore files for Dropbox. Drop a .dropboxignore into any folder under your Dropbox root and matching paths get the Dropbox ignore marker set automatically — no more node_modules/ cluttering your sync. Windows (NTFS alternate data streams), Linux (user.* xattrs), and macOS (xattrs) supported.
Upgrading from v0.2.x
The project was renamed from dropboxignore to dbxignore in v0.3.0
(the old name collides with an unrelated 2019 PyPI project). Upgrade is
a one-time manual step:
dropboxignore uninstall --purge # on v0.2.x — removes state, logs, service
pip install dbxignore # or: uv pip install dbxignore
dbxignore install # registers the new service under new names
Your .dropboxignore rule files carry over untouched — they're never
modified by install/uninstall.
Requirements
- Windows 10/11 (NTFS), or a modern Linux distro with a systemd user session, or macOS (Apple Silicon for pre-built binaries; Intel via PyPI)
- Dropbox desktop client installed
- Python ≥ 3.11 with
uv. Pre-built binaries (Windows.exe, macOS arm64 Mach-O) are alternatives.
Install (Windows, from source)
uv tool install git+https://github.com/kiloscheffer/dbxignore
dbxignore install
dbxignore install registers a Task Scheduler entry that launches the daemon (pythonw -m dbxignore daemon) at every user logon.
If install fails with "ERROR_CLOUD_FILE_INCOMPATIBLE_HARDLINKS"
Windows users whose AppData is OneDrive-synced (Files On-Demand) can hit:
error: Failed to install: psutil-...whl
Caused by: failed to hardlink file from
C:\Users\<user>\AppData\Roaming\uv\tools\... to
C:\Users\<user>\AppData\Local\uv\cache\...:
The cloud operation cannot be performed on a file with incompatible hardlinks. (os error 396)
uv hardlinks files from its cache into the tool's site-packages by default; the Cloud Files API rejects hardlinks on placeholder files (those backed by cloud storage but not yet fully materialized locally). Force uv to copy instead:
uv tool install --link-mode=copy git+https://github.com/kiloscheffer/dbxignore
Or set it as a session-wide default before the install: $env:UV_LINK_MODE = "copy". Either form works for uv tool upgrade too.
Install (Linux)
Requires a systemd user session (standard on Ubuntu, Fedora, Debian, Arch, and most modern distros; WSL2 requires systemd=true in /etc/wsl.conf).
uv tool install git+https://github.com/kiloscheffer/dbxignore
dbxignore install # writes systemd user unit, enables it
systemctl --user status dbxignore.service
dbxignore install writes ~/.config/systemd/user/dbxignore.service and runs systemctl --user enable --now so the daemon starts at login.
For non-stock Dropbox installs, export DBXIGNORE_ROOT before running dbxignore install — the install step will read the variable from your shell environment and write a corresponding Environment="DBXIGNORE_ROOT=..." line into the generated unit's [Service] block. Without this, a shell-exported value won't reach the daemon when systemd launches it. If your Dropbox location ever changes, re-run dbxignore install after updating the export.
To uninstall:
dbxignore uninstall # disables unit, removes the file
dbxignore uninstall --purge # clears markers, state files, logs, systemd drop-in
Notes:
- Dropbox on Linux marks ignored paths with the xattr
user.com.dropbox.ignored=1. Files on filesystems that don't supportuser.*xattrs (tmpfs withoutuser_xattr, vfat, some FUSE mounts) are skipped with aWARNINGin the daemon log — not a fatal error. - Several common operations strip xattrs silently:
cpwithout-a,mvacross filesystems, most archivers,vim's default save-via-rename. The watchdog plus hourly sweep re-apply markers automatically; no action needed. - Linux symlinks cannot carry
user.*xattrs (kernel restriction). A symlink matched by a rule logs oneWARNINGper sweep and is skipped. Its target is not affected.
Install (macOS)
dbxignore on macOS supports both Dropbox sync modes and auto-detects which one is active:
- Legacy mode — Dropbox folder at
~/Dropbox, ignored files marked via thecom.dropbox.ignoredextended attribute. Synced by Dropbox's own daemon. - File Provider mode — Dropbox folder at
~/Library/CloudStorage/Dropbox/, ignored files marked via thecom.apple.fileprovider.ignore#Pextended attribute (per Dropbox's docs). Synced by Apple's File Provider extension; default for installs since 2023.
The macOS xattr backend detects File Provider mode by the presence of ~/Library/CloudStorage/Dropbox/ at module-load time and selects the matching attribute name. No user action required — the daemon picks the right one automatically. The daemon registers as a launchd User Agent in either case.
If you want to verify your mode manually:
fileproviderctl dump 2>&1 | grep -q "com.getdropbox.dropbox.fileprovider" \
&& echo "File Provider mode" \
|| echo "Legacy mode"
Install:
pip install dbxignore # or: uv tool install dbxignore
dbxignore install # writes ~/Library/LaunchAgents/com.kiloscheffer.dbxignore.plist
# and bootstraps it into your GUI session
dbxignore install requires that you've logged into the macOS GUI at least once since the last reboot — the GUI domain that LaunchAgents bootstrap into isn't initialized until a graphical login. SSH-on-fresh-boot installs fail with Bootstrap failed: 5: Input/output error. Log into the GUI, then retry.
Pre-built binaries (arm64 only)
Pre-built Mach-O binaries are arm64 (Apple Silicon). Intel Mac users: install via PyPI — the wheel is universal Python.
curl -L -o dbxignore https://github.com/kiloscheffer/dbxignore/releases/latest/download/dbxignore
curl -L -o dbxignored https://github.com/kiloscheffer/dbxignore/releases/latest/download/dbxignored
chmod +x dbxignore dbxignored
sudo mv dbxignore dbxignored /usr/local/bin/
The binaries are unsigned — Gatekeeper refuses them on first launch with "cannot be opened because it is from an unidentified developer." Either right-click → Open in Finder (macOS remembers the override), or strip the quarantine xattr explicitly:
xattr -d com.apple.quarantine /usr/local/bin/dbxignore
xattr -d com.apple.quarantine /usr/local/bin/dbxignored
dbxignore install
To uninstall:
dbxignore uninstall # bootouts the agent, removes the plist
dbxignore uninstall --purge # also clears markers, state files, logs
Files written:
~/Library/LaunchAgents/com.kiloscheffer.dbxignore.plist # launchd unit
~/Library/Application Support/dbxignore/state.json # daemon state
~/Library/Logs/dbxignore/daemon.log # daemon log (rotated)
~/Library/Logs/dbxignore/launchd.log # launchd-captured stdout/stderr
Notes:
- A symlink matched by a
.dropboxignorerule is marked on the link itself, not its target. macOS allows xattrs on symlinks; Linux refuses withEPERMand emits a WARNING. So on macOS the marker lands silently and successfully — matching the design intent better than the Linux behavior. - macOS support is new in v0.4 and covers both Dropbox sync modes (legacy and File Provider — auto-detected at module-load time; see the compatibility note at the top of this section). If you hit anything unexpected, please file an issue.
Install (.exe)
- Download
dbxignore.exeanddbxignored.exefrom the latest Release. - Place both in a stable directory (e.g.
%LOCALAPPDATA%\dbxignore\bin\) and add it to yourPATH. - Run
dbxignore install.
Platform support
| Platform | Marker mechanism | Daemon mechanism | Tested |
|---|---|---|---|
| Windows 10 / 11 | NTFS Alternate Data Streams | Task Scheduler (user task) | yes (since v0.1) |
| Linux (Ubuntu 22.04 / 24.04 + most modern distros with systemd user session) | user.com.dropbox.ignored xattr |
systemd user unit | yes (since v0.2) |
| macOS (Apple Silicon; Intel via PyPI) | com.dropbox.ignored xattr (legacy mode) or com.apple.fileprovider.ignore#P (File Provider mode — default since 2023; auto-detected) |
launchd User Agent | new in v0.4 — please report issues |
.dropboxignore syntax
Full .gitignore syntax via pathspec. Matching is case-insensitive to accommodate NTFS. A file named .dropboxignore is never itself ignored — it needs to sync so your other machines see the same rules.
Example (put in a project root):
# everything javascripty
node_modules/
# Python
__pycache__/
.venv/
*.egg-info/
# Rust
target/
# build output
/dist/
/build/
# except this one specific artifact we want to share
!dist/release-notes.pdf
Commands
| Command | Purpose |
|---|---|
dbxignore install / uninstall |
Register / remove the daemon with the platform's user-scoped service manager (Task Scheduler on Windows, systemd user unit on Linux). uninstall --purge also clears every existing marker, removes local dbxignore state (state.json, daemon.log*, the state directory), and on Linux removes any systemd drop-in directory. Any stray marker on a .dropboxignore file itself is logged at WARNING before being cleared. |
dbxignore daemon |
Run the watcher + hourly sweep in the foreground. Usually invoked by Task Scheduler. |
dbxignore apply [PATH] |
One-shot reconcile of the whole Dropbox (or a subtree). |
dbxignore status |
Is the daemon running? Last sweep counts, last error. |
dbxignore list [PATH] |
Print every path currently bearing the ignore marker. |
dbxignore explain PATH |
Which .dropboxignore rule (if any) matches the path? |
Behaviour
- Source of truth.
.dropboxignorefiles declare what is ignored. Removing a rule unignores the matching paths on the next reconcile. A path marked ignored via Dropbox's right-click menu but not matching any rule will be unignored. - Hybrid trigger. The daemon reacts to filesystem events in real time and runs an hourly safety-net sweep. If the daemon is offline, an initial sweep at the next start catches any drift.
- Multi-root. Personal and Business Dropbox roots are discovered automatically from
%APPDATA%\Dropbox\info.json(Windows) or~/.dropbox/info.json(Linux).
Negations and Dropbox's ignore inheritance
Dropbox marks files and folders as ignored using xattrs. When a folder carries the ignore marker, Dropbox does not sync that folder or anything inside it — children inherit the ignored state regardless of whether they individually carry the marker. This matters for gitignore-style negation rules in your .dropboxignore.
If you write a negation whose target lives under a directory ignored by an earlier rule — the canonical case is build/ followed by !build/keep/ — the negation cannot take effect. Dropbox will ignore build/keep/ because build/ is ignored, no matter what xattr we put on the child. dbxignore detects this at the moment you save the .dropboxignore, logs a WARNING naming both rules, and drops the conflicted negation from the active rule set.
Negations that don't conflict with an ignored ancestor work normally. For example:
*.log
!important.log
Here nothing marks a parent directory as ignored (*.log matches files, not dirs), so the negation works — important.log gets synced, the other .log files don't.
Limitation. Detection uses static analysis on the rule's literal path prefix. Negations that begin with a glob (!**/keep/, !*/cache/) have no literal anchor to analyze and are accepted without conflict-check — if they land under an ignored ancestor at runtime, they silently fail to take effect. If you need guaranteed semantics, prefer negations with a literal prefix.
Configuration
Environment variables read at daemon startup:
| Variable | Default | Purpose |
|---|---|---|
DBXIGNORE_DEBOUNCE_RULES_MS |
100 |
Debounce window for .dropboxignore file events. |
DBXIGNORE_DEBOUNCE_DIRS_MS |
0 |
Debounce for directory-creation events (0 = react immediately, no coalescing). |
DBXIGNORE_DEBOUNCE_OTHER_MS |
500 |
Debounce for other file events. |
DBXIGNORE_LOG_LEVEL |
INFO |
Daemon log level. Accepts DEBUG, INFO, WARNING, ERROR, CRITICAL (case-insensitive). Unknown values fall back to INFO. Affects dbxignore daemon only — CLI commands use the top-level --verbose / -v flag (DEBUG when set, INFO otherwise). See Log levels below for what each level surfaces. |
DBXIGNORE_ROOT |
(unset) | Escape hatch for non-stock Dropbox installs: overrides info.json discovery and treats the given absolute path as the sole Dropbox root. If the path doesn't exist, a WARNING is logged and no roots are returned (so dbxignore apply exits with "No Dropbox roots found"). |
Log levels
The daemon and CLI have separate log-config knobs:
- Daemon (
dbxignore daemon) readsDBXIGNORE_LOG_LEVELfrom the environment at startup. Output goes to the rotating file (and stderr on Linux for journald). - CLI commands (
apply,list,status,explain,install,uninstall) use the top-level--verbose/-vflag — DEBUG when set, INFO otherwise. The env var is not consulted here. Output goes to stderr.
What each level surfaces:
| Level | What you see |
|---|---|
DEBUG |
Per-operation traces — individual marker reads/writes, watchdog event payloads, debouncer ticks, "xattr absent" / "path gone" race-condition skips on clear_ignored. Useful when debugging a specific reconcile decision or a marker-API edge case. |
INFO (default) |
Daemon start/stop banners, sweep summaries (paths marked / cleared per sweep), install/uninstall confirmations, environment-forwarding diagnostics. The "what's the daemon doing right now" baseline. |
WARNING |
Recoverable conditions — filesystems that don't support markers (ENOTSUP/EOPNOTSUPP), missing info.json, dropped negations under ignored ancestors, symlink EPERM on Linux, schtasks /Run failure post-install, corrupt or shape-mismatched state.json. None of these stop the daemon. |
ERROR |
Conditions that prevent progress on a specific concern — "No Dropbox roots discovered; exiting", sweep-startup failures, watchdog or debouncer handler crashes (with traceback). The daemon either continues with reduced scope or shuts down cleanly. |
CRITICAL |
Accepted by the env var but no production code path emits at this level — the project tops out at ERROR. |
Ad-hoc debugging — bump the daemon's verbosity for one run:
# Linux / macOS
systemctl --user stop dbxignore.service # Linux: stop the running daemon
launchctl bootout gui/$(id -u)/com.kiloscheffer.dbxignore # macOS: same idea
DBXIGNORE_LOG_LEVEL=DEBUG dbxignore daemon # foreground; output streams to terminal
# Windows
schtasks /End /TN dbxignore # stop the running task instance
$env:DBXIGNORE_LOG_LEVEL = "DEBUG"
dbxignore daemon # foreground in this shell
Re-enable the managed daemon (systemctl --user start dbxignore.service, launchctl bootstrap, or wait for next logon on Windows) when you're done.
CLI-side debugging — pass --verbose to any command:
dbxignore --verbose status
dbxignore -v apply ~/Dropbox/some/subtree
dbxignore -v explain ~/Dropbox/build/keep
Persisting a non-default level across managed-daemon restarts requires a platform-specific override and is not covered here — it's rarely the right move (DEBUG floods the daemon log fast). For one-off investigations, the foreground-run pattern above is the recommended path.
Logs (rotated, 25 MB total):
- Windows —
%LOCALAPPDATA%\dbxignore\daemon.log. - Linux — two sinks, same records. The rotating file at
$XDG_STATE_HOME/dbxignore/daemon.log(fallback~/.local/state/dbxignore/daemon.log) is authoritative for offline debugging and bug-report bundling;journalctl --user -u dbxignore.servicesurfaces the same records via systemd-journald for live tailing and cross-service filtering. - macOS —
~/Library/Logs/dbxignore/daemon.log(rotated).~/Library/Logs/dbxignore/launchd.logcaptures launchd-time stdout/stderr (near-empty unless the daemon crashes during startup before its own log handler initializes).
State:
- Windows —
%LOCALAPPDATA%\dbxignore\state.json. - Linux —
$XDG_STATE_HOME/dbxignore/state.json(fallback~/.local/state/dbxignore/state.json). - macOS —
~/Library/Application Support/dbxignore/state.json(split from the log dir to match Apple's app-data conventions).
Backlog
Open items, planned work, and the historical record of fixes are tracked in BACKLOG.md.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dbxignore-0.4.0.tar.gz.
File metadata
- Download URL: dbxignore-0.4.0.tar.gz
- Upload date:
- Size: 307.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb9926adb0b212fc882110534bb75786e8da627a86acd6ad1668048d41a25669
|
|
| MD5 |
fedc63be77104a56ce03eac23d762352
|
|
| BLAKE2b-256 |
dbf7f4a94c466a72db5996fb212a5ca8958623553d25d07f645abffdca273830
|
Provenance
The following attestation bundles were made for dbxignore-0.4.0.tar.gz:
Publisher:
release.yml on kiloscheffer/dbxignore
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dbxignore-0.4.0.tar.gz -
Subject digest:
eb9926adb0b212fc882110534bb75786e8da627a86acd6ad1668048d41a25669 - Sigstore transparency entry: 1423493321
- Sigstore integration time:
-
Permalink:
kiloscheffer/dbxignore@06bed4b72f84c64754aab7d63d71eb9a2d5f04cb -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/kiloscheffer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@06bed4b72f84c64754aab7d63d71eb9a2d5f04cb -
Trigger Event:
push
-
Statement type:
File details
Details for the file dbxignore-0.4.0-py3-none-any.whl.
File metadata
- Download URL: dbxignore-0.4.0-py3-none-any.whl
- Upload date:
- Size: 51.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7ba7ba800cfb019849a4b3b1177fa13cad785afc5ac2209aa830b4b08dd8b2bb
|
|
| MD5 |
d3fac3b3bb5ebf3e5b35574e5db837a1
|
|
| BLAKE2b-256 |
51c57d9f2a9d575412255e1a5f8e6ff5d4d25272a068515cbdc574e263ee6152
|
Provenance
The following attestation bundles were made for dbxignore-0.4.0-py3-none-any.whl:
Publisher:
release.yml on kiloscheffer/dbxignore
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dbxignore-0.4.0-py3-none-any.whl -
Subject digest:
7ba7ba800cfb019849a4b3b1177fa13cad785afc5ac2209aa830b4b08dd8b2bb - Sigstore transparency entry: 1423493422
- Sigstore integration time:
-
Permalink:
kiloscheffer/dbxignore@06bed4b72f84c64754aab7d63d71eb9a2d5f04cb -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/kiloscheffer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@06bed4b72f84c64754aab7d63d71eb9a2d5f04cb -
Trigger Event:
push
-
Statement type: