A simple, reliable way to set up Tailscale on macOS without needing the GUI app.
This project:
- installs and runs
tailscaledvia Homebrew - manages it with a system LaunchDaemon
- configures MagicDNS explicitly
Itβs designed for machines that you want online and reachable β without needing to log in or manage an app.
Set it up once β the machine stays connected, even before login.
This is not for everyone.
Use it if you want:
- a Mac that stays connected even when nobody is logged in
- remote access to machines like iMacs, minis, or lab devices
- predictable DNS behaviour (no mystery breakage)
- to avoid the macOS Tailscale GUI entirely
If youβre happy using the official app β you probably donβt need this.
- Fully headless
tailscaled - Starts at boot via LaunchDaemon
- Explicit MagicDNS resolver setup
- Cleans up stale DNS configs automatically
- Safe for zsh environments
- Structured workflows:
- install
- verify
- repair
- uninstall
- Smart hostname detection
- Warning if hostname doesnβt match expectation
- Fallback startup if LaunchDaemon fails
Homebrew
β
tailscaled
β
LaunchDaemon
β
/etc/resolver/*
β
macOS DNS
β
MagicDNS
You must set your tailnet domain.
Example:
example-tailnet.ts.net
Find it with:
tailscale dns status --allor via:
- Tailscale Admin Console β DNS
Do not guess this β it must be exact.
Controls:
tailscale up --hostname=...If not set, itβs auto-detected from:
LocalHostNameComputerNamehostname -s
If you override it and it differs β youβll get a warning.
Best practice:
- keep it aligned with your Mac name
- only override when you actually mean to
- Tailscale account
- existing tailnet
- tailnet domain
- MagicDNS enabled (recommended)
git clone https://github.com/MrCee/tailscale-headless-macos.git
cd tailscale-headless-macoscp .env.example .envEdit:
TAILNET_DOMAIN=your-tailnet.ts.netOptional:
TS_HOSTNAME=your-mac-name./install.shThis will:
- install or relink Tailscale
- install LaunchDaemon
- start
tailscaled - configure DNS resolvers
- optionally authenticate the node
./verify.sh./fix-magicdns.sh./uninstall.shMain setup flow:
- ensures sudo session
- installs or relinks Tailscale
- prepares state + logs
- installs LaunchDaemon
- starts daemon
- writes resolver files
- flushes DNS
- optionally runs
tailscale up
Includes fallback if LaunchDaemon bootstrap fails.
Read-only diagnostics:
- binary check
- daemon state
- LaunchDaemon status
- socket presence
- resolver files
- DNS resolution
- logs
Helps identify partial vs healthy setups.
DNS repair only:
- ensures
/etc/resolverexists - removes stale
*.ts.netfiles - rewrites managed resolvers
- flushes DNS
Clean removal:
- stops daemon
- removes LaunchDaemon
- clears state + logs
- removes resolver files
- flushes DNS
Optional:
REMOVE_BREW_PACKAGE=trueResolvers are explicitly managed:
/etc/resolver/ts.net
/etc/resolver/<tailnet>.ts.net
/etc/resolver/search.tailscale
This avoids common macOS DNS edge cases in headless setups.
Old resolver files are automatically cleaned:
/etc/resolver/*.ts.net
Right after install or login:
tailscale statusYou may briefly see stale data β give it a few seconds.
cp .env.example .env
# edit .env
./install.sh
./verify.sh./fix-magicdns.sh
./verify.sh./uninstall.sh- keep everything explicit
- run at system level, not user level
- avoid hidden macOS behaviour
- separate install / verify / repair clearly
- make failures visible and fixable
- macOS Intel
- macOS Apple Silicon
- Homebrew installs
MrCee
Turn a Mac into a quiet, always-available Tailscale node.
No GUI. No surprises. Just works.