Skip to content
Latchkey

rsync over SSH (-e ssh) in GitHub Actions

rsync -e ssh tunnels the transfer over SSH so you can deploy to a remote server without a separate rsync daemon.

The most common CI deploy: build artifacts on the runner, then rsync them to a server over SSH using a deploy key. The setup that trips people up is known_hosts.

What it does

When the destination is host:path, rsync runs over a remote shell, SSH by default. -e lets you customize that shell, most often to set a non-standard port or extra SSH options. rsync starts a remote rsync process and pipes data through the SSH connection.

SSH options

FlagWhat it does
-e "ssh ..."Set the remote shell and its options
-e "ssh -p 2222"Connect on a non-standard SSH port
-e "ssh -i KEY"Use a specific private key
-e "ssh -o StrictHostKeyChecking=accept-new"Trust an unknown host key on first connect
--rsync-path=CMDOverride the remote rsync command (e.g. sudo rsync)

Common usage

Terminal
# Default: rsync uses ssh automatically for host:path
rsync -avz dist/ deploy@example.com:/var/www/app/

# Custom port and an explicit key
rsync -avz -e "ssh -p 2222 -i ~/.ssh/deploy_key" dist/ deploy@host:/srv/app/

GitHub Actions deploy step

.github/workflows/deploy.yml
- name: Deploy over SSH
  run: |
    mkdir -p ~/.ssh
    echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/id_ed25519
    chmod 600 ~/.ssh/id_ed25519
    ssh-keyscan -p 22 example.com >> ~/.ssh/known_hosts
    rsync -avz --delete -e "ssh -i ~/.ssh/id_ed25519" \
      dist/ deploy@example.com:/var/www/app/

In CI

A fresh runner has no entry for your server in known_hosts, so the first connection would prompt to confirm the fingerprint and then hang (no TTY). Populate known_hosts with ssh-keyscan host >> ~/.ssh/known_hosts before the rsync step. For better security, pin the expected key rather than trusting whatever keyscan returns.

Common errors in CI

Host key verification failed. rsync error: unexplained error (code 255) means known_hosts has no matching key; run ssh-keyscan first. Permission denied (publickey) means the key is wrong, lacks correct permissions (chmod 600), or is not in the server authorized_keys. "rsync: connection unexpectedly closed (0 bytes received so far)" with "protocol version mismatch -- is your shell clean?" means the remote login prints text (a banner or motd) before rsync runs; quiet the shell startup.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →