Self-hosted service
appleboy/scp-action avatar
appleboy/scp-action

scp-action is a Docker action that tars your files and ships them over SSH

GitHub Action that copy files and artifacts via SSH.

1,579 stars180 forksShellMIT

At a glance

What is it?
appleboy/scp-action copies files and build artifacts to one or many remote servers from a GitHub Actions workflow, with key or password authentication, jump host support and incremental transfer. It runs only as a Linux container action, and its configuration table is honest about which options are security risks.
Who is it for?
Adopt scp-action when a workflow needs to push a build artifact to a server you control over SSH, and configure it with `key` or `key_path` plus `fingerprint` rather than a password. Skip it on self-hosted Windows runners, which it does not support, and reconsider `curl_insecure`, which the documentation itself marks as not recommended.
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?
Yes. The repository last received commits 75 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

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

Editorial analysis

Linux containers only, and that is stated up front

The first note under the title is the constraint that decides whether you can use this at all: only Linux docker containers are supported.

This is a shell action packaged as a container image, not a composite action running on the host. The repository layout is consistent with that. Alongside `action.yml` and `entrypoint.sh` there are an `images/` directory and a `.goreleaser.yaml`, which is how the image is built and published across architectures.

The consequence is that `runs-on: ubuntu-latest` is not a style preference, it is a requirement. A Windows or macOS runner will not work, and neither will a self-hosted Windows runner even though the target machine might be Windows.

There is a real benefit to paying that cost.

The repository includes a `tests/` directory referenced by the quick start, plus Traditional Chinese and Simplified Chinese versions of the README.

The quick start is nine lines of workflow YAML

The minimal configuration copies files after checkout, with the connection details coming from secrets.

yaml
name: scp files
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Copy files via SSH
        uses: appleboy/scp-action@v1
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          password: ${{ secrets.PASSWORD }}
          port: ${{ secrets.PORT }}
          source: "tests/a.txt,tests/b.txt"
          target: your_server_target_folder_path

Two details are worth noting. `source` takes a comma-separated list rather than a single path or a glob, so you list what you mean. And `port` is passed through even though the configuration table gives 22 as its default, which means a non-standard SSH port is a first-class case rather than something you patch in.

Note that this example authenticates with a password. The security section of the README says to prefer SSH key authentication instead, and the connection table marks the password field as preferring a key for security. The quick start is the shortest path, not the best one.

The action reference is `appleboy/scp-action@v1`, matching the v1.0.0 tag.

Key, key_path and fingerprint are the three that matter

The connection settings table is where the security posture is decided, and three entries do the real work.

`key` takes the private key content directly as a string, which suits a secret stored in GitHub Secrets. `key_path` takes a path to a key file instead, for workflows that write the key to disk first or mount it. `passphrase` covers an encrypted key, since an encrypted key with no passphrase fails in a way that produces an unhelpful error.

`fingerprint` is the entry most workflows skip and most should not. It takes a SHA256 fingerprint for host key verification, which is what stops a man in the middle from quietly receiving your deployment. Without it, the action trusts whatever key the server presents.

Two other defaults are visible and worth understanding before they bite. `protocol` accepts `tcp`, `tcp4` or `tcp6` and defaults to `tcp`, which lets you force IPv4 when a runner's DNS returns an address family the target cannot reach. `timeout` is the SSH connection timeout at 30 seconds, while `command_timeout` is the SCP command timeout at 10 minutes, so a large upload can hit the second one without any connection problem.

`host` accepts several comma-separated hosts, which is what makes multi-server deploys a configuration change rather than a matrix rewrite.

Transfer options reveal that files travel as a tar

The file transfer table names tar explicitly in several rows, which tells you the transfer mechanism even though the action is called scp.

The action packages the selected files into a tar archive, sends it, and extracts it on the destination. Three options control the tar step itself. `tar_dereference` follows symlinks while archiving, `tar_tmp_path` sets where the temporary archive lands on the destination, and `tar_exec` is the path to the tar executable there, defaulting to `tar`.

That last one has a practical consequence: the destination machine needs a tar binary. A minimal container image or a locked-down server may not have one at a path the action expects, and `tar_exec` exists for that case.

Four more options shape what happens on the far side. `rm` removes the target directory before the upload, which is how you get a clean deploy rather than a merge over stale files. `strip_components` removes leading path elements during extraction, so a tar built from a directory can land flat. `overwrite` overwrites existing files, and it is described as being done with tar. `target` must be a directory, and the table advises avoiding root directories for it.

The table also carries a security note column, which is unusual and useful. `source` is marked use explicit paths rather than broad globs, and `rm` is marked use with caution, both because a typo in either turns a deploy into a deletion.

For incremental behaviour, the feature list mentions incremental and differential file transfer, and the README's own scenario guide is organised around a changed-files-only example, which is the case where that matters.

