echo6-docs/vault/runbooks/syncthing-add-node.md
echo6-autocommit eb7eade7fa auto: docs sync 2026-06-18T18:00:10+00:00
Files changed: .gitignore CLAUDE.md credentials engine/.embcache.json engine/changelog.md engine/config.yaml engine/lib/__pycache__/agent.cpython-312.pyc engine/lib/agent.py engine/lib/lint.py engine/lint-report.md engine/sweep-full.log engine/sweep.sh vault/.obsidian/graph.json vault/.obsidian/workspace.json vault/INDEX.md vault/archive/projects/mmud/last-ember-chronicle.html vault/archive/projects/mmud/last-ember-howto.html vault/archive/projects/mmud/last-ember.html vault/archive/projects/mmud/mmud-phase5-prompt.md vault/archive/projects/mmud/mmud-phase6-prompt.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/01-update-planned.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/02-npc-nodes.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/03-darkcragg.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/04-dcrg-node.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/05-phase5.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/06-phase6.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/README.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/mmud-project.md vault/docs/hardware/environment.md vault/docs/hardware/ip-allocation.md vault/docs/matrix/archivist.md vault/docs/matrix/matrix_host.md vault/docs/matrix/mautrix_signal.md vault/docs/matrix/synapse.md vault/docs/matrix/synapse_retention_discovery.md vault/docs/navi/cc-rules.md vault/docs/navi/deployment.md vault/docs/navi/themes.md vault/docs/services/ots-setup.md vault/docs/services/services.md vault/docs/services/usenet.md vault/docs/software/authentik.md vault/docs/software/caddy.md vault/docs/software/dns.md vault/docs/software/geo-tools.md vault/docs/software/recon.md vault/docs/software/searxng.md vault/glossary.md vault/notes/echo6-landing-page-data-export.md vault/notes/ia-download-queue.md vault/plans/vaultwarden-plan.md vault/projects/advbbs-project.md vault/projects/argus.md vault/projects/deploy-livesync.md vault/projects/matrix-synapse-deployment.md vault/projects/meshtastic-headscale-runbook.md vault/projects/mmud-project.md vault/runbooks/add-peertube-channel.md vault/runbooks/authentik-access-groups.md vault/runbooks/authentik-create-invitation.md vault/runbooks/authentik-oidc-application.md vault/runbooks/authentik-upgrade.md vault/runbooks/ct-runbook.md vault/runbooks/edge2-access-reference.md vault/runbooks/expose-service-contabo.md vault/runbooks/expose-service-edge2.md vault/runbooks/expose-service-home.md vault/runbooks/headscale-onboard-node.md vault/runbooks/ia-cli-reference.md vault/runbooks/ia-download-mirror.md vault/runbooks/idahomesh-bridge-setup.md vault/runbooks/idahomesh-vpn-device-setup.md vault/runbooks/lxc-service-migration.md vault/runbooks/mailcow-create-mailbox.md vault/runbooks/meshmonitor-password-reset.md vault/runbooks/meshtastic-sidecar-node.md vault/runbooks/meshtasticd-sim-nodes-runbook.md vault/runbooks/nordvpn-lxc.md vault/runbooks/peertube-remote-runner.md vault/runbooks/pg-backup.md vault/runbooks/pi-nas-omv-runbook.md vault/runbooks/pipeline-patterns.md vault/runbooks/proxmox-create-ubuntu-vm.md vault/runbooks/proxmox-onboard-node.md vault/runbooks/recon-operations.md vault/runbooks/recon-service-integration.md vault/runbooks/syncthing-add-node.md vault/session-resume/SESSION-HANDOFF-meshai-test.md
2026-06-18 18:00:10 +00:00

9.6 KiB

title type tags aliases related updated
Syncthing: Add a New Node to the Project Sync Cluster runbook
mesh
ct-runbook
proxmox-onboard-node
meshtasticd-sim-nodes-runbook
idahomesh-vpn-device-setup
headscale-onboard-node
2026-06-18

Syncthing: Add a New Node to the Project Sync Cluster

Overview

Adds a new machine to the Syncthing projects folder mesh. All nodes sync bidirectionally — new files merge, nothing is overwritten or deleted.

Current cluster:

Node Device ID (short) Path OS
cortex 6VP7KIB /home/zvx/projects Ubuntu 24.04
contabo SBYGD4P /home/zvx/projects Ubuntu 24.04
bluefin 5ZTWIXM /var/home/malice/projects Fedora Atomic
matt-desktop GCH6AAG E:\Documents\projects Windows

Syncthing version: v2.0.15 (all nodes must run v2.x — v1.x is incompatible)

Config API: All configuration changes are done via the REST API at http://127.0.0.1:8384/rest/config using the API key from each node's config XML.


