<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Animesh Mohanty</title>
    <description>The latest articles on DEV Community by Animesh Mohanty (@animo93).</description>
    <link>https://dev.to/animo93</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4190857%2F9bef6e8e-e8f5-4e86-b791-c6d9c29e6cee.jpg</url>
      <title>DEV Community: Animesh Mohanty</title>
      <link>https://dev.to/animo93</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/animo93"/>
    <language>en</language>
    <item>
      <title>Turning an Old MacBook into a Home Server</title>
      <dc:creator>Animesh Mohanty</dc:creator>
      <pubDate>Sun, 18 Oct 2026 10:21:59 +0000</pubDate>
      <link>https://dev.to/animo93/turning-an-old-macbook-into-a-home-server-273g</link>
      <guid>https://dev.to/animo93/turning-an-old-macbook-into-a-home-server-273g</guid>
      <description>&lt;p&gt;Few months back , I have replaced my old Macbook pro with a &lt;a href="https://www.tuxedocomputers.com/index.php" rel="noopener noreferrer"&gt;tuxedo&lt;/a&gt; laptop and created a &lt;a href="https://medium.com/@animeshmohanty84/breaking-the-walled-garden-my-one-month-review-switching-from-macbook-to-tuxedo-4f22e5322bce" rel="noopener noreferrer"&gt;medium article&lt;/a&gt; about it .&lt;/p&gt;

&lt;p&gt;Recently I decided to repurpose it into a remote server , so that I could access seamlessly via SSH from my primary linux machine , instead of letting it gather dust.&lt;/p&gt;

&lt;p&gt;Here is the step-by-step story of how I configured it, the networking and OS-level hurdles I hit along the way, and how to solve every single one of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Networking Decision between Static IPs , WireGuard and Tailscale
&lt;/h2&gt;