Proxy settings mirror the connection settings, including the unsafe part

Full SSH proxy or jump host support is a separate table of ten entries, and the shape mirrors the direct connection table one to one.

`proxy_host` and `proxy_port`, defaulting again to 22, name the jump host. `proxy_username`, `proxy_password`, `proxy_key`, `proxy_key_path` and `proxy_passphrase` cover the same authentication options as the direct connection, and `proxy_fingerprint` gives you host key verification on the proxy as well. `proxy_timeout` is 30s, matching the direct default.

Two entries exist only for the proxy. `proxy_use_insecure_cipher` enables less secure ciphers for the proxy connection. It is there because old jump hosts in corporate networks genuinely cannot negotiate modern ciphers, which is the honest reason such an option exists in a tool like this, and also the reason it should be a deliberate setting rather than something you discover is on.

The comparison is instructive. The direct connection table has a `protocol` option to force an address family; the proxy table has no equivalent. The direct table carries a `command_timeout`; the proxy table does not. The two tables are similar enough to look interchangeable in a workflow and different enough that copying settings across without checking will leave a gap.

One remaining transfer option belongs in this group rather than the previous one. `curl_insecure` passes `--insecure` to curl, and the table marks it as not recommended. The README does not document when curl is used rather than the tar path, so if you need that option you will be relying on behaviour the documentation does not explain.

v1.0.0 followed a security patch, and version pins the transfer tool

The release history explains the version numbering more than the README does.

v0.1.6 was published on 2023-12-26 and is labelled a security patch. v0.1.7 followed on 2024-01-01. Then nothing for over a year, and v1.0.0 on 2025-04-27. The last push was on 2026-08-09, so work has continued since the 1.0 tag without a new release.

That gap is worth knowing about. If you pin `@v1` you get the 1.0.0 tag and nothing after it, which is the safe choice for a deployment action but means you are not picking up later commits on master. If you pin a commit SHA you get reproducibility at the cost of tracking updates yourself.

There is one more input in the transfer table that changes this calculus: `version`, described as the version of drone-scp to use. The action can select the version of drone-scp used for the transfer. So there are two versions to reason about in a workflow, the action tag and the transfer tool version, and the configuration table makes the second one explicit rather than hiding it.

Two workflow badges exist, one for a stable workflow and one for lint and test, which is a reasonable sign that changes go through checks before a tag moves.

The licence is MIT.

Where scp-action is the wrong tool

Three cases.

If your runner is Windows or macOS, this cannot run. The container requirement is absolute, and no amount of configuration changes it.

If the deployment needs rollback, atomic swaps or anything with ordering across several services, an scp-shaped transfer is the wrong shape. Copying files into a directory that a running server reads is not the same as switching a symlink or draining connections, and the configuration offers `rm` for a wipe but nothing for an atomic replace.

If you are deploying to a platform that expects an artifact rather than SSH access, a provider's own deploy action will be simpler than opening SSH to a build.

On security, the README's own guidance is the checklist: prefer key authentication over passwords, keep host, username, password and key in GitHub Secrets rather than in the workflow file, rotate deployment keys on a schedule it suggests at every 90 days, restrict write permissions on the target directory, and enable fingerprint verification so a redirected connection fails instead of succeeding quietly.

One more constraint is not a flaw but a boundary: the README documents handling Linux and Windows path conversion for the transfer, so the remote side can be Windows, while the runner side cannot.

Editorial conclusion

Adopt scp-action when a workflow needs to push a build artifact to a server you control over SSH, and configure it with `key` or `key_path` plus `fingerprint` rather than a password. Skip it on self-hosted Windows runners, which it does not support, and reconsider `curl_insecure`, which the documentation itself marks as not recommended. Verify first that your `target` is a directory and not a file path, since the configuration table requires a directory and offers `rm` to clear it first.

Frequently asked questions

Which runners can appleboy/scp-action run on?

Only Linux Docker containers, so `runs-on: ubuntu-latest` or an equivalent Linux runner. Windows and macOS runners are not supported, and neither are self-hosted Windows runners.

How do I authenticate to the server with an SSH key?

Use `key` to pass the private key content as a secret, or `key_path` to point at a key file, and add `passphrase` if the key is encrypted. `fingerprint` takes a SHA256 host key fingerprint for verification. The README recommends key authentication over passwords.

What does the strip_components and rm option do?

Files are transferred as a tar archive. `rm` removes the target directory before upload, `strip_components` removes leading path elements when extracting, and `overwrite` overwrites existing files. `tar_exec` sets the path to the tar executable on the destination, defaulting to `tar`.

Official sources

  1. appleboy/scp-action on GitHub
  2. License: MIT
  3. Project website
  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/appleboy-scp-action.svg)](https://hysenlabs.com/projects/appleboy-scp-action)

Was this useful?