Skip to main content

Command Palette

Search for a command to run...

Remote Laravel Development with Tailscale, Herd & Mac Studio - The Complete Guide

Updated
10 min read

A few days ago, I came across a post from Scott Tolinski on X about his remote development setup. He'd put together a full video walkthrough showing how he works on his home machine from anywhere. It was slick, but it was built around a JavaScript/Node workflow. As a Laravel developer, I immediately thought: I want this, but for my stack.

That video was the push I needed. Because here's the thing, every time I go on vacation, I tell myself I won't need to touch code. And every time, something comes up, a client finds a bug, a deployment needs a quick fix, or I just get an idea I want to prototype before I forget it.

My setup at home is a Mac Studio. It's my workhorse, all my Laravel projects, databases, Redis, everything runs on it through Laravel Herd. When I travel, I carry a MacBook Air. It's light, the battery lasts forever, and it's perfect for everything except serious development.

For years, my "remote workflow" was embarrassingly manual. Before every trip, I'd spend an hour backing up databases, pushing half-finished branches to Git, cloning repos on the MacBook Air, importing databases, configuring .env files, installing dependencies... only to realize I forgot to export that one Minio bucket, or that the MacBook Air is running a different PHP version, or that some queue worker behaves differently because the local data is stale.

And then when I got back home, I'd have to merge everything back, re-import any database changes, and hope nothing got out of sync. It was painful. I knew there had to be a better way.

Turns out, there is. What if my MacBook Air could just... use the Mac Studio directly? Same projects, same databases, same everything, as if I were sitting at my desk at home, but from a hotel room in another city?

That's exactly what I built. Here's the full guide.


The Goal

I wanted to be able to:

  • Access my Laravel .test domains (like example.test, laravel.test) over both HTTP and HTTPS from my MacBook Air, anywhere in the world

  • Edit code remotely using Zed editor's SSH feature

  • Run terminal commands on my Mac Studio via SSH

  • Access my home LAN devices (like Home Assistant) through the Mac Studio

All without exposing anything to the public internet. No port forwarding, no dynamic DNS, no VPS middleman.

What You'll Need

  • Mac Studio (or any always-on Mac) at home running Laravel Herd

  • MacBook Air (or any portable Mac) as your travel machine

  • Tailscale installed on both machines (free tier works)


Step 1: Set Up Tailscale on Both Machines

Tailscale creates a private WireGuard-based mesh network between your devices. It works through NATs and firewalls without any port forwarding, which is exactly what makes this whole setup possible.

Install Tailscale on both your Mac Studio and MacBook Air. Once connected, each machine gets a stable IP on the 100.x.x.x range.

In my case:

  • Mac Studio: 100.64.1.10

  • MacBook Air: 100.64.1.20

Configure Mac Studio as an Exit Node

On the Mac Studio, enable it as an exit node with local network access. This is done through the Tailscale menu bar app or via CLI:

tailscale up --advertise-exit-node --advertise-routes=192.168.1.0/24

Replace 192.168.1.0/24 with your home LAN subnet. This enables subnet routing so your MacBook Air can reach local LAN devices (like a NAS, Home Assistant, or a printer) through the Mac Studio.

Don't forget to approve the exit node and subnet routes in the Tailscale admin console.


Step 2: Configure Laravel Herd to Accept Remote Connections

By default, Herd's Nginx only listens on 127.0.0.1, which means it rejects connections from any IP other than localhost, including your Tailscale IP. We need to change this.

Update Nginx to Listen on All Interfaces

Herd stores its Nginx site configs here:

~/Library/Application Support/Herd/config/valet/Nginx/

Each .test domain has its own config file. You need to change the listen directives from 127.0.0.1 to 0.0.0.0 for both port 80 (HTTP) and port 443 (HTTPS).

You can do this in bulk with a single command on the Mac Studio:

