Self-hosted service
easingthemes/ssh-deploy avatar
easingthemes/ssh-deploy

easingthemes/ssh-deploy: GitHub Actions deployment over rsync and SSH

GitHub Action for deploying code via rsync over ssh. (with NodeJS)

1,342 stars156 forksJavaScriptMIT

At a glance

What is it?
easingthemes/ssh-deploy is a NodeJS GitHub Action that copies a directory from GITHUB_WORKSPACE to a remote server with rsync over SSH. It suits applications that already produce deployable files, but its default SSH behavior and target-based model require careful review before adoption.
Who is it for?
Adopt easingthemes/ssh-deploy when a GitHub workflow has build output ready in GITHUB_WORKSPACE and the target host is prepared for key-based SSH and rsync. Do not adopt it for deployment methods requiring passwords, interactive SSH prompts, application-level releases, or a transfer model beyond file synchronization.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Where ssh-deploy fits in a build workflow

easingthemes/ssh-deploy addresses the final file-transfer stage of a continuous deployment workflow. A checkout action and a build or test action can leave release files in GITHUB_WORKSPACE, commonly in a directory such as dist/. The action then selects one or more source directories from that workspace and synchronizes them with a directory on a server through rsync over SSH.

It is intended for teams whose deployment artifact is a directory tree rather than a platform-specific package. The README does not describe application version selection, database migrations, container publishing, zero-downtime release switching, health checks, or rollback. Those concerns must either remain outside this action or be encoded in SCRIPT_BEFORE and SCRIPT_AFTER.

The direct alternative for many workflows is SSH plus an rsync command in a shell step. easingthemes/ssh-deploy packages connection configuration, private-key handling, rsync invocation, and optional remote scripts into a reusable GitHub Action. That reduces repeated workflow code, but it does not remove the need to understand SSH and rsync. The README explicitly says that most reported problems concern SSH connections, rsync parameters, or permissions rather than action functionality.

The rsync and remote-script data path

The action takes configuration through inputs named as environment-style variables in its documentation. SSH_PRIVATE_KEY supplies the private half of an SSH key pair, while REMOTE_HOST and REMOTE_USER identify the destination. REMOTE_PORT is optional and defaults to 22. SOURCE selects one or more paths relative to the GITHUB_WORKSPACE root, and TARGET identifies the remote destination directory. Its documented default is /home/REMOTE_USER/.

The core transfer is an rsync operation over SSH. ARGS supplies the rsync flags and defaults to -rlgoDzvc -i. This means the action does not impose one universal synchronization policy. A workflow that expects remote files to be removed must add options such as --delete, while a workflow that should preserve unrelated files must not add them casually.

SCRIPT_BEFORE and SCRIPT_AFTER provide hooks on the remote host. The README states that SCRIPT_BEFORE commands are stored in a .sh file and executed through .bash over SSH. Rsync output is exposed to SCRIPT_AFTER through the RSYNC_STDOUT variable. Neither hook fails the job by default. Setting SCRIPT_BEFORE_REQUIRED or SCRIPT_AFTER_REQUIRED to true changes that policy.

SSH_CMD_ARGS defaults to -o StrictHostKeyChecking=no and accepts comma-separated SSH options, each prefixed with -o. This default favors unattended execution but disables an important host identity check. SSH_CMD_ARGS can replace it with stricter settings, although the README does not provide a complete known_hosts setup example for that replacement.

Preparing keys and a first GitHub Actions deployment

The README requires the corresponding public key to be installed in the receiving server's authorized_keys file. It specifies PEM format and warns that the generated private key must have an empty passphrase because the rsync-over-SSH invocation cannot accept a private-key password as a command-line parameter. Generate a 4096-bit RSA key in PEM format with the documented command:

bash
ssh-keygen -m PEM -t rsa -b 4096

For a first workflow, build the application before invoking the action so that the source directory already exists. The following repository example is a complete starting point:

yaml
name: Node CI

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Install Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '16.x'
    - name: Install npm dependencies
      run: npm install

The README's actual step label is "Install npm dependencies". Append the deployment step shown below to that job:

yaml
    - name: Deploy to Server
      uses: easingthemes/ssh-deploy@main
      with:
        SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
        ARGS: "-rlgoDzvc -i --delete"
        SOURCE: "dist/"
        REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
        REMOTE_USER: ${{ secrets.REMOTE_USER }}
        TARGET: ${{ secrets.REMOTE_TARGET }}
        EXCLUDE: "/dist/, /node_modules/"

Before enabling --delete on a recurring deployment, compare the complete contents of SOURCE and TARGET. That flag turns a stale-file difference into remote deletion. The README also documents branch-based use such as ssh-deploy@main and versioned Marketplace use such as [email protected]. The repository currently lists release v6.0.3, but the example workflow does not show a v6 reference.

Controlling preflight and post-transfer behavior

The remote hooks are useful for checks that belong at the transfer boundary. SCRIPT_BEFORE can verify the remote user, inspect a directory, or perform preparation before rsync starts. SCRIPT_AFTER can inspect the destination or consume the captured transfer output. Because both scripts support multiline commands, related commands can remain in one block rather than being split across several workflow steps.

The following pattern comes from the README. The before script lists the active user and directory contents, while the after script does the same and prints the captured rsync output:

