Ergonomic Remote Development

November 07, 2023 • 4 minute read • @mitchhanbergAnalytics

I recently built a new PC (you can see the specs on my /uses page!) and installed Linux on it.

The way I have been using it mostly has been through an SSH connection, even though its sitting underneath my desk, plugged into my monitor, and all my peripherals plugged into a nifty USB hub splitter thing!

I simply couldn't live without the macOS desktop environment. I have so much muscle memory for all of the shortcuts and have so many apps that I use that enhance my workflow.

I have never remotely developed for this long before, so I quickly ran into a bunch of papercuts for which I was able to easily find bandaids.

Tailscale

When you are going to be SSHing into a remote computer, you need to know the IP address of the computer.

Tailscale makes this super easy. You install Tailscale on your local computer and on the remote computer, and they both joing your "tailnet".

Now, you have a static IP address that only other computers on your tailnet can access!

$ ssh mitchell@<remote ip>

Welcome to Ubuntu 23.10 (GNU/Linux 6.5.0-10-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

6 updates can be applied immediately.
To see these additional updates run: apt list --upgradable

*** System restart required ***
Last login: Tue Nov  7 07:24:00 2023 from <local ip>

Clipboard

You'll want to make sure that you can still copy/paste to/from your host computer's clipboard (or 'pasteboard' as macOS calls it) from within TUI applications like Vim/Tmux and in shell scripts.

TUI Apps

If you are using a modern terminal emulator (I use Ghostty), you most likely have clipboard working already for TUI applications, as long as they support the appropriate terminal features.

I use Nvim, which does not currently support the OSC-52 terminal feature that enables system clipboard communication, but I use Nvim inside tmux, which does!

And luckily, Nvim has a tmux 'clipboard provider' which is how Nvim actually communicates to the "outside" clipboard.

If you don't use tmux and use Vim/Nvim, there are plenty of plugins that implement it!

Scripting

The way that I have implemented scripting to my local clipboard is to again use SSH!

Since your computers are using Tailscale and are on the same tailnet, this means that your local computer also has a nice and static IP address that you can use.

Here is an example:

$ ssh mitchell@<local ip> pbpaste \
  | some-local-command \
  | ssh mitchell@<local ip> pbcopy

Here, we remotely run the pbpaste command (which, along with pbpaste, are the macOS scripting utilities for using the system clipboard), pipe the ouput into a local command, and then pipe it into ssh, which will send it to the pbcopy command on our local computer.

Now, you can easily wrap that up in an alias or a shell script.

Opening your web browser

If you are used to opening web pages with utilities like the GitHub CLI gh, you'll notice that those don't work anymore. This is because (on Linux), the tool is (most likely) running xdg-open, which is similar to the open command on macOS.

You are running this on the remote computer, which is not logged into a desktop environment, and has no web browser.

Well, we can easily fix this again with SSH!

What I did was write a shell script called xdg-open and put it in my $PATH. This way, tools like gh will actually call my script instead of the built-in xdg-open.

My script looks like this:

#!/usr/bin/env bash

function main() {
	local ip
	local uri

	uri="$1"
	if [[ ! -z "$SSH_CONNECTION" ]]; then
		ip="$(echo "$SSH_CONNECTION" | awk '{ print $1 }')"

		ssh "mitchell@$ip" open "$uri"
	else
		xdg-open "$uri"
	fi
}

main "$@"

We check the $SSH_CONNECTION environment variable to see if we are in an SSH session.

If we are not, we just call xdg-open as usual. This is helpful in case we log onto our PC like a normal person.

If we are, then we parse our local IP address out of the $SSH_CONNECTION variable and run the open command remotely using ssh.

Web Development

Now, if you are a web developer, you typically will be starting a local web server and previewing your site or app in the browser. This gets a little tricky when your browser and web server are not on the same computer!

Luckily, tailscale comes to the rescue again!

With tailscale, you can create a reverse proxy served over https with your existing free plan.

$ tailscale serve https / http://localhost:4000

This will create a reverse proxy that is only available inside your tailnet!

If you want to show off your beautiful website to a client or coworker, you can run tailscale funnel 443 on, which will make your reverse proxy available outside of your tailnet.

Signing Git Commits

I sign my git commits using the 1Password SSH agent. This allows me to use an SSH key (instead of a GPG key) and to authorize the usage of the key with Touch ID on my Mac.

As you can imagine, when I went commit my first code on my new PC, I thought I had found a utter road block that would stop this new workflow from continuing.

Luckily, you can actually forward your SSH agent when you make and SSH connection. This is typically for when you are machine hopping and have to ssh to a remote machine from inside an ssh connection, so that you don't have to install your keys on your local computer and the first remote computer.

And, this also works for 1Password Git Commit signing!

Just add an entry in your ~/.ssh/config for your PC to allow forwarding on your local computer:

Host <remote ip>
  ForwardAgent yes

And on the PC, configure the IdentityAgent to use 1Password. I don't actually remember if this is necessary, but when writing this post, I found this config and I don't remember writing it, so it must be important!

Host *
  IdentityAgent ~/.1password/agent.sock

Conclusion

And there we have it!

Now you should be able to develop remotely and not even be able to tell the difference, except for the sheer speed of your overpowered new PC!


If you want to stay current with what I'm working on and articles I write, join my mailing list!

I seldom send emails, and I will never share your email address with anyone else.