## 1. Project Overview & Quickstart (appleboy/ssh-action)

## File: README.md

# 🚀 SSH for GitHub Actions

English | [繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md)

## Table of Contents

- [🚀 SSH for GitHub Actions](#-ssh-for-github-actions)
  - [Table of Contents](#table-of-contents)
  - [📖 Introduction](#-introduction)
  - [🧩 Core Concepts \& Input Parameters](#-core-concepts--input-parameters)
    - [🔌 Connection Settings](#-connection-settings)
    - [🛠️ SSH Command Settings](#️-ssh-command-settings)
    - [🌐 Proxy Settings](#-proxy-settings)
  - [📤 Output Variables](#-output-variables)
  - [⚡ Quick Start](#-quick-start)
  - [🔑 SSH Key Setup \& OpenSSH Compatibility](#-ssh-key-setup--openssh-compatibility)
    - [Setting Up SSH Keys](#setting-up-ssh-keys)
      - [Generate RSA key](#generate-rsa-key)
      - [Generate ED25519 key](#generate-ed25519-key)
    - [OpenSSH Compatibility](#openssh-compatibility)
  - [🛠️ Usage Scenarios \& Advanced Examples](#️-usage-scenarios--advanced-examples)
    - [Using password authentication](#using-password-authentication)
    - [Using private key authentication](#using-private-key-authentication)
    - [Multiple commands](#multiple-commands)
    - [Run commands from a file](#run-commands-from-a-file)
    - [Multiple hosts](#multiple-hosts)
    - [Multiple hosts with different ports](#multiple-hosts-with-different-ports)
    - [Synchronous execution on multiple hosts](#synchronous-execution-on-multiple-hosts)
    - [Pass environment variables to shell script](#pass-environment-variables-to-shell-script)
    - [Capturing command output](#capturing-command-output)
  - [🌐 Proxy \& Jump Host Usage](#-proxy--jump-host-usage)
  - [🛡️ Security Best Practices](#️-security-best-practices)
    - [Protecting Your Private Key](#protecting-your-private-key)
    - [Host Fingerprint Verification](#host-fingerprint-verification)
  - [🚨 Error Handling \& Troubleshooting](#-error-handling--troubleshooting)
    - [Q\&A](#qa)
      - [Command not found (npm or other command)](#command-not-found-npm-or-other-command)
  - [🤝 Contributing](#-contributing)
  - [📝 License](#-license)

---

## 📖 Introduction

**SSH for GitHub Actions** is a powerful [GitHub Action](https://github.com/features/actions) for executing remote SSH commands easily and securely in your CI/CD workflows.  
Built with [Golang](https://go.dev) and [drone-ssh](https://github.com/appleboy/drone-ssh), it supports a wide range of SSH scenarios, including multi-host, proxy, and advanced authentication.



[](https://github.com/appleboy/ssh-action/actions/workflows/main.yml)
[](https://github.com/appleboy/ssh-action/actions/workflows/trivy-scan.yml)

**Slides:** [SSH for GitHub Actions](https://speakerdeck.com/appleboy/ssh-for-github-actions)

---

## 🧩 Core Concepts & Input Parameters

This action provides flexible SSH command execution with a rich set of configuration options.

For full details, see [action.yml](./action.yml).

### 🔌 Connection Settings

These parameters control how the action connects to your remote host.

| Parameter           | Description                                                       | Default |
| ------------------- | ----------------------------------------------------------------- | ------- |
| host                | SSH host address                                                  |         |
| port                | SSH port number                                                   | 22      |
| username            | SSH username                                                      |         |
| password            | SSH password                                                      |         |
| protocol            | SSH protocol version (`tcp`, `tcp4`, `tcp6`)                      | tcp     |
| sync                | Run synchronously if multiple hosts are specified                 | false   |
| timeout             | Timeout for SSH connection to host                                | 30s     |
| key                 | Content of SSH private key (e.g., raw content of `~/.ssh/id_rsa`) |         |
| key_path            | Path to SSH private key                                           |         |
| passphrase          | Passphrase for the SSH private key                                |         |
| fingerprint         | SHA256 fingerprint of the host public key                         |         |
| use_insecure_cipher | Allow additional (less secure) ciphers                            | false   |
| cipher              | Allowed cipher algorithms. Uses sensible defaults if unspecified  |         |

---

### 🛠️ SSH Command Settings

These parameters control the commands executed on the remote host and related behaviors.

| Parameter       | Description                                                                       | Default |
| --------------- | --------------------------------------------------------------------------------- | ------- |
| script          | Commands to execute remotely                                                      |         |
| script_path     | Path to a file in the repository containing commands to execute remotely          |         |
| envs            | Environment variables to pass to the shell script                                 |         |
| envs_format     | Flexible configuration for environment variable transfer                          |         |
| allenvs         | Pass all environment variables with `GITHUB_` and `INPUT_` prefixes to the script | false   |
| command_timeout | Timeout for SSH command execution                                                 | 10m     |
| debug           | Enable debug mode                                                                 | false   |
| request_pty     | Request a pseudo-terminal from the server                                         | false   |
| curl_insecure   | Allow curl to connect to SSL sites without certificates                           | false   |
| capture_stdout  | Capture standard output from commands as action output                            | false   |
| version         | drone-ssh binary version. If not specified, the latest version will be used.      |         |

---

### 🌐 Proxy Settings

These parameters control the use of a proxy (jump host) for connecting to your target host.

| Parameter                 | Description                                     | Default |
| ------------------------- | ----------------------------------------------- | ------- |
| proxy_host                | SSH proxy host                                  |         |
| proxy_port                | SSH proxy port                                  | 22      |
| proxy_username            | SSH proxy username                              |         |
| proxy_password            | SSH proxy password                              |         |
| proxy_passphrase          | SSH proxy key passphrase                        |         |
| proxy_protocol            | SSH proxy protocol version                      | tcp     |
| proxy_timeout             | Timeout for SSH connection to proxy host        | 30s     |
| proxy_key                 | Content of SSH proxy private key                |         |
| proxy_key_path            | Path to SSH proxy private key                   |         |
| proxy_fingerprint         | SHA256 fingerprint of the proxy host public key |         |
| proxy_cipher              | Allowed cipher algorithms for the proxy         |         |
| proxy_use_insecure_cipher | Allow insecure ciphers for the proxy            | false   |

> **Note:** To mimic the removed `script_stop` option, add `set -e` at the top of your shell script.

---

## 📤 Output Variables

This action provides the following outputs that you can use in subsequent steps:

| Output | Description                                                                |
| ------ | -------------------------------------------------------------------------- |
| stdout | Standard output of the executed commands (requires `capture_stdout: true`) |

---

## ⚡ Quick Start

Run remote SSH commands in your workflow with minimal configuration:

```yaml
name: Remote SSH Command
on: [push]
jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: Execute remote SSH commands using password
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          password: ${{ secrets.PASSWORD }}
          port: ${{ secrets.PORT }}
          script: whoami
```

**Output:**

```sh
======CMD======
whoami
======END======
out: your_username
===============================================
✅ Successfully executed commands to all hosts.
===============================================
```

---

## 🔑 SSH Key Setup & OpenSSH Compatibility

### Setting Up SSH Keys

It is best practice to create SSH keys on your local machine (not on a remote server). Log in with the username specified in GitHub Secrets and generate a key pair:

#### Generate RSA key

```bash
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
```

#### Generate ED25519 key

```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
```

Add the new public key to the authorized keys on your server. [Learn more about authorized keys.](https://www.ssh.com/ssh/authorized_keys/)

```bash
# Add RSA key
cat .ssh/id_rsa.pub | ssh user@host 'cat >> .ssh/authorized_keys'

# Add ED25519 key
cat .ssh/id_ed25519.pub | ssh user@host 'cat >> .ssh/authorized_keys'
```

Copy the private key content and paste it into GitHub Secrets.

```bash
# macOS
pbcopy < ~/.ssh/id_rsa
# Ubuntu
xclip < ~/.ssh/id_rsa
```

> **Tip:** Copy from `-----BEGIN OPENSSH PRIVATE KEY-----` to `-----END OPENSSH PRIVATE KEY-----` (inclusive).

For ED25519:

```bash
# macOS
pbcopy < ~/.ssh/id_ed25519
# Ubuntu
xclip < ~/.ssh/id_ed25519
```

See more: [SSH login without a password](http://www.linuxproblem.org/art_9.html).

> **Note:** Depending on your SSH version, you may also need to:
>
> - Place the public key in `.ssh/authorized_keys2`
> - Set `.ssh` permissions to 700
> - Set `.ssh/authorized_keys2` permissions to 640

### OpenSSH Compatibility

If you see this error:

```bash
ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey]
```

On Ubuntu 20.04+ you may need to explicitly allow the `ssh-rsa` algorithm. Add this to your OpenSSH daemon config (`/etc/ssh/sshd_config` or a drop-in under `/etc/ssh/sshd_config.d/`):

```text
CASignatureAlgorithms +ssh-rsa
```

Alternatively, use ED25519 keys (supported by default):

```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
```

---

## 🛠️ Usage Scenarios & Advanced Examples

This section covers common and advanced usage patterns, including multi-host, proxy, and environment variable passing.

### Using password authentication

```yaml
- name: Execute remote SSH commands using password
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    password: ${{ secrets.PASSWORD }}
    port: ${{ secrets.PORT }}
    script: whoami
```

### Using private key authentication

```yaml
- name: Execute remote SSH commands using SSH key
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    script: whoami
```

### Multiple commands

```yaml
- name: Multiple commands
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    script: |
      whoami
      ls -al
```



### Run commands from a file

```yaml
- name: File commands
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    script_path: scripts/script.sh
```

### Multiple hosts

```diff
  - name: Multiple hosts
    uses: appleboy/ssh-action@v1
    with:
-     host: "foo.com"
+     host: "foo.com,bar.com"
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
      script: |
        whoami
        ls -al
```

Default `port` is `22`.

### Multiple hosts with different ports

```diff
  - name: Multiple hosts
    uses: appleboy/ssh-action@v1
    with:
-     host: "foo.com"
+     host: "foo.com:1234,bar.com:5678"
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      script: |
        whoami
        ls -al
```

### Synchronous execution on multiple hosts

```diff
  - name: Multiple hosts
    uses: appleboy/ssh-action@v1
    with:
      host: "foo.com,bar.com"
+     sync: true
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
      script: |
        whoami
        ls -al
```

### Pass environment variables to shell script

```diff
  - name: Pass environment
    uses: appleboy/ssh-action@v1
+   env:
+     FOO: "BAR"
+     BAR: "FOO"
+     SHA: ${{ github.sha }}
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     envs: FOO,BAR,SHA
      script: |
        echo "I am $FOO"
        echo "I am $BAR"
        echo "sha: $SHA"
```

> _All environment variables in the `env` object must be strings. Using integers or other types may cause unexpected results._

### Capturing command output

You can capture the standard output of remote commands and use it in subsequent steps:

```yaml
- name: Execute and capture output
  id: ssh
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    capture_stdout: true
    script: |
      echo "Hello World"
      hostname

- name: Use captured output
  run: echo "SSH output was ${{ steps.ssh.outputs.stdout }}"
```

---

## 🌐 Proxy & Jump Host Usage

You can connect to remote hosts via a proxy (jump host) for advanced network topologies.

```bash
+--------+       +----------+      +-----------+
| Laptop | <-->  | Jumphost | <--> | FooServer |
+--------+       +----------+      +-----------+
```

Example `~/.ssh/config`:

```text
Host Jumphost
  HostName Jumphost
  User ubuntu
  Port 22
  IdentityFile ~/.ssh/keys/jump_host.pem

Host FooServer
  HostName FooServer
  User ubuntu
  Port 22
  ProxyCommand ssh -q -W %h:%p Jumphost
```

**GitHub Actions YAML:**

```diff
  - name: SSH proxy command
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     proxy_host: ${{ secrets.PROXY_HOST }}
+     proxy_username: ${{ secrets.PROXY_USERNAME }}
+     proxy_key: ${{ secrets.PROXY_KEY }}
+     proxy_port: ${{ secrets.PROXY_PORT }}
      script: |
        mkdir abc/def
        ls -al
```

---

## 🛡️ Security Best Practices

### Protecting Your Private Key

A passphrase encrypts your private key, making it useless to attackers if leaked. Always store your private key securely.

```diff
  - name: SSH key passphrase
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     passphrase: ${{ secrets.PASSPHRASE }}
      script: |
        whoami
        ls -al
```

### Host Fingerprint Verification

Verifying the SSH host fingerprint helps prevent man-in-the-middle attacks. To get your host's fingerprint (replace `ed25519` with your key type and `example.com` with your host):

```sh
ssh example.com ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub | cut -d ' ' -f2
```

Update your config:

```diff
  - name: SSH key passphrase
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     fingerprint: ${{ secrets.FINGERPRINT }}
      script: |
        whoami
        ls -al
```

---

## 🚨 Error Handling & Troubleshooting

### Q&A

#### Command not found (npm or other command)

If you encounter "command not found" errors, see [this issue comment](https://github.com/appleboy/ssh-action/issues/31#issuecomment-1006565847) about interactive vs non-interactive shells.

On many Linux distros, `/etc/bash.bashrc` contains:

```sh
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
```

Comment out this line or use absolute paths for your commands.

---

## 🤝 Contributing

Contributions are welcome! Please submit a pull request to help improve `appleboy/ssh-action`.

---

## 📝 License

This project is licensed under the [MIT License](LICENSE).

---

## File: README.zh-cn.md

# 🚀 用于 GitHub Actions 的 SSH

[English](./README.md) | [繁體中文](./README.zh-tw.md) | 简体中文

## 目录

- [🚀 用于 GitHub Actions 的 SSH](#-用于-github-actions-的-ssh)
  - [目录](#目录)
  - [📖 简介](#-简介)
  - [🧩 核心概念与输入参数](#-核心概念与输入参数)
    - [🔌 连接设置](#-连接设置)
    - [🛠️ 指令设置](#️-指令设置)
    - [🌐 代理设置](#-代理设置)
  - [📤 输出变量](#-输出变量)
  - [⚡ 快速开始](#-快速开始)
  - [🔑 SSH 密钥配置与 OpenSSH 兼容性](#-ssh-密钥配置与-openssh-兼容性)
    - [配置 SSH 密钥](#配置-ssh-密钥)
      - [生成 RSA 密钥](#生成-rsa-密钥)
      - [生成 ED25519 密钥](#生成-ed25519-密钥)
    - [OpenSSH 兼容性](#openssh-兼容性)
  - [🛠️ 用法场景与进阶示例](#️-用法场景与进阶示例)
    - [使用密码认证](#使用密码认证)
    - [使用私钥认证](#使用私钥认证)
    - [多条命令](#多条命令)
    - [从文件执行命令](#从文件执行命令)
    - [多主机](#多主机)
    - [多主机不同端口](#多主机不同端口)
    - [多主机同步执行](#多主机同步执行)
    - [传递环境变量到 shell 脚本](#传递环境变量到-shell-脚本)
    - [捕获命令输出](#捕获命令输出)
  - [🌐 代理与跳板机用法](#-代理与跳板机用法)
  - [🛡️ 安全最佳实践](#️-安全最佳实践)
    - [保护你的私钥](#保护你的私钥)
    - [主机指纹验证](#主机指纹验证)
  - [🚨 错误处理与疑难解答](#-错误处理与疑难解答)
    - [常见问题](#常见问题)
      - [命令未找到（npm 或其他命令）](#命令未找到npm-或其他命令)
  - [🤝 贡献](#-贡献)
  - [📝 许可证](#-许可证)

---

## 📖 简介

**SSH for GitHub Actions** 是一个强大的 [GitHub Action](https://github.com/features/actions)，可让你在 CI/CD 工作流中轻松且安全地执行远程 SSH 命令。  
本项目基于 [Golang](https://go.dev) 和 [drone-ssh](https://github.com/appleboy/drone-ssh) 构建，支持多主机、代理、高级认证等多种 SSH 场景。



[](https://github.com/appleboy/ssh-action/actions/workflows/main.yml)
[](https://github.com/appleboy/ssh-action/actions/workflows/trivy-scan.yml)

**幻灯片：** [SSH for GitHub Actions](https://speakerdeck.com/appleboy/ssh-for-github-actions)

---

## 🧩 核心概念与输入参数

本 Action 提供灵活的 SSH 命令执行能力，并具备丰富的配置选项。

详细参数请参阅 [action.yml](./action.yml)。

### 🔌 连接设置

这些参数用于控制如何连接到远程主机。

| 参数                | 描述                                          | 默认值 |
| ------------------- | --------------------------------------------- | ------ |
| host                | SSH 主机地址                                  |        |
| port                | SSH 端口号                                    | 22     |
| username            | SSH 用户名                                    |        |
| password            | SSH 密码                                      |        |
| protocol            | SSH 协议版本（`tcp`、`tcp4`、`tcp6`）         | tcp    |
| sync                | 指定多个主机时同步执行                        | false  |
| timeout             | SSH 连接主机的超时时间                        | 30s    |
| key                 | SSH 私钥内容（如 `~/.ssh/id_rsa` 的原始内容） |        |
| key_path            | SSH 私钥路径                                  |        |
| passphrase          | SSH 私钥密码短语                              |        |
| fingerprint         | 主机公钥的 SHA256 指纹                        |        |
| use_insecure_cipher | 允许额外（不安全）的加密算法                  | false  |
| cipher              | 允许的加密算法，未指定时使用默认值            |        |

---

### 🛠️ 指令设置

这些参数用于控制在远程主机上执行的命令及相关行为。

| 参数            | 描述                                                  | 默认值 |
| --------------- | ----------------------------------------------------- | ------ |
| script          | 远程执行的命令                                        |        |
| script_path     | 仓库中包含要远程执行命令的文件路径                    |        |
| envs            | 传递给 shell 脚本的环境变量                           |        |
| envs_format     | 环境变量传递的灵活配置                                |        |
| allenvs         | 传递所有带 `GITHUB_` 和 `INPUT_` 前缀的环境变量到脚本 | false  |
| command_timeout | SSH 命令执行超时时间                                  | 10m    |
| debug           | 启用调试模式                                          | false  |
| request_pty     | 向服务器请求伪终端                                    | false  |
| curl_insecure   | 允许 curl 连接无证书的 SSL 站点                       | false  |
| capture_stdout  | 捕获命令的标准输出作为 Action 输出                    | false  |
| version         | drone-ssh 二进制版本，未指定时使用最新版本            |        |

---

### 🌐 代理设置

这些参数用于通过代理（跳板机）连接到目标主机。

| 参数                      | 描述                                      | 默认值 |
| ------------------------- | ----------------------------------------- | ------ |
| proxy_host                | SSH 代理主机                              |        |
| proxy_port                | SSH 代理端口                              | 22     |
| proxy_username            | SSH 代理用户名                            |        |
| proxy_password            | SSH 代理密码                              |        |
| proxy_passphrase          | SSH 代理私钥密码短语                      |        |
| proxy_protocol            | SSH 代理协议版本（`tcp`、`tcp4`、`tcp6`） | tcp    |
| proxy_timeout             | SSH 连接代理主机的超时时间                | 30s    |
| proxy_key                 | SSH 代理私钥内容                          |        |
| proxy_key_path            | SSH 代理私钥路径                          |        |
| proxy_fingerprint         | 代理主机公钥的 SHA256 指纹                |        |
| proxy_cipher              | 代理允许的加密算法                        |        |
| proxy_use_insecure_cipher | 代理允许额外（不安全）的加密算法          | false  |

> **注意：** 如需实现已移除的 `script_stop` 功能，请在 shell 脚本顶部添加 `set -e`。

---

## 📤 输出变量

本 Action 提供以下输出，可在后续步骤中使用：

| 输出   | 描述                                                  |
| ------ | ----------------------------------------------------- |
| stdout | 执行命令的标准输出（需设置 `capture_stdout: true`） |

---

## ⚡ 快速开始

只需简单配置，即可在工作流中执行远程 SSH 命令：

```yaml
name: Remote SSH Command
on: [push]
jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: 执行远程 SSH 命令（密码认证）
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          password: ${{ secrets.PASSWORD }}
          port: ${{ secrets.PORT }}
          script: whoami
```

**输出：**

```sh
======CMD======
whoami
======END======
out: your_username
===============================================
✅ Successfully executed commands to all hosts.
===============================================
```

---

## 🔑 SSH 密钥配置与 OpenSSH 兼容性

### 配置 SSH 密钥

建议在本地机器（而非远程服务器）上创建 SSH 密钥。请使用 GitHub Secrets 中指定的用户名登录并生成密钥对：

#### 生成 RSA 密钥

```bash
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
```

#### 生成 ED25519 密钥

```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
```

将新生成的公钥添加到服务器的 authorized_keys。 [了解更多 authorized_keys](https://www.ssh.com/ssh/authorized_keys/)

```bash
# 添加 RSA 公钥
cat .ssh/id_rsa.pub | ssh user@host 'cat >> .ssh/authorized_keys'

# 添加 ED25519 公钥
cat .ssh/id_ed25519.pub | ssh user@host 'cat >> .ssh/authorized_keys'
```

复制私钥内容并粘贴到 GitHub Secrets。

```bash
# macOS
pbcopy < ~/.ssh/id_rsa
# Ubuntu
xclip < ~/.ssh/id_rsa
```

> **提示：** 复制内容需包含 `-----BEGIN OPENSSH PRIVATE KEY-----` 到 `-----END OPENSSH PRIVATE KEY-----`（含）。

ED25519 同理：

```bash
# macOS
pbcopy < ~/.ssh/id_ed25519
# Ubuntu
xclip < ~/.ssh/id_ed25519
```

更多信息：[SSH 无密码登录](http://www.linuxproblem.org/art_9.html)。

> **注意：** 根据 SSH 版本，可能还需：
>
> - 将公钥放入 `.ssh/authorized_keys2`
> - 设置 `.ssh` 权限为 700
> - 设置 `.ssh/authorized_keys2` 权限为 640

### OpenSSH 兼容性

如果出现如下错误：

```bash
ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey]
```

在 Ubuntu 20.04+，你可能需要显式允许 `ssh-rsa` 算法。请在 OpenSSH 配置文件（`/etc/ssh/sshd_config` 或 `/etc/ssh/sshd_config.d/` 下的 drop-in 文件）中添加：

```text
CASignatureAlgorithms +ssh-rsa
```

或者，直接使用默认支持的 ED25519 密钥：

```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
```

---

## 🛠️ 用法场景与进阶示例

本节涵盖常见与进阶用法，包括多主机、代理、环境变量传递等。

### 使用密码认证

```yaml
- name: 执行远程 SSH 命令（密码认证）
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    password: ${{ secrets.PASSWORD }}
    port: ${{ secrets.PORT }}
    script: whoami
```

### 使用私钥认证

```yaml
- name: 执行远程 SSH 命令（密钥认证）
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    script: whoami
```

### 多条命令

```yaml
- name: 多条命令
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    script: |
      whoami
      ls -al
```



### 从文件执行命令

```yaml
- name: 文件命令
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    script_path: scripts/script.sh
```

### 多主机

```diff
  - name: 多主机
    uses: appleboy/ssh-action@v1
    with:
-     host: "foo.com"
+     host: "foo.com,bar.com"
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
      script: |
        whoami
        ls -al
```

默认 `port` 为 `22`。

### 多主机不同端口

```diff
  - name: 多主机
    uses: appleboy/ssh-action@v1
    with:
-     host: "foo.com"
+     host: "foo.com:1234,bar.com:5678"
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      script: |
        whoami
        ls -al
```

### 多主机同步执行

```diff
  - name: 多主机
    uses: appleboy/ssh-action@v1
    with:
      host: "foo.com,bar.com"
+     sync: true
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
      script: |
        whoami
        ls -al
```

### 传递环境变量到 shell 脚本

```diff
  - name: 传递环境变量
    uses: appleboy/ssh-action@v1
+   env:
+     FOO: "BAR"
+     BAR: "FOO"
+     SHA: ${{ github.sha }}
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     envs: FOO,BAR,SHA
      script: |
        echo "I am $FOO"
        echo "I am $BAR"
        echo "sha: $SHA"
```

> _`env` 对象中的所有环境变量必须为字符串。传递整数或其他类型可能导致意外结果。_

### 捕获命令输出

你可以捕获远程命令的标准输出，并在后续步骤中使用：

```yaml
- name: 执行并捕获输出
  id: ssh
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    capture_stdout: true
    script: |
      echo "Hello World"
      hostname

- name: 使用捕获的输出
  run: echo "SSH 输出为 ${{ steps.ssh.outputs.stdout }}"
```

---

## 🌐 代理与跳板机用法

你可以通过代理（跳板机）连接到远程主机，适用于进阶网络拓扑。

```bash
+--------+       +----------+      +-----------+
| Laptop | <-->  | Jumphost | <--> | FooServer |
+--------+       +----------+      +-----------+
```

示例 `~/.ssh/config`：

```text
Host Jumphost
  HostName Jumphost
  User ubuntu
  Port 22
  IdentityFile ~/.ssh/keys/jump_host.pem

Host FooServer
  HostName FooServer
  User ubuntu
  Port 22
  ProxyCommand ssh -q -W %h:%p Jumphost
```

**GitHub Actions YAML:**

```diff
  - name: SSH 代理命令
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     proxy_host: ${{ secrets.PROXY_HOST }}
+     proxy_username: ${{ secrets.PROXY_USERNAME }}
+     proxy_key: ${{ secrets.PROXY_KEY }}
+     proxy_port: ${{ secrets.PROXY_PORT }}
      script: |
        mkdir abc/def
        ls -al
```

---

## 🛡️ 安全最佳实践

### 保护你的私钥

密码短语会加密你的私钥，即使泄露也无法被攻击者直接利用。请务必妥善保管私钥。

```diff
  - name: SSH 密钥密码短语
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     passphrase: ${{ secrets.PASSPHRASE }}
      script: |
        whoami
        ls -al
```

### 主机指纹验证

验证 SSH 主机指纹有助于防止中间人攻击。获取主机指纹（将 `ed25519` 替换为你的密钥类型，`example.com` 替换为你的主机）：

```sh
ssh example.com ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub | cut -d ' ' -f2
```

更新配置：

```diff
  - name: SSH 密钥密码短语
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     fingerprint: ${{ secrets.FINGERPRINT }}
      script: |
        whoami
        ls -al
```

---

## 🚨 错误处理与疑难解答

### 常见问题

#### 命令未找到（npm 或其他命令）

如果遇到 "command not found" 错误，请参考 [此评论](https://github.com/appleboy/ssh-action/issues/31#issuecomment-1006565847) 了解交互式与非交互式 shell 的区别。

许多 Linux 发行版的 `/etc/bash.bashrc` 包含如下内容：

```sh
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
```

注释掉该行或使用命令的绝对路径。

---

## 🤝 贡献

欢迎贡献！请提交 Pull Request 改进 `appleboy/ssh-action`。

---

## 📝 许可证

本项目采用 [MIT License](LICENSE) 授权。

---

## File: README.zh-tw.md

# 🚀 GitHub Actions 的 SSH

[English](./README.md) | 繁體中文 | [简体中文](./README.zh-cn.md)

## 目錄

- [🚀 GitHub Actions 的 SSH](#-github-actions-的-ssh)
  - [目錄](#目錄)
  - [📖 簡介](#-簡介)
  - [🧩 核心概念與輸入參數](#-核心概念與輸入參數)
    - [🔌 連線設定](#-連線設定)
    - [🛠️ 指令設定](#️-指令設定)
    - [🌐 代理設定](#-代理設定)
  - [📤 輸出變數](#-輸出變數)
  - [⚡ 快速開始](#-快速開始)
  - [🔑 SSH 金鑰設定與 OpenSSH 相容性](#-ssh-金鑰設定與-openssh-相容性)
    - [設定 SSH 金鑰](#設定-ssh-金鑰)
      - [產生 RSA 金鑰](#產生-rsa-金鑰)
      - [產生 ED25519 金鑰](#產生-ed25519-金鑰)
    - [OpenSSH 相容性](#openssh-相容性)
  - [🛠️ 用法場景與進階範例](#️-用法場景與進階範例)
    - [使用密碼認證](#使用密碼認證)
    - [使用私鑰認證](#使用私鑰認證)
    - [多條指令](#多條指令)
    - [從檔案執行指令](#從檔案執行指令)
    - [多主機](#多主機)
    - [多主機不同埠號](#多主機不同埠號)
    - [多主機同步執行](#多主機同步執行)
    - [傳遞環境變數到 shell 腳本](#傳遞環境變數到-shell-腳本)
    - [擷取指令輸出](#擷取指令輸出)
  - [🌐 代理與跳板機用法](#-代理與跳板機用法)
  - [🛡️ 安全最佳實踐](#️-安全最佳實踐)
    - [保護你的私鑰](#保護你的私鑰)
    - [主機指紋驗證](#主機指紋驗證)
  - [🚨 錯誤處理與疑難排解](#-錯誤處理與疑難排解)
    - [常見問題](#常見問題)
      - [指令找不到（npm 或其他指令）](#指令找不到npm-或其他指令)
  - [🤝 貢獻](#-貢獻)
  - [📝 授權](#-授權)

---

## 📖 簡介

**SSH for GitHub Actions** 是一個強大的 [GitHub Action](https://github.com/features/actions)，可讓你在 CI/CD 工作流程中輕鬆且安全地執行遠端 SSH 指令。  
本專案以 [Golang](https://go.dev) 和 [drone-ssh](https://github.com/appleboy/drone-ssh) 建立，支援多主機、代理、進階認證等多種 SSH 場景。



[](https://github.com/appleboy/ssh-action/actions/workflows/main.yml)
[](https://github.com/appleboy/ssh-action/actions/workflows/trivy-scan.yml)

**簡報：** [SSH for GitHub Actions](https://speakerdeck.com/appleboy/ssh-for-github-actions)

---

## 🧩 核心概念與輸入參數

本 Action 提供彈性的 SSH 指令執行能力，並具備豐富的設定選項。

完整參數請參閱 [action.yml](./action.yml)。

### 🔌 連線設定

這些參數用於控制如何連線到遠端主機。

| 參數                | 說明                                          | 預設值 |
| ------------------- | --------------------------------------------- | ------ |
| host                | SSH 主機位址                                  |        |
| port                | SSH 埠號                                      | 22     |
| username            | SSH 使用者名稱                                |        |
| password            | SSH 密碼                                      |        |
| protocol            | SSH 協議版本（`tcp`、`tcp4`、`tcp6`）         | tcp    |
| sync                | 指定多個主機時同步執行                        | false  |
| timeout             | SSH 連線主機的逾時時間                        | 30s    |
| key                 | SSH 私鑰內容（如 `~/.ssh/id_rsa` 的原始內容） |        |
| key_path            | SSH 私鑰路徑                                  |        |
| passphrase          | SSH 私鑰密碼                                  |        |
| fingerprint         | 主機公鑰的 SHA256 指紋                        |        |
| use_insecure_cipher | 允許額外（不安全）的加密演算法                | false  |
| cipher              | 允許的加密演算法，未指定時使用預設值          |        |

---

### 🛠️ 指令設定

這些參數用於控制在遠端主機上執行的指令及相關行為。

| 參數            | 說明                                                  | 預設值 |
| --------------- | ----------------------------------------------------- | ------ |
| script          | 遠端執行的指令                                        |        |
| script_path     | 儲存庫中包含要遠端執行指令的檔案路徑                  |        |
| envs            | 傳遞給 shell 腳本的環境變數                           |        |
| envs_format     | 環境變數傳遞的彈性設定                                |        |
| allenvs         | 傳遞所有帶 `GITHUB_` 和 `INPUT_` 前綴的環境變數到腳本 | false  |
| command_timeout | SSH 指令執行逾時時間                                  | 10m    |
| debug           | 啟用除錯模式                                          | false  |
| request_pty     | 向伺服器請求偽終端                                    | false  |
| curl_insecure   | 允許 curl 連線無憑證的 SSL 網站                       | false  |
| capture_stdout  | 擷取指令的標準輸出作為 Action 輸出                    | false  |
| version         | drone-ssh 執行檔版本，未指定時使用最新版本            |        |

---

### 🌐 代理設定

這些參數用於透過代理（跳板機）連線到目標主機。

| 參數                      | 說明                                      | 預設值 |
| ------------------------- | ----------------------------------------- | ------ |
| proxy_host                | SSH 代理主機                              |        |
| proxy_port                | SSH 代理埠號                              | 22     |
| proxy_username            | SSH 代理使用者名稱                        |        |
| proxy_password            | SSH 代理密碼                              |        |
| proxy_passphrase          | SSH 代理私鑰密碼                          |        |
| proxy_protocol            | SSH 代理協議版本（`tcp`、`tcp4`、`tcp6`） | tcp    |
| proxy_timeout             | SSH 連線代理主機的逾時時間                | 30s    |
| proxy_key                 | SSH 代理私鑰內容                          |        |
| proxy_key_path            | SSH 代理私鑰路徑                          |        |
| proxy_fingerprint         | 代理主機公鑰的 SHA256 指紋                |        |
| proxy_cipher              | 代理允許的加密演算法                      |        |
| proxy_use_insecure_cipher | 代理允許額外（不安全）的加密演算法        | false  |

> **注意：** 如需實現已移除的 `script_stop` 功能，請在 shell 腳本最上方加上 `set -e`。

---

## 📤 輸出變數

本 Action 提供以下輸出，可在後續步驟中使用：

| 輸出   | 說明                                                  |
| ------ | ----------------------------------------------------- |
| stdout | 執行指令的標準輸出（需設定 `capture_stdout: true`） |

---

## ⚡ 快速開始

只需簡單設定，即可在工作流程中執行遠端 SSH 指令：

```yaml
name: Remote SSH Command
on: [push]
jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: 執行遠端 SSH 指令（密碼認證）
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          password: ${{ secrets.PASSWORD }}
          port: ${{ secrets.PORT }}
          script: whoami
```

**輸出：**

```sh
======CMD======
whoami
======END======
out: your_username
===============================================
✅ Successfully executed commands to all hosts.
===============================================
```

---

## 🔑 SSH 金鑰設定與 OpenSSH 相容性

### 設定 SSH 金鑰

建議於本地端（非遠端伺服器）產生 SSH 金鑰。請以 GitHub Secrets 指定的使用者名稱登入並產生金鑰對：

#### 產生 RSA 金鑰

```bash
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
```

#### 產生 ED25519 金鑰

```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
```

將新產生的公鑰加入伺服器的 authorized_keys。 [了解更多 authorized_keys](https://www.ssh.com/ssh/authorized_keys/)

```bash
# 加入 RSA 公鑰
cat .ssh/id_rsa.pub | ssh user@host 'cat >> .ssh/authorized_keys'

# 加入 ED25519 公鑰
cat .ssh/id_ed25519.pub | ssh user@host 'cat >> .ssh/authorized_keys'
```

複製私鑰內容並貼到 GitHub Secrets。

```bash
# macOS
pbcopy < ~/.ssh/id_rsa
# Ubuntu
xclip < ~/.ssh/id_rsa
```

> **提示：** 複製內容需包含 `-----BEGIN OPENSSH PRIVATE KEY-----` 到 `-----END OPENSSH PRIVATE KEY-----`（含）。

ED25519 同理：

```bash
# macOS
pbcopy < ~/.ssh/id_ed25519
# Ubuntu
xclip < ~/.ssh/id_ed25519
```

更多資訊：[SSH 免密碼登入](http://www.linuxproblem.org/art_9.html)。

> **注意：** 根據 SSH 版本，可能還需：
>
> - 將公鑰放入 `.ssh/authorized_keys2`
> - 設定 `.ssh` 權限為 700
> - 設定 `.ssh/authorized_keys2` 權限為 640

### OpenSSH 相容性

若出現以下錯誤：

```bash
ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey]
```

在 Ubuntu 20.04+，你可能需明確允許 `ssh-rsa` 演算法。請於 OpenSSH 設定檔（`/etc/ssh/sshd_config` 或 `/etc/ssh/sshd_config.d/` 下的 drop-in 檔案）加入：

```text
CASignatureAlgorithms +ssh-rsa
```

或直接使用預設支援的 ED25519 金鑰：

```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
```

---

## 🛠️ 用法場景與進階範例

本節涵蓋常見與進階用法，包括多主機、代理、環境變數傳遞等。

### 使用密碼認證

```yaml
- name: 執行遠端 SSH 指令（密碼認證）
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    password: ${{ secrets.PASSWORD }}
    port: ${{ secrets.PORT }}
    script: whoami
```

### 使用私鑰認證

```yaml
- name: 執行遠端 SSH 指令（私鑰認證）
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    script: whoami
```

### 多條指令

```yaml
- name: 多條指令
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    script: |
      whoami
      ls -al
```



### 從檔案執行指令

```yaml
- name: 檔案指令
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    script_path: scripts/script.sh
```

### 多主機

```diff
  - name: 多主機
    uses: appleboy/ssh-action@v1
    with:
-     host: "foo.com"
+     host: "foo.com,bar.com"
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
      script: |
        whoami
        ls -al
```

預設 `port` 為 `22`。

### 多主機不同埠號

```diff
  - name: 多主機
    uses: appleboy/ssh-action@v1
    with:
-     host: "foo.com"
+     host: "foo.com:1234,bar.com:5678"
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      script: |
        whoami
        ls -al
```

### 多主機同步執行

```diff
  - name: 多主機
    uses: appleboy/ssh-action@v1
    with:
      host: "foo.com,bar.com"
+     sync: true
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
      script: |
        whoami
        ls -al
```

### 傳遞環境變數到 shell 腳本

```diff
  - name: 傳遞環境變數
    uses: appleboy/ssh-action@v1
+   env:
+     FOO: "BAR"
+     BAR: "FOO"
+     SHA: ${{ github.sha }}
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     envs: FOO,BAR,SHA
      script: |
        echo "I am $FOO"
        echo "I am $BAR"
        echo "sha: $SHA"
```

> _`env` 物件中的所有環境變數必須為字串。傳遞整數或其他型別可能導致非預期結果。_

### 擷取指令輸出

你可以擷取遠端指令的標準輸出，並在後續步驟中使用：

```yaml
- name: 執行並擷取輸出
  id: ssh
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    capture_stdout: true
    script: |
      echo "Hello World"
      hostname

- name: 使用擷取的輸出
  run: echo "SSH 輸出為 ${{ steps.ssh.outputs.stdout }}"
```

---

## 🌐 代理與跳板機用法

你可以透過代理（跳板機）連線到遠端主機，適用於進階網路拓撲。

```bash
+--------+       +----------+      +-----------+
| Laptop | <-->  | Jumphost | <--> | FooServer |
+--------+       +----------+      +-----------+
```

範例 `~/.ssh/config`：

```text
Host Jumphost
  HostName Jumphost
  User ubuntu
  Port 22
  IdentityFile ~/.ssh/keys/jump_host.pem

Host FooServer
  HostName FooServer
  User ubuntu
  Port 22
  ProxyCommand ssh -q -W %h:%p Jumphost
```

**GitHub Actions YAML:**

```diff
  - name: SSH 代理指令
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     proxy_host: ${{ secrets.PROXY_HOST }}
+     proxy_username: ${{ secrets.PROXY_USERNAME }}
+     proxy_key: ${{ secrets.PROXY_KEY }}
+     proxy_port: ${{ secrets.PROXY_PORT }}
      script: |
        mkdir abc/def
        ls -al
```

---

## 🛡️ 安全最佳實踐

### 保護你的私鑰

密碼短語會加密你的私鑰，即使外洩也無法被攻擊者直接利用。請務必妥善保管私鑰。

```diff
  - name: SSH 私鑰密碼
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     passphrase: ${{ secrets.PASSPHRASE }}
      script: |
        whoami
        ls -al
```

### 主機指紋驗證

驗證 SSH 主機指紋有助於防止中間人攻擊。取得主機指紋（將 `ed25519` 換成你的金鑰型別，`example.com` 換成你的主機）：

```sh
ssh example.com ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub | cut -d ' ' -f2
```

更新設定：

```diff
  - name: SSH 私鑰密碼
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     fingerprint: ${{ secrets.FINGERPRINT }}
      script: |
        whoami
        ls -al
```

---

## 🚨 錯誤處理與疑難排解

### 常見問題

#### 指令找不到（npm 或其他指令）

若遇到 "command not found" 錯誤，請參考 [此討論](https://github.com/appleboy/ssh-action/issues/31#issuecomment-1006565847) 了解互動式與非互動式 shell 差異。

許多 Linux 發行版的 `/etc/bash.bashrc` 包含如下內容：

```sh
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
```

請將該行註解掉或使用指令的絕對路徑。

---

## 🤝 貢獻

歡迎貢獻！請提交 Pull Request 改善 `appleboy/ssh-action`。

---

## 📝 授權

本專案採用 [MIT License](LICENSE) 授權。

## 2. Official Technical Reference & Guides (appleboy/docs)

## File: README.md

[](https://gitter.im/drone/drone?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)

Documentation for the [Drone](https://github.com/drone/drone) continuous integration server, found at [readme.drone.io](http://readme.drone.io)

## Setup

This website uses the [Hugo](https://github.com/spf13/hugo) static site generator. If you are planning to contribute you'll want to download and install Hugo on your local machine.

Install on OSX with brew:

```
brew install hugo
```

Install using the Go tool:

```
go get github.com/spf13/hugo
```

Or follow these instructions for your platform: http://gohugo.io/overview/installing/

## Build

Generate the website and output to `./public`

```
hugo
```

Generate the website, serve on localhost:1313/installation/overview, and automatically refresh the browser when a change is detected:

```
hugo server -b localhost
```


## Generate

Generate plugin documentation:

```
go run contrib/plugin-gen.go --repo drone-s3
```

Generate the swagger documentation:


```
go run contrib/swagger-gen.go --input $GOPATH/src/github.com/drone/drone/docs/swagger.yml
```

---

## File: content/usage/artifacts.md

+++
date = "2015-12-05T16:00:21-08:00"
draft = false
title = "Publishing Artifacts"
weight = 11
menu = "usage"
toc = true
+++


# Overview

Publishing files or build artifacts is triggered at the end of a successful build. Note that publish steps are not executed for pull requests or failed builds.

Example to publish a Node package to the [npmjs.com](/plugins/npm/) registry using the NPM plugin:

```yaml
---
publish:
  npm:
    username: octocat
    password: password
    email: octocat@github.com
```

# Plugins

Drone does not have any builtin publishing or artifact capabilities. Drone instead offers a large library of plugins for publish to [DockerHub](/plugins/docker/), [S3](/plugins/s3/), [Google Storage](/plugins/google_cloudstorage/) and more. Please see the [plugin documentation](/plugins/) for a complete list.

# Docker

Building and publishing a Docker image to the registry is handled by plugins. We have multiple plugins for building and publishing Docker images to [DockerHub](/plugins/docker/), [Google Container Registry](/plugins/gcr/), and more.

# Conditions

Use the `when` block to limit publish step execution:

```yaml
---
publish:
  heroku:
    app: mycompany.com
    when:
      branch: master
```

Execute a publish step if the branch is `master` or `release`:

```yaml
---
publish:
  rubygems:
    api_key: e123f83113f79eb17308bbf1ca8885bb
    when:
      branch: [master, release]
```

Execute a publish step if the branch is _not_ `master`:

```yaml
---
publish:
  rubygems:
    api_key: e123f83113f79eb17308bbf1ca8885bb
    when:
      branch: "!master"
```

Execute a publish step when the commit is a `tag`:

```yaml
---
publish:
  rubygems:
    api_key: e123f83113f79eb17308bbf1ca8885bb
    when:
      event: tag
```

Execute a publish step for a single matrix permutation:

```yaml
---
publish:
  rubygems:
    api_key: e123f83113f79eb17308bbf1ca8885bb
    when:
      matrix:
        RUBY_VERSION: 2.3
        REDIS_VERSION: 2.8
```

---

## File: content/usage/build_test.md

+++
date = "2015-12-05T16:00:21-08:00"
draft = false
title = "Build & Test"
weight = 7
menu = "usage"
toc = true
+++

# Overview

Drone uses the `build` section of the `.drone.yml` to define your Docker build environment and your build and test instructions. The following is an example build definition:

```yaml
---
build:
  image: golang
  commands:
    - go get
    - go build
    - go test
```

This example downloads and runs a `golang` container and executes the specified commands inside the container. Your source code, previously fetched during the `clone` step, is provided to the container as a Docker volume.

# Images

Drone supports any valid Docker image from any Docker registry:

```yaml
---
image: golang
image: golang:1.5
image: library/golang:1.5
image: index.docker.io/library/golang:1.5
```

Provide your registry credentials if your build image is private:

```yaml
---
build:
  image: foo/bar
  auth_config:
    username: octocat
    password: password
    email: octocat@github.com
```

Use the `pull` attribute to instruct Drone to always pull the latest Docker image. This helps ensure you are always testing your code against the latest image:

```yaml
---
build:
  image: golang
  pull: true
```

# Workspace

The build workspace is located in the `/drone/src/<repository>` directory inside the `/drone` volume. You cannot change the volume location, however, you can change the subdirectory.

The below example overrides the default workspace to `/drone/src/github.com/octocat/hello-world`:

```yaml
---
clone:
  path: github.com/octocat/hello-world
```

# Environment

Use the environment section to inject environment variables into your build environment:

```yaml
---
build:
  image: golang:1.5
  environment:
    - GO15VENDOREXPERIMENT=0
    - GOOS=linux
    - GOARCH=amd64
    - CGO_ENABLED=0
```

Variable expansion is not supported. The following example __will not work__:

```yaml
---
environment:
  - PATH=$PATH:/go
```

# Commands

Build commands execute sequentially in a shell environment with the `-e` flag. The `-e` flag instructs the shell environment to exit immediately if a command returns a non-zero exit code.

```yaml
---
build:
  image: golang:1.5
  commands:
    - go get
    - go build
    - go test
```

Please be aware that chaining commands together with `&&` may result in the build not exiting on failure. This is not a bug with Drone. This is the default bash `-e` behavior. See [this stackoverflow](http://stackoverflow.com/questions/25794905/why-does-set-e-true-false-true-not-exit#25795239) for more details.

# Multiple Steps

Build commands can be split into multiple named steps. The below example let's us split our frontend build (node) and our backend build (go). Each step executes in its own isolated container environment while sharing the same build workspace.

```yaml
---
build:
  frontend:
    image: node:5.3.0
    commands:
      - npm install
      - npm test
  backend:
    image: golang:1.5
    commands:
      - go get
      - go build
      - go test
```

# Conditions

Limit when a build step should is executed using conditional logic. The below example splits our build into two steps. The second build step, building the distribution, is only executed when we tag a release. This reduces the build time for `push` and `pull_request` events by removing un-necessary steps:

```yaml
---
build:
  test:
    image: golang:1.5
    commands:
      - go get
      - go test
  bundle:
    image: golang:1.5
    commands:
      - go build
      - tar -cvf bundle.tar myapp
    when:
      event: tag
```

Only execute a step when building a `push` or `tag` event:

```yaml
---
build:
  bundle:
    when:
      event: [push, tag]
```

Only executes a step when building the `master` branch:

```yaml
---
build:
  bundle:
    when:
      branch: master
```

There are a number of other conditions which can be used in whens: `repo` (a full repo path) and `success`, `failure` and `change` (all booleans based on the previous job status).

```yaml
---
build:
  bundle:
    when:
      repo: drone/drone
      success: true
      failure: true
      change: true
```

# Dependencies

Cloning private submodules or private dependencies (using `npm` or `go get`) requires remote authentication. We recommend using `git+https` for submodules or dependencies, allowing Drone to authenticate to your remote repository using a `.netrc` file injected by default into your build environment.

Instead of using the `git+ssh` url in your `package.json` file:

```js
{
    "library": "git+ssh://git@github.com:octocat/library.git"
}
```

Use the `git+https` url in your `package.json` file:

```js
{
    "library": "https://github.com/octocat/library.git"
}
```

# Volumes

Use the `volumes` attribute to mount folders on your host machine into your build container. These are [Docker volumes](https://docs.docker.com/engine/userguide/dockervolumes/) and therefore use the same `<host>:<container>` declaration conventions:

```yaml
---
build:
  image: golang
  volumes:
    - /some/path/on/host:/path/in/container
```

For security reasons this option is only available to trusted repositories. Trusted repositories are enabled on a per-repository basis by a Drone administrator from your repository settings screen.

# Devices

Use the `devices` attribute to map devices from your host machine into your build container. These are [Docker devices](https://docs.docker.com/compose/compose-file/#devices) and therefore use the same declaration conventions:

```yaml
---
build:
  image: golang
  devices:
    - "/dev/ttyUSB0:/dev/ttyUSB0"
```

For security reasons this option is only available to trusted repositories. Trusted repositories are enabled on a per-repository basis by a Drone administrator from your repository settings screen.

# Privileged

Use the `privileged` attribute to run your build in a privileged Docker container:

```yaml
---
build:
  image: golang
  privileged: true
```

For security reasons this option is only available to trusted repositories. Trusted repositories are enabled on a per-repository basis by a Drone administrator from your repository settings screen.

# Timeouts

There is a default 60 minute timeout for your build. This can be increased by a Drone administrator from your repository settings screen.

# Triggers

Builds are triggered primarily by `push` and `pull_request` hooks from your remote system (ie GitHub). Sometimes changes to an upstream project have a cascading impact on downstream projects. You can trigger cascading builds using the [trigger](/plugins/downstream/) plugin.

# Scheduling

Drone does not have any builtin scheduling capabilities. For scheduled builds (ie nightly builds) we recommend using the Drone command line tools with cron to trigger builds at scheduled intervals.

# Skip Commits

Instruct Drone to skip builds by by including any combination of `ci` and `skip` wrapped in square brackets in your commit message. Examples: `[skip CI]` `[ci skip]`

# Skip Branches

Instruct Drone to skip branches by including a branch white-list in your `.drone.yml`

```yaml
---
branches:
  - master
  - develop
```

---

## File: content/usage/caching.md

+++
date = "2015-12-05T16:00:21-08:00"
draft = false
title = "Caching"
weight = 5
menu = "usage"
toc = true
+++

# Overview

Drone allows you to cache directories within the build workspace (inside the `/drone` volume). When a build successfully completes, the named directories are gzipped and stored on the host machine. When a new build starts, the named directories are restored from the gzipped files. This can be used to improve the performance of your builds.

Example configuration to cache the `.git` and the `node_modules` directory:

```yaml
---
cache:
  mount:
    - node_modules
    - .git
```

# Branches and Matrix

Drone keeps a separate cache for each Branch and Matrix combination. Let’s say, for example, you are using matrix builds to test node `0.11.x` and node `0.12.x` and you are caching `node_modules`. Drone will separately cache `node_modules` for each version of node.

# Pull Requests

Pull requests have read-only access to the cache. This means pull requests are not permitted to re-build the cache. This is done for security and stability purposes, to prevent a pull request from corrupting your cache.


# Deleting the Cache

There is currently no mechanism to automatically delete or flush the cache. This must be done manually, on each worker node. The cache is located in `/var/lib/drone/cache/`.

# Distributed Cache

This is outside the scope of Drone. You may, for example, use a distributed filesystem such as `ceph` or `gluster` mounted to `/var/lib/drone/cache/` to share the cache across nodes.

# Common Issues

The most common issue occurs when you try to cache files or folders that are not children of the `/drone` volume. The below example **will not work**:

```yaml
---
cache:
  mount:
    - /root/node_modules
    - /usr/lib/python3.2/site-packages
```

You should instead only cache directories in `/drone`:


```yaml
---
cache:
  mount:
    - /drone/node_modules
    - /drone/site-packages
```

To work around this limitation we can cache files or folders in `/drone` and copy they to the expected location at the start of the build, and then copy them back to `/drone` at the end fo the build:

```yaml
---
build:
  image: node
  commands:
    - cp -a /drone/node_modules /root
    - rm -rf /drone/node_modules
    - npm -g install
    - npm run build
    - npm run tests
    - cp -a /root/node_modules /drone
```

---

## File: content/usage/cloning.md

+++
date = "2015-12-05T16:00:21-08:00"
draft = false
title = "Cloning"
weight = 4
menu = "usage"
toc = true
+++

# Overview

Drone will automatically clone your repository during the build lifecycle. You can customize this behavior in the **clone** section of the yaml file. This section is entirely optional.

This is an example yaml configuration:

```yaml
---
clone:
  depth: 50
  recursive: true
  path: github.com/drone/drone
```

Which results in the following command:

```
git clone --depth=50 --recusive=true \
    https://github.com/drone/drone.git \
    /drone/src/github.com/drone/drone
```

# Authentication

Drone uses `git+https` combined with a `.netrc` file to authenticate and clone private repositories. This allows Drone to clone private Git submodules that also use `git+https`.

# Customization

Limit the amount of git history that is fetched from the remote repository:

```yaml
---
clone:
  depth: 50
```

Fetch all tags from the remote repository:

```yaml
---
clone:
  tags: true
```

Disable tls verification when cloning the remote repository:

```yaml
---
clone:
  skip_verify: true
```

Recursively clone all submodules:

```yaml
---
clone:
  recursive: true
```

Recursively clone and update all submodules:

```yaml
---
clone:
  recursive: true
  submodule_update_remote: true
```

# Private Submodules

Private submodules may encounter the following error:

```
Warning: Permanently added 'github.com,192.30.252.130' (RSA) to the list of known hosts.
ERROR: Repository not found.
fatal: Could not read from remote repository.
```

This happens when a private submodule uses a git+ssh url:

```git
[submodule "hello-world"]
    path = hello-world
    url = git@github.com:octocat/hello-world.git
```

This can be mitigated by overriding the submodule url to use git+https:

```yaml
---
clone:
  recursive: true
  submodule_override:
    hello-world: https://github.com/octocat/hello-world.git
```

Overriding the submodule url to force `git+https` allows us to take advantage of the `netrc` file and automatically authenticate to the submodule repository.

---

## File: content/usage/deploy.md

+++
date = "2015-12-05T16:00:21-08:00"
draft = false
title = "Deployments"
weight = 8
menu = "usage"
toc = true
+++

# Overview

Deployments are triggered at the end of a successful build. Note that deployment steps are not executed for pull requests or failed builds.

Example Heroku deployment using the Heroku plugin:

```yaml
---
deploy:
  heroku:
    app: petstore
```

# Plugins

Drone does not have any builtin deployment capabilities. Drone instead offers a large library of plugins for deploying to [Heroku](/plugins/heroku/), [Tutum](/plugins/tutum/), [Amazon](/plugins/aws_codedeploy/) and more. Please see the [plugin documentation](/plugins/) for a complete list.

# Docker

Building and deploying a Docker image to the registry is handled by plugins. We have multiple plugins for building and publishing Docker images to [DockerHub](/plugins/docker/), [Google Container Registry](/plugins/gcr/), and more.

# Conditions

Use the `when` block to limit deployment step execution:

```yaml
---
deploy:
  heroku:
    app: mycompany.com
    when:
      branch: master
```

Execute a deployment step if the branch is `master` or `develop`:

```yaml
---
deploy:
  heroku:
    app: mycompany.com
    when:
      branch: [master, develop]
```

Execute a deployment step if the branch is _not_ `master`:

```yaml
---
deploy:
  heroku:
    app: mycompany.com
    when:
      branch: "!master"
```

Execute a deployment step if the branch is starts with `prefix/*`:

```yaml
---
deploy:
  heroku:
    app: mycompany.com
    when:
      branch: prefix/*
```

Execute a deployment step when the commit is a `tag`:

```yaml
---
deploy:
  heroku:
    app: mycompany.com
    when:
      event: tag
```

Execute a deployment step for a single matrix permutation:

```yaml
---
deploy:
  heroku:
    app: mycompany.com
    when:
      matrix:
        GO_VERSION: 1.5
        REDIS_VERSION: 2.8
```

Define the same deployment step multiple times, using different configuration based on branch:

```yaml
---
deploy:
  heroku:
    app: test.mycompany.com
    when:
      branch: develop

  heroku:
    app: mycompany.com
    when:
      branch: master
```

---

## File: content/usage/matrix.md

+++
date = "2015-12-05T16:00:21-08:00"
draft = false
title = "Matrix"
weight = 11
menu = "usage"
toc = true
+++

# Overview

Drone uses the `matrix` section of the `.drone.yml` to define the build matrix. Drone executes a build for each permutation in the matrix, allowing you to build and test a single commit against many configurations.

Example matrix definition for testing multiple Go and Redis versions. This matrix results in a total of 6 different build permutations:

```yaml
---
matrix:
  GO_VERSION:
    - 1.4
    - 1.3
  REDIS_VERSION:
    - 2.6
    - 2.8
    - 3.0
```

# Interpolation

Matrix variables are injected into the `.drone.yml` file using the `$$` syntax, performing a simple find / replace. Matrix variables are also injected into your build container as environment variables.

Example `.drone.yml` file before injecting the matrix parameters:

```yaml
---
build:
  image: golang:$$GO_VERSION
  commands:
    - go get
    - go build
    - go test

compose:
  redis:
    image: redis:$$REDIS_VERSION

matrix:
  GO_VERSION:
    - 1.4
    - 1.3
  REDIS_VERSION:
    - 2.6
    - 2.8
    - 3.0
```

Example `.drone.yml` file after injecting the matrix parameters:

```yaml
---
build:
  image: golang:1.4
  environment:
    - GO_VERSION=1.4
    - REDIS_VERSION=3.0
  commands:
    - go get
    - go build
    - go test

compose:
  redis:
    image: redis:3.0
```

# Deployments

Matrix builds execute the same `.drone.yml` multiple times, but with different parameters. This means that publish and deployment steps are executed multiple times as well, which is typically undesired. To restrict a publish or deployment step to a single permutation you can add the following condition:

```yaml
---
deploy:
  heroku:
    app: foo
    when:
      matrix:
        GO_VERSION: 1.4
        REDIS_VERSION: 3.0
```

---

## File: content/usage/notify.md

+++
date = "2015-12-05T16:00:21-08:00"
draft = false
title = "Notifications"
weight = 9
menu = "usage"
toc = true
+++

# Overview

Notifications are triggered at the end of your entire build. If you are running a matrix build the notification is sent after all matrix jobs complete.

Example Slack notification using the Slack plugin:

```yaml
---
notify:
  slack:
    webhook_url: https://hooks.slack.com/services/...
    channel: dev
    username: drone
```

Please note notifications are executed after a build is already terminated and will not write any output to your build logs. For troubleshooting failed notifications please contact your system administrator to research errors in the Drone server logs.

# Plugins

Drone does not have any builtin notification capabilities. Drone instead offers a large library of plugins for sending [Slack](/plugins/slack/), [Hipchat](/plugins/hipchat/), [Email](/plugins/email/) notifications and more. Please see the [plugin documentation](/plugins/) for a complete list.

# Conditions

Use the `when` block to limit notification step execution:

```yaml
---
notify:
  slack:
    channel: foo
    when:
      success: false
      failure: false
      change: true
```

Execute a notification step if the branch is `master`:

```yaml
---
notify:
  slack:
    channel: foo
    when:
      branch: master
```

Execute a notification step if the branch is `master` or `develop`:

```yaml
---
notify:
  slack:
    channel: foo
    when:
      branch: [master, develop]
```

Execute a notification step if the branch is _not_ `develop`:

```yaml
---
notify:
  slack:
    channel: foo
    when:
      branch: "!develop"
```

Execute a notification step if the branch is starts with `prefix/*`:

```yaml
---
notify:
  slack:
    channel: foo
    when:
      branch: prefix/*
```

Execute a notification step when the commit is a `pull_request`, `push` or `tag`:

```yaml
---
notify:
  slack:
    channel: foo
    when:
      event: pull_request
```

Define the same notification step multiple times, using different configuration based on branch:

```yaml
---
notify:
  slack:
    channel: foo
    when:
      branch: develop

  slack:
    channel: bar
    when:
      branch: master
```

# Templates

Many of the Drone notifications support [handlebars](http://handlebarsjs.com/) templates for customizing notification messages. This example demonstrates a custom hipchat notification message:

```yaml
---
notify:
  hipchat:
    template: |
      {{ repo.full_name }} finished build {{ build.number }}
      with a status of {{ build.status }}
```

Example template that generates a different message based on build status:

```yaml
---
notify:
  hipchat:
    template: |
      {{#success build.status}}
        {{ build.author }} successfully pushed to {{ build.branch}}
      {{else}}
        {{ build.author }} broke the build. Hang your head in shame.
      {{/success}}
```

# Template Data

This is an example data structure passed to the template engine. Any of the below variables may be referenced in your template:

```js
{
    "build": {
        "number": 22,
        "status": "success",
        "started_at": 1421029603,
        "finished_at": 1421029813,
        "event": "push",
        "commit": "7fd1a60",
        "branch": "master",
        "message": "Update README",
        "author": "octocat",
        "author_email": "octocat@github.com",
        "link_url": "https://github.com/octocat/Hello-World/commit/7fd1a60"
    },
    "repo": {
        "owner": "octocat",
        "name": "hello-world",
        "full_name": "octocat/hello-world",
        "link_url": "https://github.com/octocat/hello-world",
        "clone_url": "https://github.com/octocat/hello-world.git"
    },
    "system": {
        "link_url": "https://drone.mycompany.com"
    }
}
```

# Template Builtins

Drone includes a number of builtin handlebars functions to help format your messages. This is an example builtin function that converts a string to all uppercase characters:

```handlebars
{{uppercase build.status}}
```

Converts a string to all lowercase characters:

```handlebars
{{uppercase build.author}}
```

Truncates a string to N characters:

```handlebars
{{ truncate build.commit 8 }}
```

Calculates a duration and returns a human readable string:

```handlebars
{{ duration build.started_at build.finished_at }}
```

Converts a timestamp to a human readable string:

```handlebars
finished at {{ datetime build.finished_at "3:04PM" "UTC" }}
```

Returns true if the build is successful:

```handlebars
{{#success build.status}}
  It works
{{/success}}
```

Returns true if the build failed:

```handlebars
{{#failure build.status}}
  Something is busted
{{/failure}}
```

---

## File: content/usage/overview.md

+++
date = "2015-12-05T16:00:21-08:00"
draft = false
title = "Quick Start"
weight = 1
menu = "usage"
toc = true
+++

# Overview

In order to configure your build you must include a `.drone.yml` file in the root of your repository. This section provides a brief overview of the configuration file and build process.

Example .drone.yml configuration:

```yaml
---
build:
  image: golang
  commands:
    - go get
    - go build
    - go test
```

# Hooks

Once activated, every commit and pull request automatically send a hook from your version control system (ie GitHub) to Drone. These hooks instruct Drone to execute a new build.

When Drone receives a hook it fetches the `.drone.yml` from your repository and uses this as a blueprint for build execution. For the purposes of this tutorial let's assume your repository uses the following configuration:

```yaml
---
build:
  image: golang
  commands:
    - go get
    - go build
    - go test
```

# Cloning

Hooks specify commit details including branch and commit hash. Drone will automatically clone and checkout the commit into the build workspace:

```
git clone --depth=50 --recusive=true \
    https://github.com/octocat/hello-world.git \
    /drone/src/github.com/octocat/hello-world

git checkout 7fd1a60
```

# Images

Drone executes your build inside an ephemeral Docker image. This means you don't have to setup or install any repository dependencies on your host machine. Use any valid Docker image in any Docker registry as your build environment.

Example .drone.yml configuration uses the official Go Docker image:

```yaml
---
build:
  image: golang
```

# Commands

Drone previously cloned your source code into the build workspace. The build workspace is mounted into your Docker container at runtime as a volume. This means your code is cloned **outside** of the build container but your build commands are run **inside** of the build container.

Drone executes the following bash commands inside your build container:

```yaml
---
build:
  image: golang
  commands:
    - go get
    - go build
    - go test
```

# Services

Drone supports launching separate, ephemeral Docker containers as part of the build process. This is useful, for example, if you require a database for running your unit tests.

Example .drone.yml configuration with a Postgres database:

```yaml
---
build:
  image: golang
  commands:
    - go get
    - go build
    - go test

compose:
  database:
    image: postgres
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=mysecretpassword
```

# Deployments

Drone supports a large number of publish and deployment capabilities through external plugins. Plugins are Docker containers that are automatically downloaded, attach to your build, and execute a very specific publish or deployment task.

Example .drone.yml configuration with the Docker publish plugin:

```yaml
---
build:
  image: golang
  commands:
    - go get
    - go build
    - go test

publish:
  docker:
    username: octocat
    password: password
    email: octocat@github.com
    repo: octocat/hello-world
```

First Drone runs your build commands inside the Golang container:

```yaml
---
build:
  image: golang
  commands:
    - go get
    - go build
    - go test
```

Drone executes publish and deployment plugins upon successful completion of the build step. Plugins are executed in separate Docker containers but have access to your build workspace. This means any files created and stored in the `/drone` workspace are available to plugins.

The Docker plugin in our example runs `docker build` and `docker publish` after the build step successfully completes using the configuration parameters in the .drone.yml file:

```yaml
---
publish:
  docker:
    username: octocat
    password: password
    email: octocat@github.com
    repo: octocat/hello-world
```

# Local Testing

Download the [command line tools](/devs/cli) to build and test your code locally inside a Docker environment using the exact same build process as Drone. You should think of your `.drone.yml` file as a `docker-compose.yml` alternative that is optimized for repeatable, local testing.

Command to execute a local build from the command line:

```
drone exec
```

# Getting Help

For help troubleshooting **failed builds** please use [Stackoverflow](https://stackoverflow.com). The Stackoverflow community will be able to answer questions unique to your programming language and technology stack that the Drone maintainers are unqualified to answer.

For all other questions or issues please use the community [chat room](https://gitter.im/drone/drone) for support.

---

## File: content/usage/secrets.md

+++
date = "2015-12-05T16:00:21-08:00"
draft = false
title = "Secrets"
weight = 3
menu = "usage"
toc = true
+++

> WARNING: drone does not prevent you from inadvertently exposing your secrets to the world. Please read this section of the documentation carefully to ensure you follow best practices and avoid exposing sensitive data.


# Overview

Drone lets you store secret variables in an encrypted `.drone.sec` file in the root of your repository. This is useful when your build requires sensitive information that should not be stored in plaintext in your yaml file. This document assumes you have installed the Drone [command line tools](/devs/cli).

Start with a plaintext YAML file that defines your secrets. For demonstration purposes let's assume this file is stored on disk and named `secrets.yml`. Secrets are defined in the `environment` section of this file:

```yaml
---
environment:
  HEROKU_TOKEN: pa$$word
```

Reference secrets in your `.drone.yml` file using the `$$` notation:

```yaml
---
deploy:
  heroku:
    app: petstore
    token: $$HEROKU_TOKEN
```

Encrypt the `secrets.yml` file and generate a `.drone.sec` file:

```
drone secure --repo octocat/hello-world --in secrets.yml
```

Commit the encrypted `.drone.sec` file to the root of your repository:

```
git add .drone.sec
git commit -m "added .drone.sec file"
```

# Interpolation

Secrets are injected directly into the YAML at runtime using the `$$` notation:

```yaml
---
deploy:
  heroku:
    app: foo
    token: $$HEROKU_TOKEN
```

Note that secrets are not automatically exposed to your build environment as
environment variables. For security purposes, you be must explicit when and
where to inject secrets.

This example demonstrates explicitly injecting a secret as an environment variable:

```yaml
---
build:
  image: golang
  commands:
    - go build
    - go test
  environment:
    - PRIVATE_KEY=$$PRIVATE_KEY
```

Secrets that are multiple lines or contain special characters should be escaped to
avoid YAML parsing errors post-interpolation. You can escape strings by wrapping
the variable in quotes as shown below:

```yaml
---
build:
  image: golang
  commands:
    - go build
    - go test
  environment:
    - PRIVATE_KEY="$$PRIVATE_KEY"
```

# Checksums

Drone automatically calculates and stores a checksum of your `.drone.yml` file inside your `.drone.sec` file. Secrets are not injected into your build if the checksum cannot be verified. This means you must re-generate your `.drone.sec` file every time you `.drone.yml` changes (yes security is inconvenient).

Invalid checksums result in the following error at the top of your build logs:

```
Unable to validate YAML checksum.
2ca66eb7be89f31afdebb197174abfa6dd14866ecbf9e552f44be5bd3244d08a
```

# Pull Requests

Drone injects secrets for all build types, including pull requests, as long as the checksum validation passes. You should avoid injecting secrets into the build section of the YAML and inadvertently exposing your secrets to malicious pull requests.

**This is bad** because a malicious pull request could gain access to your secrets:

```yaml
---
build:
  image: node
  environment:
    PASSWORD: $$PASSWORD
  commands:
    - npm install
    - npm run tests
    - npm run integration
```

**This is better** because the build is split into sections. Secrets are injected into a section of the build that is not executed for pull requests:

```yaml
---
build:
  unit_tests:
    image: node
    commands:
      - npm install
      - npm run tests

  integration_tests:
    image: node
    environment:
      PASSWORD: $$PASSWORD
    commands:
      - npm run integration
    when:
      event: push
```


# Global Secrets

Global secrets are stored in the `PLUGIN_PARAMS` environment variable declared in your Drone server configuration file. Global secrets are passed to every single build and should therefore only be used in trusted environments with trusted developers.

Example global secret declaration:

```bash
PLUGIN_PARAMS=SMTP_PASSWORD=foo SLACK_TOKEN=bar
```

Example injecting global secrets into your `.drone.yml` file:

```yaml
---
deploy:
  slack:
    channel: foo
    token: $$SLACK_TOKEN
```


# Common Issues

Secrets are not injected into your build if the checksum cannot be validated. This happens when you change your `.drone.yml` file without re-generating a `.drone.sec` file resulting in the following error message at the top of your build logs:

```
Unable to validate YAML checksum.
2ca66eb7be89f31afdebb197174abfa6dd14866ecbf9e552f44be5bd3244d08a
```