yaml
      SCRIPT_BEFORE: |
        whoami
        ls -al
      SCRIPT_AFTER: |
        whoami
        ls -al
        echo $RSYNC_STDOUT

By default, failure of either hook does not necessarily fail the deployment job. That behavior can conceal a failed preparation step or a failed post-transfer command. Set SCRIPT_BEFORE_REQUIRED or SCRIPT_AFTER_REQUIRED to true when the workflow must stop on hook failure. The variables can be used together, but the README does not document their interaction with partial transfers, so preflight validation is preferable for commands that determine whether rsync should proceed.

For multiple deployment targets, the README suggests saving shared environment variables to $GITHUB_ENV. It points to .github/workflows/e2e.yml for an example rather than embedding that pattern in the main documentation. Multiple SOURCE values are space-separated. For more complex layouts, the README recommends rsync's -R option through ARGS to manipulate directory structure.

Exclusions, source layouts, and synchronization risk

SOURCE defaults to an empty string, so users must understand what the action transfers in their workflow. The README describes SOURCE as a path relative to GITHUB_WORKSPACE and permits multiple sources separated by spaces. This makes SOURCE suitable for selecting generated output such as dist/ while leaving build tooling and repository metadata outside the transfer.

EXCLUDE is also easy to misconfigure. It accepts paths separated by commas, with examples written as /dist/ and /node_modules/. The README does not document whether leading or trailing spaces are normalized, nor does it demonstrate how patterns containing rsync wildcard characters behave. Exact exclusions should therefore be checked against the rsync invocation rather than inferred from appearance.

The larger risk lies in ARGS. The default -rlgoDzvc -i does not mirror every deployment policy. Adding --delete removes destination files that are absent from the source, while options such as -R change how source paths are reconstructed at the destination. SSH_CMD_ARGS similarly controls the SSH side. These are direct rsync and SSH behaviors, not higher-level deployment guarantees.

For complex cases, the README directs users to ARGS and SSH_CMD_ARGS rather than relying on the convenience variables. That advice acknowledges that target ownership, file permissions, preserve behavior, compression choices, host-key verification, and remote path structure vary by environment. The action remains appropriate only when the team can state the effective rsync and SSH options and reproduce them manually.

Operational constraints and release maintenance

The repository is not archived, and its last push was on 2026-04-02. The release list includes v6.0.3, v6.0.2, and v6.0.1, with dates on 2026-04-02. These facts establish recent activity at that date, but the README does not document a compatibility matrix for GitHub Actions runner images, Node.js releases, rsync versions, or remote SSH servers.

The package metadata identifies the distributed implementation as dist/index.js. The build script runs linting and uses @vercel/ncc to build src/index.js into dist, which explains why a dist directory is present and packaged. package-lock.json and the test directory are also repository entries. The README mentions an end-to-end test executed on every pull request merge to main and directs readers to the Actions tab for its output, but it does not describe the test matrix or supported operating systems.

The project is licensed under MIT. That identifier permits use and modification under the terms of the MIT License, but operators still need to protect SSH private keys as secrets and assess repository, host, and third-party obligations separately. The README explicitly advises checking keys and deployment paths and using the action at one's own risk.

Compared with a handwritten run: ssh plus rsync workflow step, this action centralizes repeatable configuration and offers remote hooks. A direct shell step exposes every command in the workflow and is easier to inspect, but it also repeats connection setup and secret handling. Users who need SSH agent forwarding, an SSH password, interactive prompts, or a deployment system not based on directory synchronization should treat that boundary as a reason to choose another approach. The README documents neither of those capabilities.

Editorial conclusion

Adopt easingthemes/ssh-deploy when a GitHub workflow has build output ready in GITHUB_WORKSPACE and the target host is prepared for key-based SSH and rsync. Do not adopt it for deployment methods requiring passwords, interactive SSH prompts, application-level releases, or a transfer model beyond file synchronization. Before using it, manually verify the SSH connection, rsync flags, EXCLUDE paths, TARGET directory, required-script behavior, and host-key policy. The first deployment should also confirm whether --delete is appropriate for the exact contents of TARGET.

Frequently asked questions

How do I install and use easingthemes/ssh-deploy in GitHub Actions?

Add `uses: easingthemes/ssh-deploy@main` to a workflow step, provide `SSH_PRIVATE_KEY`, `REMOTE_HOST`, and `REMOTE_USER`, and set `SOURCE` and `TARGET`. The README also documents versioned Marketplace references such as `[email protected]`.

Which rsync options does easingthemes/ssh-deploy use by default?

The default `ARGS` value is `-rlgoDzvc -i`. The action documentation also shows `-rlgoDzvc -i --delete` when the deployment should also remove destination files that are no longer present in the source.

Does easingthemes/ssh-deploy support scripts before and after deployment?

It supports `SCRIPT_BEFORE` and `SCRIPT_AFTER`, with commands executed on the remote host. By default their failure does not necessarily fail the job; `SCRIPT_BEFORE_REQUIRED` and `SCRIPT_AFTER_REQUIRED` can make those steps required.

Official sources

  1. easingthemes/ssh-deploy on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/easingthemes-ssh-deploy.svg)](https://hysenlabs.com/projects/easingthemes-ssh-deploy)

Was this useful?