Prerequisites

  • New node has network access to at least one existing node (Tailscale preferred)
  • SSH access to the new node and at least one existing node

Step 1: Install Syncthing v2

Linux (apt-based)

Download the binary directly — the apt repo may only have v1.x:

curl -fsSL https://github.com/syncthing/syncthing/releases/download/v2.0.15/syncthing-linux-amd64-v2.0.15.tar.gz -o /tmp/syncthing.tar.gz
tar -xzf /tmp/syncthing.tar.gz -C /tmp
sudo cp /tmp/syncthing-linux-amd64-v2.0.15/syncthing /usr/bin/syncthing
syncthing --version  # verify v2.x

Linux (Homebrew — Fedora Atomic/Bluefin)

brew install syncthing
# Creates ~/.local/state/syncthing/ for config

Windows

New-Item -ItemType Directory -Force -Path "$HOME\syncthing"
Invoke-WebRequest -Uri "https://github.com/syncthing/syncthing/releases/download/v2.0.15/syncthing-windows-amd64-v2.0.15.zip" -OutFile "$HOME\syncthing\st.zip"
Expand-Archive -Path "$HOME\syncthing\st.zip" -DestinationPath "$HOME\syncthing" -Force
Copy-Item "$HOME\syncthing\syncthing-windows-amd64-v2.0.15\syncthing.exe" "$HOME\syncthing\syncthing.exe" -Force
Remove-Item -Recurse -Force "$HOME\syncthing\syncthing-windows-amd64-v2.0.15"
Remove-Item "$HOME\syncthing\st.zip"

Step 2: Generate Config and Get Device ID

syncthing generate
# Output includes: device=XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX

Save the full device ID — you'll need it for all other nodes.


Step 3: Set Up Auto-Start

Linux (systemd service — existing unit)

If syncthing@<user>.service exists (apt installs it):

sudo systemctl enable --now syncthing@zvx

Linux (systemd user service — manual)

Create ~/.config/systemd/user/syncthing.service:

[Unit]
Description=Syncthing - Open Source Continuous File Synchronization
After=network.target

[Service]
ExecStart=/path/to/syncthing serve --no-browser --no-restart --logflags=0
Restart=on-failure
RestartSec=10
SuccessExitStatus=3 4
RestartForceExitStatus=3 4