# Update HTTP (port 80)
sed -i '' 's/listen 127.0.0.1:80/listen 0.0.0.0:80/g' ~/Library/Application\ Support/Herd/config/valet/Nginx/*

# Update HTTPS (port 443)
sed -i '' 's/listen 127.0.0.1:443 ssl/listen 0.0.0.0:443 ssl/g' ~/Library/Application\ Support/Herd/config/valet/Nginx/*

# Restart Nginx
herd restart nginx

Heads up: Herd may regenerate these configs when you add or remove sites, or when Herd itself updates. I keep this as a shell script so I can re-run it in seconds (more on that later).


Step 3: Configure DNS on MacBook Air

Your MacBook Air needs to know that .test domains should resolve to the Mac Studio's Tailscale IP. The browser doesn't know that example.test lives on 100.64.1.10 — we need to tell it. We'll use dnsmasq for this.

Install and Configure dnsmasq

brew install dnsmasq

Edit the dnsmasq config:

nano /opt/homebrew/etc/dnsmasq.conf

Add this line (replace with your Mac Studio's Tailscale IP):

address=/.test/100.64.1.10

Note the dot before test , address=/.test/... matches all subdomains under .test. Without the leading dot, it won't catch everything correctly.

Start dnsmasq:

sudo brew services start dnsmasq

Point macOS to Use dnsmasq for .test Domains

Create a resolver file:

sudo mkdir -p /etc/resolver
sudo bash -c 'echo "nameserver 127.0.0.1" > /etc/resolver/test'

Verify it works:

ping example.test
# Should resolve to 100.64.1.10

Step 4: Enable SSH on Mac Studio

Go to System Settings → General → Sharing → Remote Login and turn it on. This lets you SSH into the Mac Studio using its Tailscale IP.

Test from MacBook Air:

ssh yourusername@100.64.1.10

I'd also recommend setting up SSH key authentication so you don't have to type your password every time. It also makes Zed's remote editing much smoother.


Step 5: Set Up Zed Editor for Remote Development

This is where the magic really happens. Zed has built-in SSH remote editing support, it feels almost indistinguishable from editing local files.

On your MacBook Air:

  1. Open the command palette (Cmd+Shift+P)

  2. Search for "Connect via SSH"

  3. Enter your Mac Studio's Tailscale IP and the path to your project

Zed opens the remote folder with full LSP support (so you get autocompletion, go-to-definition, error highlighting, all powered by the Mac Studio's PHP/Node/etc.). You also get an integrated terminal that runs commands directly on the Mac Studio.

I was honestly surprised at how good this feels. There's a tiny bit of latency on autocomplete, but it's barely noticeable over Tailscale's WireGuard connection. It's miles better than working off a stale local clone.


Step 6: Fix HTTPS — The Tricky Part

At this point, http://example.test works beautifully from the MacBook Air. But https://example.test? Nope. The connection just hangs or throws an SSL error.

This took me a while to figure out. There are actually two separate problems, and you need to fix both.

Problem 1: Tailscale Serve Hijacking Port 443

If you've ever used tailscale serve on your Mac Studio (even once, even experimentally), it may still be listening on port 443. This means Tailscale intercepts all HTTPS traffic before Nginx ever sees it.

I spent way too long staring at Nginx configs before I thought to check this. Here's how to diagnose:

sudo lsof -iTCP:443 -sTCP:LISTEN -nP

If you see io.tailsc instead of nginx, that's your problem. Tailscale is eating your HTTPS traffic.

Fix it:

tailscale serve off

Don't worry, this is completely safe. It only disables the HTTPS proxy feature. Your Tailscale VPN tunnel, exit node, subnet routes, and SSH all continue working perfectly since they use WireGuard over UDP, not TCP port 443.

Now restart Nginx so it can claim the port:

herd restart nginx

Verify Nginx is now on 443:

sudo lsof -iTCP:443 -sTCP:LISTEN -nP
# Should show nginx-arm, not io.tailsc

Problem 2: Untrusted SSL Certificate on MacBook Air

Herd uses its own Certificate Authority (CA) to generate SSL certificates for .test domains. This CA is automatically trusted on the Mac Studio (where Herd installed it into the Keychain), but your MacBook Air has no idea it exists.

So when you access https://example.test from the MacBook Air, the browser sees a certificate signed by an unknown CA and rightfully rejects it.

The fix is simple: copy the CA certificate to the MacBook Air and trust it.

Copy the CA certificate:

# Run this on your MacBook Air
scp yourusername@100.64.1.10:"~/Library/Application Support/Herd/config/valet/CA/LaravelValetCASelfSigned.pem" ~/Desktop/

Trust it in Keychain Access:

  1. Double-click LaravelValetCASelfSigned.pem - it opens Keychain Access

  2. When prompted, select the System keychain (important - not login, not iCloud)

  3. Find "Laravel Valet CA Self Signed CN" in the certificate list

  4. Double-click it → expand Trust → set "When using this certificate" to Always Trust

  5. Close and enter your password to confirm

Now test:

curl https://example.test
# Should return your site's HTML without any SSL errors

Open https://example.test in your browser, you should see the padlock with a valid certificate. That moment felt incredibly satisfying after all the debugging.


The Final Result

With everything configured, here's what my workflow looks like now:

I open my MacBook Air in a hotel, a café, or my in-laws' living room. Tailscale connects automatically within seconds. I open https://example.test in the browser and my Laravel app loads, with valid HTTPS, hitting the real database, with all my test data intact. I open Zed, connect via SSH, and I'm editing the actual project files on the Mac Studio. The integrated terminal runs artisan commands, queue workers, migrations, everything, directly on the Mac Studio.

No more cloning repos. No more exporting databases. No more "works on my machine" discrepancies between two laptops. It's the same machine, the same environment, the same data. I just happen to be 500 kilometers away from it.


Quick Reference: Commands to Re-Run After Herd Updates

Herd sometimes resets Nginx configs when you add/remove sites or update the app. Here's a handy script to fix everything in one go:

#!/bin/bash
# fix-herd-remote.sh — Run on Mac Studio after Herd updates

# Allow remote connections on HTTP and HTTPS
sed -i '' 's/listen 127.0.0.1:80/listen 0.0.0.0:80/g' ~/Library/Application\ Support/Herd/config/valet/Nginx/*
sed -i '' 's/listen 127.0.0.1:443 ssl/listen 0.0.0.0:443 ssl/g' ~/Library/Application\ Support/Herd/config/valet/Nginx/*

# Restart Nginx
herd restart nginx

echo "Done! Remote access restored."

Save it somewhere handy and chmod +x fix-herd-remote.sh. I keep mine in ~/scripts/ and run it after every Herd update.


Troubleshooting

Can't connect to port 80 or 443? Check what's listening: sudo lsof -iTCP:80 -sTCP:LISTEN -nP and sudo lsof -iTCP:443 -sTCP:LISTEN -nP. You want to see nginx, not io.tailsc or nothing at all.

SSL handshake fails with "internal error" and no certificate? Tailscale Serve is almost certainly intercepting port 443. Run tailscale serve status to confirm, then tailscale serve off to fix it.

Certificate shows as untrusted on MacBook Air? Make sure you imported the Herd CA into the System keychain (not login or iCloud) and set it to Always Trust. Some browsers need a restart to pick up new CA certificates.

DNS not resolving .test domains on MacBook Air? Check that dnsmasq is running (brew services list), the resolver file exists (cat /etc/resolver/test), and the dnsmasq config has the correct syntax: address=/.test/100.64.1.10 (note the dot before test).

Herd reset my Nginx configs? Re-run the fix-herd-remote.sh script above. Takes two seconds.


Wrapping Up

This setup has completely changed how I think about travel and work. I no longer dread being away from my desk. The MacBook Air has gone from "I guess I can do basic stuff on it" to a full portal into my development environment. The M-series chips on the Mac Studio handle all the heavy lifting, and I get to enjoy the portability of the Air without any compromises.

If you're a Laravel developer with a desktop Mac at home and a laptop for travel, I can't recommend this enough. The initial setup takes about 30 minutes, and it saves hours of painful sync work on every single trip.

Happy coding from wherever you are! ❤️