Insights

How to Turn a Mac mini Into a 24/7 Home Server and Connect It with SSH and Tailscale

5 min read#mac#ssh#tailscale#home-server#automation#launchd

Who this is forMac users who run scheduled jobs or automation tools on a laptop and want a reliable, remotely reachable machine at home.

Automation that runs on a laptop breaks as soon as the laptop sleeps. Scheduled jobs are delayed or skipped, and a closed lid over a weekend can wipe out an entire day’s worth of work. This note describes how I moved that workload to a Mac mini that runs 24/7, and how I connected it to my MacBook with SSH and Tailscale so that a single command, ssh macmini, reaches it from anywhere. You will get the connection options compared, the key-based login steps, a reusable SSH config, the power settings that keep the machine awake, and the migration issues I ran into when moving launchd jobs between machines with different user names.

Summary

The problem was that my MacBook went to sleep and stopped running automation jobs. I switched the Mac mini to a 24/7 home server and connected it remotely with SSH and Tailscale. Once the connection was set up, I could control the Mac mini from anywhere with ssh macmini.

Choosing a Remote Connection Method

I compared three ways to reach the Mac mini. Each has a different range, capability, and setup cost.

Item macOS Screen Sharing (VNC) Tailscale SSH Chrome Remote Desktop
Network scope Same Wi-Fi only Anywhere (outside networks OK) Anywhere
SSH/terminal ✅ Supported ✅ Supported ❌ Screen only
File transfer ✅ scp/rsync ✅ scp/rsync Limited
Performance Best on local network Negligible VPN overhead Low (browser-based)
Setup difficulty Built-in OS feature Tailscale account + app Google account + extension
Use case Inside the home only Home and outside, combined (recommended) Screen only, for urgent cases

Screen Sharing is limited to the local network, and Chrome Remote Desktop does not give you a terminal. Tailscale covers both gaps. It works from a café or a trip, and it supports scp and rsync for file transfer, so it is the option I recommend.

Setting Up Passwordless SSH Login with Keys

Follow these steps in order.

  1. On the Mac mini: open System Settings → General → Sharing and turn on Remote Login.
  2. Generate a key on the MacBook: ssh-keygen -t ed25519 -C "macbook"
  3. Send the public key: ssh-copy-id -i ~/.ssh/id_ed25519.pub mac_mini@[Mac mini IP]
    • If you see “The authenticity of host…” on the first connection, type yes.
  4. Register the key with ssh-agent using the Keychain: ssh-add --apple-use-keychain ~/.ssh/id_ed25519
    • After this, the passphrase is handled automatically in later sessions.

Connecting with One Line Using an SSH Config Alias

Register both addresses in ~/.ssh/config and you can connect with ssh macmini alone:

Host macmini
    HostName 100.123.107.37           # Tailscale IP (outside networks OK)
    User mac_mini
    IdentityFile ~/.ssh/id_ed25519
    AddKeysToAgent yes
    UseKeychain yes

Host macmini-local
    HostName 192.168.45.125           # Home Wi-Fi (faster)
    User mac_mini
    IdentityFile ~/.ssh/id_ed25519
    AddKeysToAgent yes
    UseKeychain yes

The macmini entry uses the Tailscale address, so it works from any network. The macmini-local entry uses the home Wi-Fi address, which is faster when you are at home.

What Tailscale Does

  • Two personal computers are free, tied to a single account.
  • Each device gets a fixed IP address (100.x.x.x).
  • It is a VPN mesh network, so the same IP works from any network.
  • It supports existing protocols such as SSH, VNC, and scp without changes.
  • After installation, signing in to the same account on both the MacBook and the Mac mini pairs them automatically.

To check the connection, run tailscale status. It shows the connected devices and their IPs.

Running Remote Commands and Transferring Files

From a terminal on the MacBook, including a Claude Code session, you can run commands on the Mac mini directly:

# Run a command
ssh macmini 'brew install node@22'
ssh macmini 'launchctl load ~/Library/LaunchAgents/com.ggplab.tech-news-digest.plist'

# Transfer a file
scp ~/local-file.plist macmini:~/Library/LaunchAgents/

# Synchronize a directory (incremental, with exclusion rules)
rsync -avz --exclude='*.log' ~/.n8n/ macmini:~/.n8n/