[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user enable --now syncthing

Windows (Scheduled Task)

schtasks /create /tn Syncthing /tr "C:\Users\administrator\syncthing\syncthing.exe serve --no-browser --no-restart" /sc onlogon /rl highest /f

Then start it for the current session:

Start-Process -FilePath "$HOME\syncthing\syncthing.exe" -ArgumentList "serve","--no-browser","--no-restart" -WindowStyle Hidden

Windows Firewall

Required — syncthing won't accept connections without this:

netsh advfirewall firewall add rule name="Syncthing" dir=in action=allow program="C:\Users\administrator\syncthing\syncthing.exe" enable=yes
netsh advfirewall firewall add rule name="Syncthing-Out" dir=out action=allow program="C:\Users\administrator\syncthing\syncthing.exe" enable=yes

Step 4: Configure the New Node via REST API

Wait for syncthing to start (~5 seconds), then get the API key:

# Linux
grep apikey ~/.local/state/syncthing/config.xml | sed 's/.*<apikey>//' | sed 's/<\/apikey.*//'

# Windows (PowerShell)
([xml](Get-Content "$env:LOCALAPPDATA\Syncthing\config.xml")).configuration.gui.apikey

Use the API to add devices and the projects folder. This Python snippet does it all — run it on the new node:

import json, urllib.request

API_KEY = "<apikey from above>"
MY_DEVICE_ID = "<new node device ID>"
PROJECTS_PATH = "<local path to projects folder>"  # e.g. /home/zvx/projects

# All cluster nodes — add the new node's ID to this list when updating existing nodes
DEVICES = {
    "cortex": {"id": "6VP7KIB-ZHBI3AT-XO5FMY2-LFAZYM6-UMAV75U-MZZADW3-ZOBHJXY-GF26DAC", "addr": "tcp://100.64.0.14:22000"},
    "contabo": {"id": "SBYGD4P-BUWMWRQ-JJYYG75-YBR4WOO-OH42WH4-IAAO33D-STJZX6O-SZA2SQ4", "addr": "tcp://100.64.0.1:22000"},
    "bluefin": {"id": "5ZTWIXM-XNBUEW5-XWJM7PG-FJDMX5H-YMXM3CC-ZVS2PNO-NG2E3KJ-D5HXKQB", "addr": "dynamic"},
    "matt-desktop": {"id": "GCH6AAG-IWPH6TR-7GI7THZ-DIVXRRQ-EQMRBNN-IZG7Y2F-HM6BRLX-AC3MIQ6", "addr": "dynamic"},
}

def api(method, path, data=None):
    url = f"http://127.0.0.1:8384{path}"
    body = json.dumps(data).encode() if data else None
    req = urllib.request.Request(url, data=body, method=method,
        headers={"X-API-Key": API_KEY, "Content-Type": "application/json"})
    return json.loads(urllib.request.urlopen(req).read())

cfg = api("GET", "/rest/config")
existing_ids = [d["deviceID"] for d in cfg["devices"]]

# Add all peer devices
for name, dev in DEVICES.items():
    if dev["id"] not in existing_ids and dev["id"] != MY_DEVICE_ID:
        cfg["devices"].append({
            "deviceID": dev["id"], "name": name,
            "addresses": [dev["addr"]], "compression": "metadata",
            "paused": False, "autoAcceptFolders": False
        })

# Add projects folder if missing
folder_ids = [f["id"] for f in cfg["folders"]]
if "projects" not in folder_ids:
    all_device_ids = [d["id"] for d in DEVICES.values()] + [MY_DEVICE_ID]
    cfg["folders"].append({
        "id": "projects", "label": "projects",
        "path": PROJECTS_PATH, "type": "sendreceive",
        "rescanIntervalS": 60, "fsWatcherEnabled": True, "fsWatcherDelayS": 10,
        "devices": [{"deviceID": did} for did in set(all_device_ids)]
    })

api("PUT", "/rest/config", cfg)
print("New node configured")

Step 5: Add the New Node to ALL Existing Nodes

For each existing node, run the following (substituting the new node's device ID and name):

APIKEY=$(grep apikey ~/.local/state/syncthing/config.xml | sed 's/.*<apikey>//' | sed 's/<\/apikey.*//')
NEW_ID="<new node device ID>"
NEW_NAME="<new node name>"

CONFIG=$(curl -s -H "X-API-Key: $APIKEY" http://127.0.0.1:8384/rest/config)
CONFIG=$(echo "$CONFIG" | python3 -c "
import json,sys
c = json.load(sys.stdin)
did = '$NEW_ID'
ids = [d['deviceID'] for d in c['devices']]
if did not in ids:
    c['devices'].append({'deviceID': did, 'name': '$NEW_NAME', 'addresses': ['dynamic'], 'compression': 'metadata', 'paused': False, 'autoAcceptFolders': False})
for f in c['folders']:
    if f['id'] == 'projects':
        fids = [d['deviceID'] for d in f['devices']]
        if did not in fids:
            f['devices'].append({'deviceID': did})
json.dump(c, sys.stdout)
")
curl -s -X PUT -H "X-API-Key: $APIKEY" -H 'Content-Type: application/json' -d "$CONFIG" http://127.0.0.1:8384/rest/config

Important: The CLI (syncthing cli config devices add / syncthing cli config folders <id> devices add) panics on v2.0.15 with a reflect.Value.Elem on slice Value bug. Always use the REST API instead.


Step 6: Verify

Check connections from the new node:

syncthing cli show connections

Check sync status:

APIKEY=$(grep apikey ~/.local/state/syncthing/config.xml | sed 's/.*<apikey>//' | sed 's/<\/apikey.*//')
curl -s -H "X-API-Key: $APIKEY" http://127.0.0.1:8384/rest/db/status?folder=projects | python3 -m json.tool

Key fields: state should be syncing then idle, needFiles should reach 0.


Troubleshooting

Problem Cause Fix
Connections establish then drop with "reading length: EOF" Version mismatch (v1 vs v2) Upgrade all nodes to v2.x
Node shows as device but never connects Firewall blocking port 22000 Open inbound/outbound for syncthing binary (Windows) or port 22000 (Linux)
syncthing cli config ... add panics Known bug in v2.0.15 CLI Use REST API at http://127.0.0.1:8384/rest/config instead
Windows: syncthing not listening after start Process started but exited silently Check %LOCALAPPDATA%\Syncthing\ for config issues; restart with --logfile flag
SSH to Windows mangles backslashes Bash SSH escaping Use PowerShell scripts via SCP, or use $HOME\ which expands server-side

Config File Locations

OS Config XML Data/Index
Linux (apt) ~/.local/state/syncthing/config.xml ~/.local/state/syncthing/
Linux (brew) ~/.local/state/syncthing/config.xml ~/.local/state/syncthing/
Windows %LOCALAPPDATA%\Syncthing\config.xml %LOCALAPPDATA%\Syncthing\

API Reference

  • Get config: GET http://127.0.0.1:8384/rest/config
  • Set config: PUT http://127.0.0.1:8384/rest/config (full config JSON)
  • Connections: GET http://127.0.0.1:8384/rest/system/connections
  • Folder status: GET http://127.0.0.1:8384/rest/db/status?folder=projects
  • Header: X-API-Key: <apikey>