Using Another Computer Remotely
Suppose we have a second computer whose operating system, processor, or tools we need. We may want to sit at our usual computer, wake the second computer, send it a project, build the project there, copy the result back, and let the second computer return to sleep. This article builds that workflow from the beginning for Linux, macOS, and Windows.
We will use SSH for the remote terminal and encrypted file transfers. On Windows, we will use MSYS2 as the Unix-like command-line and build environment. Windows' built-in OpenSSH service will accept the connection, then it can launch a build inside MSYS2.
The people and machines involved
The person asking another computer to do something is the operator. The computer at the operator's keyboard initiates an SSH connection, so SSH calls it the client. It is also commonly called the local machine.
The computer being contacted runs an SSH server, so SSH calls it the server. It is also called the remote machine, target machine, or host. “Client” and “server” describe the two machines' roles in this connection; they do not imply that either computer is permanently more important.
operator
|
| uses
v
local machine / SSH client -------- SSH --------> remote machine / SSH server
|
v
a particular user account
SSH access belongs to an account on the remote machine, not to the machine in general. In ssh ada@build-mac.local, ada is a user on the Mac, even if the local computer has a differently named user.
First case: both computers are already on
Begin with both computers awake and connected to the same local network. The remote machine needs an SSH server listening for connections. The local machine only needs an SSH client. No router port forwarding is needed when both machines are on the same local network.
Do not begin with Wake-on-LAN. First make SSH, authentication, file copying, and the build work while we can walk over to the target and see what it is doing. Power management is much easier to diagnose after this foundation works.
Prepare the command line on the local machine
Linux
Most desktop Linux installations already have the OpenSSH client. Check without assuming a particular distribution:
command -v ssh
ssh -V
If it is absent, install the OpenSSH client with the distribution's package manager. Package names are commonly openssh-client or openssh.
macOS
macOS includes an SSH client. Apple's Command Line Tools are not required merely to use SSH, but they provide the compiler and related tools needed for our build example:
ssh -V
xcode-select -p
cc --version
If xcode-select -p says that the tools are missing, request their installer:
xcode-select --install
Windows with MSYS2
Open the MSYS2 UCRT64 terminal. UCRT is Microsoft's current C runtime; the UCRT64 environment is a good general default for native 64-bit Windows software. Update MSYS2, then install the SSH client, rsync, and a C compiler:
pacman -Syu
pacman -S --needed openssh rsync mingw-w64-ucrt-x86_64-gcc
If MSYS2 asks us to close the terminal during a core update, close it, reopen UCRT64, and run the commands again. From this point, commands such as ssh, scp, rsync, and ssh-keygen look much like their Linux equivalents.
Enable the SSH server on the remote machine
Linux target
Check whether the OpenSSH server is installed:
command -v sshd
If it is absent, install the OpenSSH server with the distribution's package manager. The package is commonly named openssh-server or openssh. Distributions using systemd usually name the service either ssh or sshd:
systemctl list-unit-files | grep -E '^(ssh|sshd)\.service'
# Use the name that the preceding command displayed.
sudo systemctl enable --now sshd
sudo systemctl status sshd
On a distribution without systemd, enable sshd with that system's service manager. If a firewall is active, allow TCP port 22 from the local network only. The firewall command differs by distribution, so inspect the active firewall rather than blindly copying a command for a different system.
macOS target
macOS already contains an SSH server. Open System Settings → General → Sharing, enable Remote Login, and choose which users may log in. On older macOS versions, the path is System Preferences → Sharing → Remote Login.
It can also be enabled from a Mac terminal:
sudo systemsetup -setremotelogin on
sudo systemsetup -getremotelogin
Some macOS versions restrict systemsetup unless the terminal has Full Disk Access. The Sharing panel is the simpler fallback.
Windows target
MSYS2 supplies our tools and build environment, but the Windows OpenSSH service is the most direct way to accept a connection before anyone has logged into the desktop. In an Administrator PowerShell window, inspect and install the optional OpenSSH Server feature:
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Start-Service sshd
Set-Service -Name sshd -StartupType Automatic
Installation normally creates and enables the inbound firewall rule named OpenSSH-Server-In-TCP. Check that rule if connections time out. The initial SSH shell is Windows' configured OpenSSH shell, not automatically an MSYS2 shell; we will bridge into MSYS2 for builds later.
Find the remote account and network address
Run the relevant commands at the remote machine. Record both the username and one address on the local network.
Linux
whoami
ip -brief -4 address show
hostname -I is another convenient option when it is available. Ignore loopback address 127.0.0.1; it means “this same machine.” Home-network addresses commonly begin with 192.168., 10., or 172.16. through 172.31..
macOS
whoami
scutil --get LocalHostName
ipconfig getifaddr en0
If en0 has no address, use networksetup -listallhardwareports to find the device name for the active Wi-Fi or Ethernet port. A Mac whose local hostname is Build-Mac may also be reachable as Build-Mac.local through Bonjour.
Windows
From MSYS2, these Windows programs print the Windows account and network configuration:
powershell.exe -NoProfile -Command '[Environment]::UserName'
ipconfig.exe
Use the Windows account name, not the name of the MSYS2 environment. If the address changes later, make a DHCP reservation in the router or use a working local hostname instead of scattering a temporary address through scripts.
Make and verify the first connection
From the local machine, combine the remote username and address:
ssh ada@192.168.1.105
The first connection displays the server's host-key fingerprint. This fingerprint lets us determine whether the machine answering really is our target. Before entering the target account's password, compare it with the fingerprint printed directly on that target.
On a Linux or macOS target:
sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
On a Windows target, from Administrator PowerShell:
ssh-keygen.exe -lf "$env:ProgramData\ssh\ssh_host_ed25519_key.pub"
Compare the complete SHA256: value. After we accept the correct key, SSH records it in the local machine's ~/.ssh/known_hosts. A later “host identification has changed” warning deserves investigation; do not reflexively delete the old entry.
SSH negotiates encryption before asking for the account password. The password is therefore not sent as readable text across the network. Verifying the host key still matters: encryption to an impostor would protect the password from bystanders but not from the impostor itself.
Use a regular remote account and an SSH key
Normally connect to a regular user intended for this work, not Linux root, a macOS administrator acting with elevated privileges, or a Windows administrator. Use sudo or an Administrator terminal only for the individual operations that require it. A dedicated build account is useful when automation should have access to only build files and tools.
Create a key on the local machine:
ssh-keygen -t ed25519
The private file, normally ~/.ssh/id_ed25519, stays on the local machine. The public .pub file is the part installed for the remote account. Give the private key a passphrase unless unattended automation requires a separate, tightly restricted key.
Install the key on Linux or macOS
If ssh-copy-id is available on the local machine:
ssh-copy-id -i ~/.ssh/id_ed25519.pub ada@192.168.1.105
ssh-copy-id makes an ordinary encrypted SSH connection, authenticates once with the remote account's current password, and appends only the public key to that account's ~/.ssh/authorized_keys. It does not send the private key. If ssh-copy-id is unavailable, this portable command performs the essential operation:
cat ~/.ssh/id_ed25519.pub | ssh ada@192.168.1.105 \
'umask 077; mkdir -p ~/.ssh; cat >> ~/.ssh/authorized_keys'
The manual command can add a duplicate if run repeatedly; ssh-copy-id normally checks first.
Install the key on Windows
For an ordinary, non-administrator Windows account, put the complete public-key line in C:\Users\the-user\.ssh\authorized_keys. Creating the file from a PowerShell session logged in as that target user avoids confusing ownership.
Windows OpenSSH treats accounts in the Administrators group differently by default: their keys go in C:\ProgramData\ssh\administrators_authorized_keys, and that file must be writable only by Administrators and SYSTEM:
icacls.exe "C:\ProgramData\ssh\administrators_authorized_keys" /inheritance:r `
/grant "Administrators:F" /grant "SYSTEM:F"
A non-administrator build account is usually simpler and safer. Test a fresh connection before disabling any password-based recovery route.
Give the target a short SSH name
Add an entry to ~/.ssh/config on the local machine:
Host build-mac
HostName 192.168.1.105
User ada
IdentityFile ~/.ssh/id_ed25519
Now all SSH-based tools can use the same name:
ssh build-mac
Run commands and move files
A command after the target name runs on the target instead of opening an interactive shell:
ssh build-mac 'uname -a'
Send a project with scp:
scp -r ./project build-mac:~/remote-build/
Copy a finished artifact back:
mkdir -p ./artifacts
scp build-mac:~/remote-build/project/out/my-program ./artifacts/
For repeated transfers, rsync can avoid retransmitting unchanged data. It must be installed on both ends, and the remote SSH shell must be able to find it. See Sending files between Linux computers for its source/destination syntax and the important meaning of a trailing slash.
Build on the remote computer
If we send source code to a Mac and invoke the Mac's compiler there, that is a remote native build. Doing the same project on several operating systems is often called a multi-platform build. It is not cross-compilation in the strict sense. Cross-compilation means that a compiler running on one platform directly produces code for a different target platform.
On a Linux or macOS target, a project can expose one build script:
ssh build-mac 'cd ~/remote-build/project && ./build.sh'
On the Mac, verify xcode-select -p and cc --version locally once before relying on an unattended build.
Run a build inside MSYS2 on a Windows target
An SSH session accepted by Windows does not automatically receive the environment established by the MSYS2 UCRT64 launcher. Create a small Windows command file such as C:\Users\build\run-msys2-build.cmd on the target:
@echo off
C:\msys64\usr\bin\env.exe MSYSTEM=UCRT64 CHERE_INVOKING=1 /usr/bin/bash -lc "cd /c/Users/build/remote-project && ./build.sh"
MSYSTEM=UCRT64 selects that MSYS2 environment. Bash's -l requests a login shell so the environment's startup setup runs, and -c supplies the command. Invoke the wrapper from the local machine:
ssh windows-build 'C:\Users\build\run-msys2-build.cmd'
Keep the wrapper under the build user's control and give it a narrow purpose. This is clearer than making every remote Windows shell pretend to be an interactive MSYS2 terminal.
Second case: the remote computer is asleep or off
SSH cannot connect to a computer that is not running. Wake-on-LAN, abbreviated WoL, is a separate mechanism: a network interface watches for a small “magic packet” containing its hardware address and asks the machine to wake.
Wake-on-LAN is not the same as remotely pressing the power button. Support depends on the firmware, network adapter, driver, connection type, and power state. Ethernet is the most reliable choice. Waking over Wi-Fi is supported only by some combinations of hardware and operating system.
For a repeatable on-demand build machine, prefer sleep, or hibernation on hardware that explicitly supports waking from it. Waking from a complete soft-off state is hardware-dependent. In particular, Windows officially supports ordinary WoL from sleep and explicit hibernation, not universally from Fast Startup's hybrid shutdown or the fully off state. Macs should likewise be treated as wake-from-sleep machines unless that exact model has been tested otherwise.
Record the target's hardware address
A network interface's MAC address identifies that interface on the local network. It is six bytes commonly written as a1:b2:c3:d4:e5:f6. It is not a secret.
On Linux:
ip link show
# If ethtool is installed, this is another useful check:
sudo ethtool -P enp3s0
On macOS:
networksetup -listallhardwareports
On Windows, from MSYS2 or a Windows terminal:
getmac.exe /v
ipconfig.exe /all
Record the address of the interface that will remain connected while the computer sleeps, normally wired Ethernet. Also find the local network's broadcast address. A network such as 192.168.1.0/24 commonly uses 192.168.1.255, but do not assume this if the subnet differs.
Send a Wake-on-LAN packet from any of the three systems
This site includes a small portable C program so the same command can be built on Linux, macOS, or Windows/MSYS2: download send_wake_on_lan.c.
Compile it on Linux or macOS:
cc -std=c11 -O2 send_wake_on_lan.c -o send-wol
Compile it in the MSYS2 UCRT64 terminal:
cc -std=c11 -O2 send_wake_on_lan.c -lws2_32 -o send-wol.exe
Send the packet, supplying the target MAC address and broadcast address:
./send-wol a1:b2:c3:d4:e5:f6 192.168.1.255
From MSYS2, use ./send-wol.exe. Omitting the second argument uses the limited broadcast address 255.255.255.255, but some networks require their directed broadcast address instead. The program sends UDP port 9 by convention; the magic packet itself is normally unauthenticated, so it should be treated as a local-network convenience rather than an authorization system.
Configure Wake-on-LAN on the target
Start in the machine's UEFI or older BIOS settings and enable an option such as Wake on LAN, Power on by PCI-E, or Resume by LAN. The operating system must then leave the adapter armed when it sleeps.
Linux target
If ethtool is installed, inspect the wired interface. Replace enp3s0 with its actual name:
sudo ethtool enp3s0
Under Supports Wake-on, the letter g means the adapter supports waking from a magic packet. Enable it for the current boot:
sudo ethtool -s enp3s0 wol g
A network manager or driver reload may reset that setting. If NetworkManager controls the connection, make it persistent on that connection profile:
nmcli connection show
sudo nmcli connection modify "Wired connection 1" 802-3-ethernet.wake-on-lan magic
sudo nmcli connection up "Wired connection 1"
Replace the profile name exactly. If systemd-networkd manages the interface instead, a .link file can use this shape:
[Match]
MACAddress=a1:b2:c3:d4:e5:f6
[Link]
WakeOnLan=magic
Configure the manager that actually owns the interface; do not layer several persistence mechanisms onto it without understanding which one wins.
Windows target
After enabling the firmware option, open the network adapter in Device Manager. Its Advanced page commonly has Wake on Magic Packet; its Power Management page commonly has Allow this device to wake the computer. Names vary by driver.
Administrator PowerShell can show and arm wake-capable devices:
powercfg /devicequery wake_programmable
powercfg /deviceenableawake "Exact adapter name"
powercfg /devicequery wake_armed
After an unexpected wake, powercfg /lastwake can help identify its source. Test sleep first, then explicit hibernation. Do not assume a normal Windows shutdown will remain remotely wakeable; Fast Startup and the fully off state have additional limitations even when the firmware advertises WoL.
macOS target
In System Settings, enable Wake for network access under Battery or Energy Saver. The exact panel name depends on the macOS version. The equivalent power-management setting can be inspected and enabled from Terminal:
pmset -g custom
sudo pmset -a womp 1
womp means wake on magic packet. For an older MacBook, test while it is connected to power, using Ethernet if possible, and initially leave the lid open. Some Macs support Wake on Wireless; inspect the Wi-Fi report rather than assuming it:
system_profiler SPAirPortDataType | grep "Wake On Wireless"
Apple's remote-wake behavior can also depend on whether the machines share a subnet or a Bonjour sleep proxy is present. This is another reason to establish the workflow on one local network first.
Test waking separately from building
- Confirm SSH works while the target is awake.
- Put the target to sleep manually.
- Send the magic packet from another machine.
- Watch the target directly and confirm that it wakes.
- Wait for SSH to become available.
After key authentication and the host key have already been set up, this loop waits until SSH is ready:
until ssh -o BatchMode=yes -o ConnectTimeout=2 build-mac true; do
sleep 2
done
BatchMode=yes prevents a hidden password or confirmation prompt from making an automation appear to hang. Only after sleep works should we test hibernation or shutdown, and only if the hardware documentation claims to support that state.
An on-demand build sequence
A simple local script can now perform the sequence explicitly:
#!/bin/sh
set -eu
./send-wol a1:b2:c3:d4:e5:f6 192.168.1.255
until ssh -o BatchMode=yes -o ConnectTimeout=2 build-mac true; do
sleep 2
done
scp -r ./project build-mac:~/remote-build/
ssh build-mac 'cd ~/remote-build/project && ./build.sh'
mkdir -p ./artifacts
scp build-mac:~/remote-build/project/out/my-program ./artifacts/
For Windows, replace the remote build command with the MSYS2 wrapper shown earlier. Real build scripts should also use a maximum wait time, preserve build logs, and stop before scheduling sleep or shutdown if copying the artifact fails.
Leave it available for 30 minutes, then power it down
Schedule the final power action only after the artifact has been copied safely. The following commands request a shutdown in 30 minutes.
Linux:
ssh -t linux-build 'sudo shutdown -h +30'
# Cancel before it occurs:
ssh -t linux-build 'sudo shutdown -c'
macOS:
ssh -t build-mac 'sudo shutdown -h +30'
Windows:
ssh windows-build 'shutdown.exe /s /t 1800'
# Cancel before it occurs:
ssh windows-build 'shutdown.exe /a'
These commands really ask for shutdown. That satisfies “turn it off,” but it may also make the next arbitrary network wake impossible. For a machine meant to be used repeatedly on demand, configure its normal power policy to sleep, run the build under the operating system's sleep-inhibition facility, keep it inhibited for another 30 minutes, and then allow normal sleep:
- On macOS,
caffeinate -i sh -c './build.sh; sleep 1800'prevents idle sleep during the build and wait. - On a systemd Linux desktop,
systemd-inhibit --what=sleep sh -c './build.sh; sleep 1800'serves the same purpose. - On Windows, use the machine's sleep power plan together with a narrowly scoped scheduled task or build agent that marks the build active and releases that request after 30 minutes.
After the inhibitor exits, the operating system's configured idle policy decides when to sleep. Exact unattended “sleep now” automation is privileged and OS-specific. Prefer a root-owned system service, macOS launchd job, or Windows scheduled task that can perform only this one action. Do not store an administrator password in a script and do not grant password-free access to an arbitrary shell merely to make power management convenient.
Connecting from outside the local network
Broadcast Wake-on-LAN packets normally stay inside one local network, and exposing SSH or a Wake-on-LAN UDP port directly through a home router increases the attack surface. For remote use, connect to the home network through a VPN or keep a small, always-on machine there as an authenticated relay. That relay can send the local magic packet and provide a route to the build machine.
Do not forward a broadcast WoL port from the public Internet. The magic packet normally proves no identity, and routers deliberately restrict directed broadcasts.
What to check when it fails
- Connection refused while awake: the address is reachable, but the SSH server is not listening or a local firewall rejected it.
- Connection times out while awake: recheck the address, Wi-Fi isolation, subnet, firewall, and whether both machines are actually on the same network.
- Password works but the key does not: recheck the remote account,
authorized_keyslocation, ownership, and permissions. Windows administrator accounts use the special global file by default. - Magic packet sends but nothing wakes: recheck the target interface's MAC address, directed broadcast address, firmware setting, driver setting, power state, Ethernet link lights, and whether the adapter remains powered.
- The target wakes but SSH is initially unavailable: booting and acquiring an address takes time; use the bounded retry loop rather than one immediate SSH attempt.
- The target cannot be woken after shutdown: retest from sleep. The fully off state is not portable Wake-on-LAN behavior.
Further reading
- Allowing SSH access between Linux computers — a closer look at accounts, keys,
ssh-copy-id, and rented Linux servers. - Sending files between Linux computers — repeated file transfers with
rsync. - Microsoft: Get started with OpenSSH Server for Windows.
- Microsoft: OpenSSH Server configuration for Windows.
- Microsoft: Wake-on-LAN behavior.
- Apple: Allow a remote computer to access your Mac.
- Apple: Share your Mac resources when it is in sleep.
- MSYS2 OpenSSH package and MSYS2 launcher documentation.
- Linux
ethtoolmanual, NetworkManager settings, and systemd.link documentation.