Secure Dev‑Ops: Syncing .env Files Across macOS & Linux with Syncthing + Tailscale

For developers managing environment variables across multiple machines, the "copy-paste via Slack" method is a security risk, and cloud storage (Dropb...

Deep Research AI

Author’s note:

Question: how can i use syncthing over tailscale to sync .env files across computers?

Context: Context:

I’m coding across my laptop and my mac. how do i keep them in sync? I also use 1Pass if there’s something there that can help. I’m annoyed re-managing environment variables.


Executive Summary

For developers managing environment variables across multiple machines, the “copy-paste via Slack” method is a security risk, and cloud storage (Dropbox/Google Drive) often breaks the “config in environment” principle of 12-Factor Apps. The optimal solution for syncing sensitive .env files between a laptop and a Mac is a Zero-Trust Mesh architecture combining Syncthing and Tailscale.

By binding Syncthing exclusively to Tailscale’s stable Carrier-Grade NAT (CGNAT) IP addresses (e.g., 100.x.y.z) 1, you create a private synchronization lane that is invisible to the public internet. This setup leverages WireGuard’s encryption 2 and Syncthing’s mutual TLS authentication 3, ensuring that secrets never leave your encrypted mesh. For production secrets or scenarios where file syncing is undesirable, 1Password’s CLI (op run) offers a robust alternative by injecting variables at runtime 4.


1. The Architecture: Why Syncthing + Tailscale?

The 12-Factor App methodology explicitly states that configuration should be stored in the environment, not in code 5. However, keeping these environment files (.env) synchronized across development machines is a persistent friction point.

The Tailscale Layer (The Network)

Tailscale creates a private network (tailnet) where every device is assigned a stable IP address from the 100.64.0.0/10 subnet (specifically 100.64.0.0 through 100.127.255.255) 1. Unlike standard LAN IPs (like 192.168.x.x) which change when you move from home to a coffee shop, Tailscale IPs remain constant regardless of your physical location 1. This stability is critical for Syncthing, allowing you to hard-code device addresses without relying on public discovery servers.

The Syncthing Layer (The Sync Engine)

Syncthing is a continuous file synchronization program that uses a unique Device ID (a SHA-256 hash of the device’s certificate) to authenticate peers 3. It uses TLS for all device-to-device traffic 3. By default, it broadcasts its presence to local and global discovery servers to find peers. In this secure setup, we disable these public broadcasts and force traffic solely through the Tailscale tunnel.


2. Network Design: Binding & Port Configuration

To secure the synchronization of sensitive files, we must restrict Syncthing’s communication channels.

Forcing Traffic Over Tailscale

By default, Syncthing listens on 0.0.0.0:22000 (all interfaces) and uses public relays if direct connections fail 6. To lock this down:

  1. Identify Tailscale IPs: Run tailscale ip on each machine to find its 100.x.y.z address 7.
  2. Configure Device Addresses: In Syncthing, replace the default dynamic address for your remote device with tcp://<TAILSCALE_IP>:22000 7.
  3. Disable Relays: Turn off relaysEnabled in the configuration. This forces a direct connection, which is significantly faster and more private, as relays are third-party servers that retransmit encrypted data 8.

Firewall & Port Requirements

Syncthing typically requires the following ports:

  • TCP 22000: The sync protocol port 9.
  • UDP 21027: Used for local discovery broadcasts 9.

When using Tailscale, you can rely on MagicDNS to resolve device names (e.g., my-macbook) instead of raw IPs, which simplifies configuration 10. Because we are defining static Tailscale addresses (or using MagicDNS names), you can disable globalAnnounceEnabled (Global Discovery) and localAnnounceEnabled (Local Discovery) 6. This reduces network noise and prevents your device from announcing its existence on public Wi-Fi networks.


3. Configuring Syncthing for.env Files

Syncing environment files requires precision to avoid overwriting machine-specific configurations or leaking non-secret files.

The .stignore Pattern

You likely only want to sync .env files, not the entire project directory (which git handles). Use a “whitelist” pattern in your .stignore file:

!*.env
!*.env.local
*

Note: The ! character negates the ignore pattern, effectively including the file. The final * ignores everything else.

Folder Types & Versioning

To prevent accidental data loss:

  • Folder Type: Set the source (e.g., your main laptop) to “Send Only” if you want it to be the authoritative source. Set the destination to “Receive Only” if you never edit secrets there. For bi-directional editing, use “Send & Receive” 6.
  • Versioning: Enable “Simple File Versioning” or “Staggered File Versioning” on the folder. This creates backups if a file is overwritten or deleted, providing a safety net for your secrets 6.

4. Security Deep Dive: Encryption & Untrusted Devices

This stack provides multiple layers of security for your secrets.

Transport Security

All traffic is end-to-end encrypted using TLS 3. Even if the Tailscale mesh were theoretically compromised, the Syncthing traffic inside it remains encrypted. Syncthing uses “certificate pinning” via Device IDs, meaning a device cannot join the cluster unless its specific certificate hash is manually added to your config 3.

Untrusted (Encrypted) Devices

If you need to sync .env files to a device you don’t fully trust (e.g., a shared server or a cloud instance), you can use Syncthing’s Untrusted Devices feature.

  • You set a password for the folder when sharing it with the untrusted device.
  • Data sent to that device is encrypted before leaving your trusted machine.
  • The untrusted device stores the encrypted data but cannot read it (it sees only garbage data) 11.

5. Alternative: 1Password CLI (op run)

The user mentioned using 1Password. While Syncthing is excellent for files, 1Password offers a superior workflow for runtime injection of secrets, adhering strictly to 12-Factor principles without creating persistent files on disk.

The op run Workflow

Instead of syncing a .env file, you can store secrets in 1Password and inject them directly into your application process.

  1. Define References: Create a template file (e.g., prod.env) containing references to 1Password items:
Terminal window
DB_PASSWORD=op://dev/db-prod/password
API_KEY=op://dev/stripe/secret-key
  1. Execute: Run your application using the 1Password CLI:
Terminal window
op run --env-file="./prod.env" -- npm start

The op run command scans the environment file, fetches the secrets, and passes them to the subprocess (npm start) as environment variables 4 12.

Comparison: Syncthing vs. 1Password

FeatureSyncthing + Tailscale1Password (op run)
Primary Use CaseSyncing physical .env files for dev tools that expect them.Injecting secrets into processes at runtime.
PersistenceFiles exist on disk (risk of accidental commit).Secrets exist only in memory during process execution.
Setup ComplexityMedium (Requires Tailscale + Syncthing setup).Low (Requires CLI install + Vault access).
Offline AccessYes (Files are local).Yes (CLI caches secrets, but requires initial auth).
CostFree (Open Source).Requires 1Password Subscription.

6. Practical Walkthrough: Setting Up the Sync

Here is how to implement the Syncthing + Tailscale solution on macOS and Linux.

Step 1: Install Syncthing

On macOS (via Homebrew):

Terminal window
brew install syncthing
brew services start syncthing

*Source: 13 *

On Linux: Use apt install syncthing or download the binary from the official site.

Step 2: Secure the Network

  1. Ensure Tailscale is up: tailscale up.
  2. Get your Tailscale IP: tailscale ip (e.g., 100.68.50.20).
  3. Open the Syncthing GUI (default: http://127.0.0.1:8384).

Step 3: Configure the Connection

  1. Add Device: Enter the Device ID of the other machine.
  2. Advanced Tab: In the “Addresses” field, replace dynamic with tcp://100.68.50.20:22000 (replace with the other device’s Tailscale IP) 7.
  3. Settings > Connections:
  • Uncheck “Global Discovery”.
  • Uncheck “Enable Relaying” 7.
  • Uncheck “Local Discovery” (optional, if strictly using Tailscale IPs).

Step 4: Create the .env Sync Folder

  1. Add a new folder pointing to your project’s config directory.
  2. Ignore Patterns: Add !*.env and * to the Ignore Patterns tab.
  3. Share: Share with the remote device.

Bottom Line

To securely sync .env files between your laptop and Mac:

  1. Use Tailscale IPs: Hardcode tcp://100.x.y.z:22000 in Syncthing to bypass public discovery and relays 7 1.
  2. Lock Down Syncthing: Disable Global Discovery and Relaying to reduce attack surface and latency 7 8.
  3. Whitelist Files: Use .stignore to sync only *.env files, preventing git conflicts 6.
  4. Use 1Password for Production: For deployment or strictly sensitive secrets, prefer op run to inject variables into memory rather than syncing files to disk 4.

This setup creates a private, encrypted, peer-to-peer mesh that keeps your environment variables synchronized and secure without relying on third-party cloud storage.

References

Footnotes

  1. How I Built My Own Private Cloud with Syncthing + Tailscale - Medium 2 3 4

  2. Setup Syncthing with Tailscale for P2P File Synchronisation! - Falka’s

  3. Syncthing with Tailscale? - Reddit 2 3 4 5

  4. Tailscale & Syncthing on Mac: File Transfer Help Q&A - JustAnswer 2 3

  5. Syncthing and Tailscale gave me the private cloud experience I always wanted Dropbox to be

  6. Syncthing Configuration — Syncthing documentation 2 3 4 5

  7. Getting Started — Syncthing documentation 2 3 4 5 6

  8. Ignoring Files — Syncthing documentation 2

  9. File Versioning — Syncthing documentation 2

  10. 1.1. Getting Started — Syncthing v1 documentation

  11. Tailscale quickstart · Tailscale Docs

  12. MagicDNS · Tailscale Docs

  13. About WireGuard · Tailscale Docs