&lt;p&gt;My first question was simple: &lt;em&gt;Do I need to buy a static IP address from my ISP to SSH into my MacBook from outside my home network?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Thankfully, the answer is &lt;strong&gt;no&lt;/strong&gt;. While home ISPs assign dynamic public IPs (and frequently lock connections behind CGNAT), you generally have three paths for remote access:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic DNS (DDNS) + Port Forwarding:&lt;/strong&gt; Free services like &lt;a href="https://www.duckdns.org/" rel="noopener noreferrer"&gt;DuckDNS&lt;/a&gt; map a domain to your changing IP, but you still have to open port &lt;code&gt;22&lt;/code&gt; on your home router—exposing your SSH daemon to constant automated brute-force bots.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Raw WireGuard:&lt;/strong&gt; &lt;a href="https://www.wireguard.com/" rel="noopener noreferrer"&gt;WireGuard&lt;/a&gt; is an extraordinarily fast,open source VPN protocol. However, running raw WireGuard still requires a designated server endpoint, manual UDP port forwarding (&lt;code&gt;51820&lt;/code&gt;), and DDNS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Headscale vs. Tailscale:&lt;/strong&gt; I briefly looked at &lt;strong&gt;&lt;a href="https://www.proxmox.com/en/" rel="noopener noreferrer"&gt;Proxmox&lt;/a&gt;&lt;/strong&gt; (which is a bare-metal hypervisor and doesn't solve the networking layer on its own) and &lt;strong&gt;&lt;a href="https://headscale.net/stable/" rel="noopener noreferrer"&gt;Headscale&lt;/a&gt;&lt;/strong&gt; (the open-source, self-hosted control plane for Tailscale). However, Headscale requires an external VPS with a public IP to coordinate handshakes anyway.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; I opted for &lt;strong&gt;&lt;a href="https://tailscale.com/" rel="noopener noreferrer"&gt;Tailscale&lt;/a&gt;&lt;/strong&gt;. It uses WireGuard under the hood as its data plane (meaning traffic is peer-to-peer encrypted), while acting as a zero-config coordination plane that punches through CGNAT and firewalls automatically. Best of all, Tailscale assigns each device a &lt;strong&gt;permanent &lt;code&gt;100.x.y.z&lt;/code&gt; IP address&lt;/strong&gt; tied to its cryptographic node key—meaning my MacBook's virtual IP never changes, no matter where I take my client laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Setting Up Remote Login &amp;amp; Hitting the First Roadblocks
&lt;/h2&gt;

&lt;p&gt;With Tailscale installed on both my MacBook and my Linux laptop, I enabled macOS Remote Login via the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemsetup &lt;span class="nt"&gt;-setremotelogin&lt;/span&gt; on

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Roadblock #1: Pinging the Public IP Fails
&lt;/h3&gt;

&lt;p&gt;Initially, I tried pinging my MacBook’s public IP from my Linux machine to test connectivity and got 100% packet loss.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Cause:&lt;/strong&gt; Home routers drop incoming WAN ICMP (&lt;code&gt;ping&lt;/code&gt;) requests by default, and Tailscale doesn't route over public IPs anyway—it builds an isolated Layer 3 virtual network (&lt;code&gt;utun&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; Always test connectivity against the &lt;strong&gt;Tailscale IP&lt;/strong&gt; (&lt;code&gt;100.x.y.z&lt;/code&gt;), not the public IP:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tailscale ping &amp;lt;mac_tailscale_ip&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Roadblock #2: &lt;code&gt;No ED25519 host key is known for &amp;lt;ip&amp;gt; and you have requested strict checking&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When I tried to SSH into the Mac's Tailscale IP for the first time, my Linux client immediately rejected the connection.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Cause:&lt;/strong&gt; My Linux SSH client had &lt;code&gt;StrictHostKeyChecking=yes&lt;/code&gt; enforced, refusing to connect to any host whose fingerprint wasn't already recorded in &lt;code&gt;~/.ssh/known_hosts&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; Override the check to automatically accept new host keys on initial handshake while keeping strict verification for future logins:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;accept-new &amp;lt;username&amp;gt;@&amp;lt;mac_tailscale_ip&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Hardening SSH: Why MAC Filtering Doesn't Work &amp;amp; Enforcing Key-Only Auth
&lt;/h2&gt;

&lt;p&gt;At first, I considered adding &lt;strong&gt;MAC address filtering&lt;/strong&gt; on my firewall for an extra layer of security. However, I quickly realized why that wouldn't work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MAC addresses operate at Layer 2 (Data Link Layer):&lt;/strong&gt; They do not survive router hops across the internet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tailscale is a Layer 3 (IP) tunnel:&lt;/strong&gt; Its virtual &lt;code&gt;utun&lt;/code&gt; interface encapsulates raw IP packets without Ethernet frames—meaning MAC addresses don't even exist inside a Tailscale tunnel.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, the gold standard is &lt;strong&gt;Passwordless SSH (Ed25519 public-key cryptography)&lt;/strong&gt; combined with completely disabling password logins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Roadblock #3: Disabling &lt;code&gt;PasswordAuthentication&lt;/code&gt; Isn't Enough on macOS
&lt;/h3&gt;

&lt;p&gt;On standard Linux distros, setting &lt;code&gt;PasswordAuthentication no&lt;/code&gt; in &lt;code&gt;sshd_config&lt;/code&gt; usually suffices. On macOS, however, OpenSSH falls back to PAM via interactive keyboard prompts (&lt;code&gt;KbdInteractiveAuthentication&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Fix:&lt;/strong&gt; After copying my Linux public key over (&lt;code&gt;ssh-copy-id&lt;/code&gt;), I created a drop-in configuration file at &lt;code&gt;/etc/ssh/sshd_config.d/01-disable-passwords.conf&lt;/code&gt; (which survives macOS system updates):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ssh"&gt;&lt;code&gt;&lt;span class="k"&gt;PubkeyAuthentication&lt;/span&gt; &lt;span class="no"&gt;yes&lt;/span&gt;
&lt;span class="k"&gt;PasswordAuthentication&lt;/span&gt; &lt;span class="no"&gt;no&lt;/span&gt;
&lt;span class="k"&gt;KbdInteractiveAuthentication&lt;/span&gt; &lt;span class="no"&gt;no&lt;/span&gt;
&lt;span class="k"&gt;ChallengeResponseAuthentication&lt;/span&gt; &lt;span class="no"&gt;no&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I validated and reloaded the macOS SSH daemon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sshd &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;launchctl stop com.openssh.sshd
&lt;span class="nb"&gt;sudo &lt;/span&gt;launchctl start com.openssh.sshd

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Principle of Least Privilege: Isolating a Standard &lt;code&gt;user&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Logging in repeatedly using my Mac's primary administrator account felt risky. If an automated script or exposed service were ever compromised, the attacker would have immediate &lt;code&gt;sudo&lt;/code&gt; access. I wanted a dedicated, non-admin user (&lt;code&gt;serveruser&lt;/code&gt;) exclusively for SSH.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Standard User via Terminal (&lt;code&gt;dscl&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Rather than clicking through System Settings, you can provision a standard user (Group &lt;code&gt;20&lt;/code&gt; / &lt;code&gt;staff&lt;/code&gt;) directly from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dscl &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-create&lt;/span&gt; /Users/serveruser
&lt;span class="nb"&gt;sudo &lt;/span&gt;dscl &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-create&lt;/span&gt; /Users/serveruser UserShell /bin/zsh
&lt;span class="nb"&gt;sudo &lt;/span&gt;dscl &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-create&lt;/span&gt; /Users/serveruser RealName &lt;span class="s2"&gt;"Server Service Account"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dscl &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-create&lt;/span&gt; /Users/serveruser UniqueID 502
&lt;span class="nb"&gt;sudo &lt;/span&gt;dscl &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-create&lt;/span&gt; /Users/serveruser PrimaryGroupID 20
&lt;span class="nb"&gt;sudo &lt;/span&gt;dscl &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-create&lt;/span&gt; /Users/serveruser NFSHomeDirectory /Users/serveruser
&lt;span class="nb"&gt;sudo &lt;/span&gt;dscl &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-passwd&lt;/span&gt; /Users/serveruser
&lt;span class="nb"&gt;sudo &lt;/span&gt;createhomedir &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; serveruser

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Roadblock #4: &lt;code&gt;No such key: GroupMembership&lt;/code&gt; When Restricting SSH Access
&lt;/h3&gt;

&lt;p&gt;To lock down SSH so &lt;em&gt;only&lt;/em&gt; &lt;code&gt;serveruser&lt;/code&gt; could log in (and block my admin account), I tried reading the macOS SSH access group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dscl &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-read&lt;/span&gt; /Groups/com.apple.access_ssh GroupMembership

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which threw the error: &lt;code&gt;No such key: GroupMembership&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Cause:&lt;/strong&gt; When Remote Login is enabled globally, macOS doesn't initialize the &lt;code&gt;GroupMembership&lt;/code&gt; key until individual users are explicitly added via &lt;code&gt;dseditgroup&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; Using &lt;code&gt;dseditgroup&lt;/code&gt; automatically initializes the key and adds &lt;code&gt;serveruser&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dseditgroup &lt;span class="nt"&gt;-o&lt;/span&gt; edit &lt;span class="nt"&gt;-a&lt;/span&gt; serveruser &lt;span class="nt"&gt;-t&lt;/span&gt; user com.apple.access_ssh
&lt;span class="nb"&gt;sudo &lt;/span&gt;dseditgroup &lt;span class="nt"&gt;-o&lt;/span&gt; edit &lt;span class="nt"&gt;-d&lt;/span&gt; &amp;lt;admin_user&amp;gt; &lt;span class="nt"&gt;-t&lt;/span&gt; user com.apple.access_ssh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For defense-in-depth, I also created &lt;code&gt;/etc/ssh/sshd_config.d/02-restrict-users.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ssh"&gt;&lt;code&gt;&lt;span class="k"&gt;AllowUsers&lt;/span&gt; serveruser

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Granting Scoped &lt;code&gt;sudo&lt;/code&gt; Privileges
&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;serveruser&lt;/code&gt; shouldn't have full root privileges, it occasionally needs to reload firewall rules or adjust power settings. By creating &lt;code&gt;/etc/sudoers.d/serveruser&lt;/code&gt; via &lt;code&gt;sudo visudo -f /etc/sudoers.d/serveruser&lt;/code&gt;, I granted scoped, passwordless execution to specific binary paths only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;serveruser&lt;/span&gt; &lt;span class="n"&gt;ALL&lt;/span&gt;=(&lt;span class="n"&gt;root&lt;/span&gt;) &lt;span class="n"&gt;NOPASSWD&lt;/span&gt;: /&lt;span class="n"&gt;sbin&lt;/span&gt;/&lt;span class="n"&gt;pfctl&lt;/span&gt; -&lt;span class="n"&gt;f&lt;/span&gt; /&lt;span class="n"&gt;etc&lt;/span&gt;/&lt;span class="n"&gt;pf&lt;/span&gt;.&lt;span class="n"&gt;conf&lt;/span&gt;, /&lt;span class="n"&gt;sbin&lt;/span&gt;/&lt;span class="n"&gt;pfctl&lt;/span&gt; -&lt;span class="n"&gt;e&lt;/span&gt;, /&lt;span class="n"&gt;sbin&lt;/span&gt;/&lt;span class="n"&gt;pfctl&lt;/span&gt; -&lt;span class="n"&gt;d&lt;/span&gt;
&lt;span class="n"&gt;serveruser&lt;/span&gt; &lt;span class="n"&gt;ALL&lt;/span&gt;=(&lt;span class="n"&gt;root&lt;/span&gt;) &lt;span class="n"&gt;NOPASSWD&lt;/span&gt;: /&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;pmset&lt;/span&gt; -&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;disablesleep&lt;/span&gt; *

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Locking Down Ports with the macOS Packet Filter (&lt;code&gt;pf&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;The built-in macOS Application Firewall in System Settings only blocks or allows applications globally—it cannot filter by source IP or port. To restrict incoming connections at the network packet level, macOS includes BSD's &lt;strong&gt;Packet Filter (&lt;code&gt;pf&lt;/code&gt;)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because my Linux laptop's Tailscale IP (&lt;code&gt;100.x.y.z&lt;/code&gt;) never changes, I created a custom anchor at &lt;code&gt;/etc/pf.anchors/custom.rules&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pass quick on lo0 all
pass out all keep state

# Allow SSH (and future custom app ports) strictly from my Linux Tailscale IP
pass in quick proto tcp from &amp;lt;linux_tailscale_ip&amp;gt; to any port 22 keep state

# Block all other incoming SSH attempts
block in quick proto tcp from any to any port 22

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After referencing &lt;code&gt;custom.rules&lt;/code&gt; inside &lt;code&gt;/etc/pf.conf&lt;/code&gt;, I enabled it with &lt;code&gt;sudo pfctl -ef /etc/pf.conf&lt;/code&gt;. If I ever spin up a web app on port &lt;code&gt;8080&lt;/code&gt;, Tailscale allows all ports between my devices by default—I simply add port &lt;code&gt;8080&lt;/code&gt; to my &lt;code&gt;pf&lt;/code&gt; anchor (or manage it centrally via Tailscale ACLs).&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Preventing Sleep Mode from Dropping Connections
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Roadblock #5: macOS Drops SSH When the Screen Sleeps or the Lid Closes
&lt;/h3&gt;

&lt;p&gt;Even with everything configured, my SSH session disconnected after a few minutes whenever the Mac went idle or I closed the lid.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Cause:&lt;/strong&gt; macOS aggressively powers down network interfaces during idle sleep, and closing the lid triggers immediate system suspension unless an external display is attached.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; Use &lt;code&gt;pmset&lt;/code&gt; to disable system sleep on AC power while allowing the display backlight to turn off after 5 minutes:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Never sleep the system when plugged into AC power&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;pmset &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;0

&lt;span class="c"&gt;# Turn off the screen after 5 minutes to save energy&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;pmset &lt;span class="nt"&gt;-c&lt;/span&gt; displaysleep 5

&lt;span class="c"&gt;# Keep network connections alive&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;pmset &lt;span class="nt"&gt;-c&lt;/span&gt; tcpkeepalive 1
&lt;span class="nb"&gt;sudo &lt;/span&gt;pmset &lt;span class="nt"&gt;-c&lt;/span&gt; womp 1

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you want to close the lid completely without attaching a dummy HDMI plug, override the lid switch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;pmset &lt;span class="nt"&gt;-a&lt;/span&gt; disablesleep 1

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. The Final Boss: Running Docker Remotely Over SSH
&lt;/h2&gt;

&lt;p&gt;Unlike Linux—where &lt;code&gt;dockerd&lt;/code&gt; runs natively as a root &lt;code&gt;systemd&lt;/code&gt; service—Docker on macOS runs inside a lightweight Linux VM managed by &lt;strong&gt;Docker Desktop&lt;/strong&gt;, a graphical application. This created two major headaches over SSH.&lt;/p&gt;

&lt;h3&gt;
  
  
  Roadblock #6: &lt;code&gt;open -a Docker&lt;/code&gt; Fails with &lt;code&gt;Domain does not support specified action&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When I tried launching Docker Desktop from my remote SSH terminal using &lt;code&gt;open -a Docker&lt;/code&gt;, macOS threw a LaunchServices error.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Cause:&lt;/strong&gt; SSH sessions live in a headless background bootstrap domain, whereas &lt;code&gt;open&lt;/code&gt; requires an active graphical WindowServer (Aqua) session.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; Route the launch command into the active GUI user's bootstrap context using their UID (typically &lt;code&gt;501&lt;/code&gt;), or launch the binary directly in unattended mode:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find the active GUI user's UID&lt;/span&gt;
&lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"%u"&lt;/span&gt; /dev/console

&lt;span class="c"&gt;# Execute inside the GUI user's session&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;launchctl asuser 501 open &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"Docker"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Roadblock #7: Docker Desktop is Running, But &lt;code&gt;docker info&lt;/code&gt; Over SSH Says It Isn't
&lt;/h3&gt;

&lt;p&gt;Even after starting Docker Desktop manually on the Mac, running &lt;code&gt;docker info&lt;/code&gt; or &lt;code&gt;docker ps&lt;/code&gt; over SSH as &lt;code&gt;serveruser&lt;/code&gt; returned: &lt;code&gt;Cannot connect to the Docker daemon... Is the docker daemon running?&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Cause:&lt;/strong&gt; Modern Docker Desktop creates its UNIX socket inside the home directory of the GUI user who launched it (&lt;code&gt;/Users/&amp;lt;admin_user&amp;gt;/.docker/run/docker.sock&lt;/code&gt;) rather than exclusively using &lt;code&gt;/var/run/docker.sock&lt;/code&gt;. When &lt;code&gt;serveruser&lt;/code&gt; runs &lt;code&gt;docker info&lt;/code&gt;, the CLI looks in &lt;code&gt;/Users/serveruser/.docker/run/docker.sock&lt;/code&gt;, finds nothing, and fails.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; Locate the active socket, grant read/write permissions so &lt;code&gt;serveruser&lt;/code&gt; can communicate with it, and export &lt;code&gt;DOCKER_HOST&lt;/code&gt; in &lt;code&gt;serveruser&lt;/code&gt;'s &lt;code&gt;~/.zshrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Grant access to the socket path (run as admin)&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;755 /Users/&amp;lt;admin_user&amp;gt;/.docker /Users/&amp;lt;admin_user&amp;gt;/.docker/run
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;666 /Users/&amp;lt;admin_user&amp;gt;/.docker/run/docker.sock

&lt;span class="c"&gt;# 2. Point serveruser's Docker CLI to the active socket&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export DOCKER_HOST="unix:///Users/&amp;lt;admin_user&amp;gt;/.docker/run/docker.sock"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.zshrc
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;What started as a simple attempt to SSH into an old MacBook turned into a deep dive into macOS internals—covering &lt;code&gt;launchd&lt;/code&gt; bootstrap domains, BSD &lt;code&gt;pf&lt;/code&gt; firewalls, &lt;code&gt;dscl&lt;/code&gt; directory services, &lt;code&gt;pmset&lt;/code&gt; power assertions, and UNIX domain sockets.&lt;/p&gt;

&lt;p&gt;Today, my old MacBook sits quietly on a shelf: screen off, unreachable from the public internet, locked down to a single non-admin user via Ed25519 keys, and running Docker containers smoothly whenever I connect over Tailscale from my Linux laptop.&lt;/p&gt;

</description>
      <category>selfhosted</category>
      <category>linux</category>
      <category>tailscale</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