Configuring the Mac mini as a 24/7 Home Server

Turn off sleep completely and enable automatic restart after a power failure:

# Disable sleep completely + restart automatically after a power outage
sudo pmset -a sleep 0 displaysleep 10 disksleep 0 \
  autorestart 1 powernap 1 womp 1 \
  tcpkeepalive 1 networkoversleep 1

# Enable automatic login (System Settings → Users & Groups)
# → This is what allows launchd jobs to access the Keychain

Migrating launchd Automation: A Real Case

These are the results of moving my workload from the MacBook to the Mac mini:

  • n8n (port 5678, daemon): I copied all of ~/.n8n/ with rsync, and launchd starts it automatically on the Mac mini.
  • worklog-server (port 3010): The code lives on GitHub. I rebuilt only the runtime environment on the Mac mini.
  • Four daily scripts: I changed only the paths in the plists, replacing /Users/limjung/ with /Users/mac_mini/.
  • Health report: Every day at 23:50, it collects the status of all jobs and sends it to Discord.
  • claude-code-ping: I redesigned it from a Terminal GUI approach to a headless claude -p "hi" call.

Why a Laptop Is a Poor Home Server

A laptop manages sleep by default, so launchd or cron jobs get delayed or skipped. If you close the lid and leave over a weekend, the automation for that entire period is lost. This actually happened to me: over one weekend, daily-todo, tech-news, and other jobs all failed to run. An always-on mini desktop such as a Mac mini, an Intel NUC, or a Raspberry Pi is the right structure for an automation server, separate from your daily laptop.

Claude Code Appears to Install the Remote Machine, but It Runs Commands over SSH

Throughout this migration, I set up the entire Mac mini environment (Node.js, n8n, launchd jobs, the gws CLI, and more) from a Claude Code session on the MacBook. From the outside, it looks like an AI went into the remote machine and did the work. In reality, every action was carried out as a remote command run through a single ssh macmini '...' line. Once key-based passwordless SSH is in place, an AI agent can operate the remote machine with the same permissions a local terminal has.

SSH Non-Interactive Sessions and Keychain Access

The macOS Keychain is locked by default in non-interactive SSH sessions. As a result, ssh macmini "claude -p ..." fails with a “Not logged in” error. There are three ways around this:

  • Require a GUI session: Turn on automatic login so that a user session is always active right after boot.
  • launchd is the exception: As long as a GUI session is running, launchd jobs can access the Keychain. I verified this in practice.
  • Alternative: Put ANTHROPIC_API_KEY in ~/.zshenv to bypass the Keychain. This is billed separately from a subscription.

Hardcoded plist Paths Are the Main Migration Complexity

Existing plists are full of absolute paths such as /Users/[macbook-username]/ and /Users/[macbook-username]/.nvm/versions/node/v22.17.0/bin/. Because the Mac mini has a different user name (mac_mini), every plist has to be edited. A bulk replacement with sed handles most of this. If Node.js is installed through nvm on one machine and Homebrew on the other, you need an extra mapping. Using $HOME or ~ in new plists is worth considering, but launchd does not expand ~. A more robust pattern is a wrapper shell script that moves paths out into environment variables.

Sources

Bottom line

Running automation on a MacBook is unreliable because sleep silently skips scheduled jobs. A Mac mini with sleep disabled, reachable through SSH keys and Tailscale, gives you a dedicated 24/7 server that you can manage from anywhere with ssh macmini. The migration is mostly a matter of rewriting plist paths and copying data, with the Keychain and non-interactive session limits as the main things to plan for.

Frequently asked questions

Why do scheduled jobs fail when I run them on a MacBook?
A laptop goes to sleep by default, and launchd or cron jobs are delayed or skipped while it sleeps. If the lid is closed for a whole weekend, every automation scheduled in that window is lost. A Mac mini that stays awake avoids this.
How do I connect to my Mac mini from anywhere with one command?
Enable Remote Login on the Mac mini, set up SSH key authentication, install Tailscale on both machines under the same account, and add a Host macmini entry to ~/.ssh/config. After that, ssh macmini works from any network.

Want the full system? The Claude Code & Codex Skills guidebook collects the skills and subagents behind this blog, from $19.