Headless Daemon Mode
Neomd can run in headless daemon mode to continuously screen emails in the background without launching the TUI. This is useful for running neomd on a server (like a NAS) that screens emails automatically, while you use the TUI on your laptop or mobile device.
Overview
When running in headless mode, neomd:
- Screens emails automatically every
bg_sync_intervalminutes - Watches screener list files and reloads when they change (via Syncthing)
- Runs in the background as a standard process
- Logs to stdout for monitoring
Quick Start
# Run in foreground (for testing)
neomd --headless
# Run in background
neomd --headless &
# Run in background with logging
nohup neomd --headless > /var/log/neomd.log 2>&1 &
# Or redirect to a file
neomd --headless >> ~/.local/share/neomd/daemon.log 2>&1 &Multi-Device Setup with Syncthing
The headless daemon is designed to work with Syncthing to keep screener lists synchronized across multiple devices.
Architecture
- NAS/Server: Runs
neomd --headlesscontinuously, screening emails every 5 minutes - Laptop: Runs TUI with
bg_sync_interval = 0(disabled), classifies senders manually - Android/Mobile: Runs TUI with
bg_sync_interval = 0(disabled), classifies senders manually - Syncthing: Syncs screener list files across all devices
Benefits
- Automatic screening: Emails are screened on the server even when your laptop/phone is offline
- Mobile email apps work: Your phone’s native email app sees screened emails in the correct IMAP folders
- No conflicts: Only the daemon moves emails; TUI instances only classify senders
- Instant sync: Classification decisions propagate to all devices via Syncthing
Configuration
Server Config (Daemon)
On your NAS/server, set bg_sync_interval to enable periodic screening:
# ~/.config/neomd/config.toml (server)
[ui]
bg_sync_interval = 5 # Screen inbox every 5 minutesLaptop/Mobile Config (TUI)
On devices where you run the TUI, disable background sync to avoid duplicate moves:
# ~/.config/neomd/config.toml (laptop/mobile)
[ui]
bg_sync_interval = 0 # Disable background screening (daemon handles it)Syncthing Setup
What Gets Synced
Screener list directory: ~/.config/neomd/lists/ - you can also sync the entire neomd folder, if you don’t have passwords stored in there, only ENVs:
screened_in.txtscreened_out.txtfeed.txtpapertrail.txtspam.txt
Step-by-Step Setup
1. Install Syncthing
On Arch Linux / Server:
sudo pacman -S syncthing
systemctl --user enable syncthing
systemctl --user start syncthingOn other systems: See Syncthing installation docs
Note: Use
systemctl --userinstead of adding to window manager autostart scripts. This ensures Syncthing runs on login, works across different environments, and continues running independently of your desktop session.
2. Access Web UI
Open http://localhost:8384 on each device
3. Connect Devices
On Device A (e.g., your laptop):
- Go to Actions → Show ID to get your Device ID
- Copy the long alphanumeric Device ID
On Device B (e.g., your server):
- Click Add Remote Device
- Paste Device A’s Device ID
- Name it (e.g., “laptop”)
- Click Save
Back on Device A:
- Accept the connection notification
- Name Device B (e.g., “server”)
- Click Save
Repeat for all devices (laptop, server, Android).
4. Create Shared Folder
On one device (e.g., server):
- Click Add Folder
- Set Folder Label:
neomd-lists - Set Folder ID:
neomd-lists(same on all devices) - Set Folder Path:
/home/user/.config/neomd/lists/(or~/.config/neomd/if syncing entire folder) - Go to Sharing tab → check all other devices
- Go to File Versioning tab:
- Select Simple File Versioning
- Keep Versions:
5
- Click Save
On other devices:
- Accept the folder share notification
- Verify/set the correct path for that device
- Enable File Versioning (same as above)
- Click Save
5. Backup First (Important!)
Before syncing existing data, backup your lists:
cp -r ~/.config/neomd/lists ~/.config/neomd/lists.backup-$(date +%Y%m%d)6. Wait for Initial Sync
Watch the folder status in the web UI. It will show “Syncing” with progress, then “Up to Date” when complete.
Server Setup (FreeBSD / No GUI)
If running neomd headless on a FreeBSD server without a desktop environment, use SSH port forwarding to access the Syncthing web UI:
1. Start Syncthing on FreeBSD
# Enable and start as system service
sudo sysrc syncthing_enable="YES"
sudo sysrc syncthing_user="sspaeti"
sudo service syncthing start
# Or run as user service (no sudo)
syncthing &
# Or with nohup for persistent operation
nohup syncthing > ~/syncthing.log 2>&1 &Check it’s running:
ps aux | grep syncthing2. SSH Port Forwarding
From your local machine (laptop/desktop with browser), create an SSH tunnel:
ssh -L 8385:localhost:8384 your-serverThis forwards localhost:8385 on your local machine to localhost:8384 on the server.
Now open in your local browser: http://localhost:8385
You’ll see the server’s Syncthing web UI!
3. Get Server Device ID
In the web UI at http://localhost:8385:
- Go to Actions → Show ID
- Copy the Device ID
4. Connect Your Devices
On your local machine’s Syncthing (http://localhost:8384):
- Click Add Remote Device
- Paste the server’s Device ID
- Name it (e.g., “freebsd-server”)
- Click Save
On the server’s UI (http://localhost:8385 via SSH tunnel):
- Accept the connection notification
- Name your local device (e.g., “laptop”)
- Click Save
5. Share the Folder
On your local machine (http://localhost:8384):
- Find your existing
neomd-listsfolder - Click Edit
- Go to Sharing tab
- Check the box next to your server device
- Click Save
On the server (http://localhost:8385):
- Accept the folder share notification
- Set Folder Path:
/home/user/.config/neomd/lists/(or~/.config/neomd/if syncing entire folder) - Go to File Versioning tab:
- Select Simple File Versioning
- Keep Versions:
5
- Click Save
6. Handle Existing Files
Before syncing, backup the server’s existing lists:
# On server
cp -r ~/.config/neomd/lists ~/.config/neomd/lists.backup-$(date +%Y%m%d)Syncthing will merge files from both sides. To start fresh from your local machine’s data:
# On server - remove existing files (after backup!)
rm -rf ~/.config/neomd/lists/*7. Close SSH Tunnel
Once setup is complete, you can close the SSH tunnel (Ctrl+C in the SSH session). Devices will continue syncing in the background.
For future configuration changes, create the SSH tunnel again when needed:
ssh -L 8385:localhost:8384 your-serverVerify Sync is Working
# Check files exist
ls -la ~/.config/neomd/lists/
# Watch real-time sync in logs
journalctl --user -u syncthing -fThe daemon watches for file changes and reloads screener lists automatically when Syncthing updates them.
Conflict Handling
- File-level conflicts: Syncthing creates
.sync-conflict-*files if two devices modify the same file simultaneously - Email-level: IMAP is the source of truth; no local email state to conflict
- Screener lists: Append-only operations are safe; duplicates are harmless (normalized automatically)
Check for conflicts periodically:
find ~/.config/neomd/lists -name "*.sync-conflict-*"Systemd Service (Optional)
For servers with systemd, you can create a service unit for automatic startup and logging:
# /etc/systemd/user/neomd.service
[Unit]
Description=Neomd Headless Email Screener
After=network.target
[Service]
Type=simple
ExecStart=%h/.local/bin/neomd --headless
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=default.targetEnable and start the service:
# Install neomd to ~/.local/bin
make install
# Reload systemd
systemctl --user daemon-reload
# Enable auto-start on login
systemctl --user enable neomd
# Start now
systemctl --user start neomd
# Check status
systemctl --user status neomd
# View logs
journalctl --user -u neomd -fMonitoring
View Logs
If running with nohup or redirected output:
tail -f /var/log/neomd.logIf running as systemd service:
journalctl --user -u neomd -fLog Format
The daemon logs structured output with timestamps:
time=2025-04-18T10:00:00Z level=INFO msg="neomd daemon starting" version=headless
time=2025-04-18T10:00:00Z level=INFO msg="screening interval configured" minutes=5
time=2025-04-18T10:00:00Z level=INFO msg="watching directory for changes" dir=/home/user/.config/neomd/lists
time=2025-04-18T10:00:00Z level=INFO msg="daemon running" interval=5m0s
time=2025-04-18T10:00:05Z level=INFO msg="running initial screening"
time=2025-04-18T10:00:05Z level=INFO msg="fetched inbox emails" count=42
time=2025-04-18T10:00:05Z level=INFO msg="emails to screen" count=12
time=2025-04-18T10:00:05Z level=INFO msg="screened email" index=1 total=12 from="newsletter@example.com" subject="Weekly Update" dst=Feed
...
time=2025-04-18T10:00:06Z level=INFO msg="screening complete" moved=12 total=12Graceful Shutdown
Send SIGTERM or SIGINT to stop the daemon:
# If running in foreground
Ctrl+C
# If running in background
kill <pid>
# With systemd
systemctl --user stop neomdThe daemon will finish the current screening operation before exiting.
Troubleshooting
Daemon exits immediately
Check that bg_sync_interval is set to a value > 0:
grep bg_sync_interval ~/.config/neomd/config.tomlScreener lists not reloading
Check file watcher logs:
tail -f /var/log/neomd.log | grep "watching directory"Verify Syncthing is running and syncing:
# Check Syncthing web UI (usually http://localhost:8384)Emails not being screened
- Check daemon is running:
ps aux | grep neomd - Check IMAP connection in logs
- Verify screener list files exist and contain email addresses
- Check folder configuration in config.toml
Duplicate screening
If emails are being moved twice (once by daemon, once by TUI):
- Set
bg_sync_interval = 0on TUI devices - Only run one daemon instance per account
Android Termux Example
Note
See Android Termux Setup at Android Docs
On Android, you can run the daemon in a Termux session:
# Install Termux:Boot from F-Droid to auto-start on device boot
pkg install termux-boot
# Create boot script
mkdir -p ~/.termux/boot
cat > ~/.termux/boot/neomd-daemon.sh <<'EOF'
#!/data/data/com.termux/files/usr/bin/bash
cd ~/neomd
nohup ./neomd --headless >> ~/neomd-daemon.log 2>&1 &
EOF
chmod +x ~/.termux/boot/neomd-daemon.sh
# Reboot device to auto-start daemonNotes
- The daemon only reads screener list files and moves emails via IMAP
- All sender classification (adding to lists) happens in the TUI
- File watching requires the screener list directory to exist
- The daemon uses the first configured account from
config.toml - IMAP connection is kept alive and automatically reconnects on failures