Clipboard Sharing to a Remote Dev Environment
I run my development environment in portable and reproducible virtual machine
built with nix. Once the VM is started, accessing all my
projects is just a matter of using ssh
, or mosh depending
on what I’m feeling that day. The biggest, and perhaps only, pain point with my
approach is clipboard sharing between client (my laptop) and server (my VM). For
example, when I’m copying logs from the VM’s tmux session or copying lines in
neovim, I want to have those instantly in the clipboard of my client machine.
There are are a variety of ways to share a clipboard in popular virtual machine applications like Fusion or Parallels. However, these approaches don’t work for me for a few reasons:
- It’s not portable: While I usually run my VM locally, sometimes I’ll do it in a cloud provider.
- My VM does not run a desktop environment, thus there is no concept of a clipboard.
Like many things in software, I’ve gone searching for a solution only to come back to a technology that’s been around since (at least) the 80s. This technology is the named pipe. Effectively I used this persistent pipe/queue as a pseudo clipboard to facilitate sharing and today I’m going to explain how.
This post assumes you understand how a named pipe works. But, if you don’t have experience with named pipes, checking my post on Pipes: Named and Unnamed to dig into how they work.
Attachment
Attaching my client machine to the VM is a matter of ensuring:
- The named pipe exists.
- The client can “connect and listen” to the pipe.
- When data is in the pipe, it reads it, and puts it in the guest clipboard.
Here’s a visual representation of this idea:
In this model, there are 2 SSH connections. One where I’m accessing my work
environment using tmux
and altering files with nvim
. The other listens for
data to be placed in the named piped located at ~/clip
.
For setup in the VM, I first create the named pipe ~/clip
:
mkfifo ~/clip
Next, I need to ensure that any copy request coming out of tmux
or nvim
forwards the data to this named pipe. For tmux
, I add this to
~/.config/tmux.conf
:
set -g mouse on
setw -g mode-keys vi
# map vi keys when in copy-mode for selection and copy (yank)
bind -T copy-mode-vi 'v' send -X begin-selection
bind -T copy-mode-vi 'V' send -X select-line
bind -T copy-mode-vi 'r' send -X rectangle-toggle
# send buffer to ~/clip when copying with 'y'
bind -T copy-mode-vi 'y' send -X copy-pipe-and-cancel "tmux show-buffer > ~/clip"
# send buffer to ~/clip when dragging with the mouse
bind -T copy-mode-vi MouseDragEnd1Pane send -X copy-selection-and-cancel\; run "tmux show-buffer > ~/clip"
The key pieces in the above are the copy-mode-vi
settings that run tmux show-buffer > ~/clip
on specific events.
For nvim
, I setup a dedicated command that will forward selected content to
~/clip
. I intentionally choose not to hook into yank or override any register
behaviors because I want to keep those as is. Here are the relevant parts of my
function written in lua stored in init.vim
:
function SaveSelectionToClipboard()
-- Save the current selection to the ~/clip file
vim.cmd([[ '<,'>w! >> ~/clip ]])
end
-- Create a mapping to call the function in Visual mode
vim.api.nvim_set_keymap('v', '<leader>c', ':lua SaveSelectionToClipboard()<CR>', { noremap = true })
I believe the equivalent in vimscript would look something like:
vnoremap <silent> <your_hotkey> :w! ~/clip<CR>
The last step is to connect the client machine (laptop) to the VM. This should likely be done by using an init system like systemd or homebrew/launchctl to run it as a service that constantly triggers. However, for the sake of a generic example, here’s how you can connect using a simple bash loop:
while true
do ssh -i ~/.ssh/joshrosso.pem 192.168.1.77 'cat ~/clip' | pbcopy
done
In the above, you’ll change 192.168.1.77
to your host’s IP address.
Additionally, if not using MacOS, you’ll swap pbcopy
out for something that
saves piped output to your clipboard, like xclip
. If you want to prevent
password prompting for the ssh
command, you should add this host and identity
file to your ~/.ssh/config
.
Now you’re set to trigger copies on your VM and you should see the contents get forwarding to your client’s clipboard.
Conclusion
Hope you enjoyed this random but interesting use case for sharing clipboards between a guest and host machine!