Capistrano 3.20 配置参数详解

Capistrano 3.20.1 配置体系全解:Capfile/deploy.rb/stage 三层加载、doctor 实测参数清单、deploy_to 目录结构、linked_files/linked_dirs、SSH 与角色定义、自定义任务与部署钩子,附生产配置模板与排障实录。

CapistranoCapistranoRuby自动化部署2026/10/27

Capistrano 3.20 配置参数详解

目标:逐层讲清 Capistrano 3.20.1 的配置体系——Capfile / config/deploy.rb / config/deploy/<stage>.rb 三层文件、全部核心参数(含默认值与实测回显)、server/role 定义、SSH 选项、自定义任务与部署钩子。已在 Ubuntu 22.04.5 控制机(Capistrano 3.20.1)+ 2 台 Ubuntu 22.04 目标服务器(web1.local / web2.local)实测通过(2026-10-27)。

一、环境信息

  • 控制机:Ubuntu 22.04.5 LTS,Ruby 3.0.2p107,Capistrano 3.20.1(/usr/local/bin/cap),Rake 13.0.6
  • 目标服务器:web1.local、web2.local(Ubuntu 22.04.5,OpenSSH + git,演示账号 admin / q1w2e3r4,SSH 密钥免密)
  • 演示项目:/root/demo-deploy(cap install STAGES=production 生成),演示应用仓库 http://controller.local:8000/demo-app.git
  • 部署路径:/var/www/demo-app;stage:production

二、前置条件

  1. 控制机已安装 Capistrano 3.20.1,安装与排障全过程见《Ubuntu 22.04 安装 Capistrano 3.20 指南》。
  2. 目标服务器已启动 SSH 服务、创建部署账号 admin、控制机公钥已写入 /home/admin/.ssh/authorized_keys,且控制机可免密登录:
ssh admin@web1.local "hostname; whoami"
ssh admin@web2.local "hostname; whoami"

实测回显:

web1.local
admin
web2.local
admin
  1. 项目目录已用 cap install STAGES=production 初始化(生成 Capfile、config/deploy.rb、config/deploy/production.rb)。
  2. 目标服务器已预建部署目录与 shared 资源(linked 机制依赖):
mkdir -p /var/www/demo-app/shared/config /var/www/demo-app/shared/log
echo "env=production" > /var/www/demo-app/shared/config/app.conf
chown -R admin:admin /var/www/demo-app

三、配置体系与参数查看方式

3.1 三层配置文件加载顺序

Capistrano 项目固定 4 个配置落点,加载顺序为:

  1. Capfile:入口文件,负责 require 各功能模块(setup、deploy、SCM 插件、第三方 gem、自定义任务),基本不改;
  2. config/deploy.rb:全局参数,对所有 stage 生效(应用名、仓库地址、deploy_to、linked_* 等);
  3. config/deploy/<stage>.rb:stage 级参数与服务器清单(每台服务器的地址/用户/角色、stage 特有变量),同名变量覆盖 deploy.rb;
  4. lib/capistrano/tasks/*.rake:自定义任务,Capfile 末尾的 Dir.glob 自动加载。

3.2 查看最终生效参数:cap <stage> doctor

配置是分层覆盖的,想看「某 stage 下所有参数的最终值」,用内置 doctor 任务:

cd /root/demo-deploy
cap production doctor

实测回显(完整照抄):


Environment
    Ruby     ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux-gnu]
    Rubygems 3.3.5
    Bundler  N/A
    Command  /usr/local/bin/cap production doctor

Gems
    capistrano 3.20.1
    airbrussh  1.6.2
    rake       13.0.6 (update available)
    sshkit     1.25.1
    net-ssh    8.0.4

Variables
    :application                    "demo-app"
    :branch                         "master"
    :default_env                    {}
    :deploy_to                      "/var/www/demo-app"
    :format                         :airbrussh
    :format_options                 {:command_output=>true, :log_file=>"log/capistrano.log", :color=>:auto, :truncate=>:auto}
    :git_environmental_variables    {:git_askpass=>"/bin/echo", :git_ssh=>"/tmp/git-ssh-126704dfe3244a4761df.sh"}
    :git_max_concurrent_connections 10
    :git_shallow_clone              false
    :git_wait_interval              0
    :git_wrapper_path               "/tmp/git-ssh-126704dfe3244a4761df.sh"
    :keep_releases                  3
    :linked_dirs                    ["log"]
    :linked_files                   ["config/app.conf"]
    :local_user                     nil
    :log_level                      :debug
    :pty                            false
    :repo_url                       "http://controller.local:8000/demo-app.git"
    :ssh_options                    {:verify_host_key=>:never}
    :stage                          :production
    :tmp_dir                        "/tmp"

Servers (2)
    admin@web1.local [:app, :web]
    admin@web2.local [:app, :web]

这份输出同时是排障利器:环境、gem 版本、最终变量、生效服务器四段一次看全。也可只查变量段:cap production doctor:variables。

3.3 参数赋值三种写法

set :application, "demo-app"          # 直接赋值
append :linked_dirs, "log", "tmp"     # 向数组型参数追加元素(不覆盖已有)
ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp   # 交互询问,默认值取当前分支

set 覆盖、append 追加、ask 部署时暂停等待输入(CI 场景慎用);读取用 fetch(:application)。set 还支持惰性求值块:set :local_user, -> { \git config user.name`.chomp }`。

四、参数总览

参数默认值类别说明
application无(必设)应用应用名,影响 deploy_to 默认路径
repo_url无(必设)仓库目标服务器可访问的仓库地址
branchmaster仓库部署分支/tag
deploy_to/var/www/<application>路径远端部署根目录
keep_releases5路径保留的历史 release 数
tmp_dir/tmp路径远端临时目录(git wrapper 等)
linked_files[]链接从 shared 软链进每个 release 的文件
linked_dirs[]链接从 shared 软链进每个 release 的目录
format:airbrussh输出日志格式器
format_options见 5.5输出输出明细/日志文件/颜色/截断
log_level:debug输出日志级别(log 文件用)
ptyfalseSSH是否分配伪终端(sudo 场景需要)
ssh_options{}SSH透传给 Net::SSH 的连接选项
local_userENV['USER']审计写入 revisions.log 的操作者名
git_shallow_clonefalseGit镜像仓库是否浅克隆(--depth 1)
git_max_concurrent_connections10Gitgit 操作最大并发连接数
git_wait_interval0Gitgit 操作间隔秒数(限流用)
default_env{}环境远端命令注入的环境变量

五、核心参数分类详解

5.1 应用与仓库参数

set :application, "demo-app"
set :repo_url, "http://controller.local:8000/demo-app.git"
set :branch, "master"
  • application:应用名。不显式设 deploy_to 时默认部署到 /var/www/<application>,多应用同机部署就靠不同 application/deploy_to 隔离。
  • repo_url:在每台目标服务器上执行 clone,所以地址必须是目标服务器可达的(不是控制机本地路径)。本文用内网 HTTP 裸仓库演示;生产多为 git@github.com:org/repo.git(SSH 协议,目标服务器需配好部署密钥或 agent forwarding)。
  • branch:支持分支名、tag、commit SHA。ask :branch, ... 可在部署时交互输入。

验证仓库可达性(在部署前单独跑):

cap production git:check

实测回显(节选):

00:00 git:check
      01 git ls-remote http://controller.local:8000/demo-app.git HEAD
      01 7dd8661a9547f2b51b927db290a9499acd8faa93	HEAD
    ✔ 01 admin@web2.local 0.060s
      01 7dd8661a9547f2b51b927db290a9499acd8faa93	HEAD
    ✔ 01 admin@web1.local 0.063s

5.2 部署目录结构(deploy_to 之下)

deploy_to 是理解全部 linked/keep_releases 机制的钥匙。实测部署成功后 web1 上的结构:

ls -la /var/www/demo-app/
total 24
drwxr-xr-x 5 admin admin 4096 Oct 27 09:15 .
drwxr-xr-x 3 root  root  4096 Oct 27 09:15 ..
lrwxrwxrwx 1 admin admin   41 Oct 27 09:15 current -> /var/www/demo-app/releases/20261027091548
drwxrwxr-x 3 admin admin 4096 Oct 27 09:15 releases
drwxrwxr-x 7 admin admin 4096 Oct 27 09:15 repo
-rw-r--r-- 1 admin admin   99 Oct 27 09:15 revisions.log
drwxr-xr-x 4 admin admin 4096 Oct 27 09:15 shared
  • releases/<时间戳>:每次部署一个时间戳目录(git archive 导出),回滚=把 current 软链指回上一个;
  • current:指向当前 release 的软链,部署全部步骤成功后最后一步才切换,中途失败不影响线上;
  • repo:远端镜像仓库(git clone --mirror),每次部署先 git remote update --prune 再 archive 出新 release;
  • shared:跨 release 持久内容(linked_files/linked_dirs 的实体);
  • revisions.log:每次部署/回滚追加一行审计日志。

keep_releases 控制保留份数(默认 5,本文演示设为 3),超出后部署末尾自动清理最旧的,实测回显:

00:00 deploy:cleanup
      Keeping 3 of 4 deployed releases on web1.local
      Keeping 3 of 3 deployed releases on web2.local
      01 rm -rf /var/www/demo-app/releases/20261027091548

💡 技术拓展:「发布切换」在部署工具里有两种主流设计——Capistrano 的软链切换(current 指哪用哪,切换即回滚,零停机易实现)与整目录替换/重启加载(无历史 release,回滚要重新发布)。本文采用前者,也是 Capistrano 的标志性设计。

5.3 链接资源参数:linked_files 与 linked_dirs

append :linked_files, "config/app.conf"
append :linked_dirs, "log"
  • linked_files:不入库的敏感/环境文件(数据库配置、密钥),实体放 shared/config/app.conf,每个 release 里是指向它的软链;
  • linked_dirs:日志、上传文件、缓存等跨发布保留的目录,实体在 shared/ 下。

实测部署时的链接动作(逐行照抄):

00:00 deploy:symlink:linked_files
      01 mkdir -p /var/www/demo-app/releases/20261027091548/config
    ✔ 01 admin@web2.local 0.008s
      02 ln -s /var/www/demo-app/shared/config/app.conf /var/www/demo-app/releases/20261027091548/config/app.conf
    ✔ 02 admin@web1.local 0.004s
    ✔ 02 admin@web2.local 0.006s
00:00 deploy:symlink:linked_dirs
      02 ln -s /var/www/demo-app/shared/log /var/www/demo-app/releases/20261027091548/log
    ✔ 02 admin@web2.local 0.008s
    ✔ 02 admin@web1.local 0.006s

⚠️ 排障实录:若某台目标服务器缺少 linked_files 实体,部署会在 deploy:check:linked_files 阶段中止。实测把 web2 的 shared/config/app.conf 移走后部署,真实报错:

00:00 deploy:check:linked_files
      ERROR linked file /var/www/demo-app/shared/config/app.conf does not exist on web2.local

解决:在该机补建文件(echo "env=production" > /var/www/demo-app/shared/config/app.conf)后重新部署即可。这正是 deploy:check 系列任务存在的意义——先检查、后发布。

5.4 SSH 连接参数与服务器/角色定义

stage 文件的核心是服务器清单,两种等价写法:

# server 写法(推荐,逐台声明属性)
server "web1.local", user: "admin", roles: %w{app web}
server "web2.local", user: "admin", roles: %w{app web}

# role 写法(按角色聚组)
role :web, %w{admin@web1.local admin@web2.local}
role :app, %w{admin@web1.local admin@web2.local}

常用 ssh_options(透传 Net::SSH):

set :ssh_options, {
  keys: %w(/root/.ssh/id_ed25519),   # 指定私钥(默认自动找 ~/.ssh 下常见密钥)
  forward_agent: false,              # agent 转发,目标机要再跳 clone 私有仓库时设 true
  auth_methods: %w(publickey password),
  verify_host_key: :never            # 首次连接不交互确认 host key;生产建议 :secure
}
  • verify_host_key::never(跳过校验,CI/内网演示方便)、:secure(严格校验 known_hosts,首次需手工 ssh 一次入库)、:accept_new_or_local_tunnel(自动接受新 key)。本文演示环境用 :never;有安全审计要求的环境用 :secure 并提前 ssh-keyscan 入库。
  • pty:远端需要 sudo 交互(sudo 默认 requiretty)时设 set :pty, true,其余场景保持 false。
  • 逐台覆盖:某台机特殊就在 server 行内嵌 ssh_options:,如 server "web2.local", user: "admin", roles: %w{app web}, ssh_options: { port: 2222 }。
  • primary:role :db, %w{admin@db.local}, primary: true 标主库(跑迁移等只一次的任务用 on roles(:db), primary: true)。

💡 技术拓展:角色(role)是 Capistrano 的多机调度核心——on roles(:app) 决定任务落在哪些机上。常见角色分工:app(应用进程)、web(静态/前端/反向代理上游)、db(跑数据库迁移的那台)。本文两台目标服务器同为 app+web;生产可按机器职责拆开,同名任务自动只落在对应角色主机上。

5.5 输出与日志参数

set :format, :airbrussh
set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto
set :log_level, :debug
  • command_output: true:把远端命令的 stdout/stderr 回显到终端(本文所有回显里的 01 ecddef79... HEAD 就是这么来的);
  • log_file:控制机本地留一份完整执行日志,实测 /root/demo-deploy/log/capistrano.log 逐条记录了每条命令的 DEBUG/INFO 行:
 DEBUG [42c03e23] Command: cd /var/www/demo-app/releases && /usr/bin/env echo "Branch master (at 0f135f586e7f5b1df8fbd7f7f7343471c0bcf313) deployed as release 20261027092158 by admin" >> /var/www/demo-app/revisions.log
  INFO [42c03e23] Finished in 0.007 seconds with exit status 0 (successful).
  • truncate: :auto:超长命令截断显示(日志文件里仍完整)。

5.6 Git 行为参数

set :git_shallow_clone, false               # true 时镜像仓库 --mirror --depth 1,省空间但不能切任意历史
set :git_max_concurrent_connections, 10     # 多机并发 git clone/update 的连接上限
set :git_wait_interval, 0                   # 每个 git 连接间隔秒数,保护脆弱的 git 服务时限流用
set :git_environmental_variables, {}        # 给远端 git 命令注入环境变量
set :git_wrapper_path, "/tmp/git-ssh-<随机>.sh"  # 自动生成的 git-ssh 包装脚本位置(tmp_dir 派生)

doctor 实测值见 3.2 回显。包装脚本的作用是屏蔽 git 的交互式询问(git_askpass=/bin/echo),避免部署卡在密码提示上。

5.7 自定义任务与部署钩子

Capistrano 任务是 Rake 任务 + SSHKit DSL。在 lib/capistrano/tasks/app.rake 写自定义任务(本文实测文件):

namespace :app do
  desc "Restart demo app (write restart timestamp into shared/log)"
  task :restart do
    on roles(:app) do
      execute :echo, "\"restarted at \$(date +%s)\" >> #{shared_path}/log/restart.log"
    end
  end

  desc "Show current release revision"
  task :show_revision do
    on roles(:app) do
      info capture(:cat, "#{current_path}/REVISION")
    end
  end
end
after "deploy:published", "app:restart"

DSL 要点:on roles(:app) 选定目标机;execute 远端执行命令;capture 执行并取回 stdout;info 打印日志;shared_path/current_path/release_path 为内置路径变量。after "deploy:published", "app:restart" 把重启挂进部署流程。

部署主流程的钩子顺序(cap -T 可见全部内置任务):deploy:starting → started → updating → updated → publishing → published → finishing → finished,可在任意节点 before/after 挂自定义任务。

单独调用自定义任务实测:

cap production app:restart
cap production app:show_revision

实测回显:

00:00 app:restart
      01 echo "restarted at $(date +%s)" >> /var/www/demo-app/shared/log/restart.log
    ✔ 01 admin@web2.local 0.109s
    ✔ 01 admin@web1.local 0.115s
===
00:00 app:show_revision
      e417cb264e541ce26b0a2d667ece77e71a2efa15
      e417cb264e541ce26b0a2d667ece77e71a2efa15

web1 上验证重启日志真实写入:cat /var/www/demo-app/shared/log/restart.log → restarted at 1793092682。

lock "~> 3.20.1"(deploy.rb 顶部):cap 启动时校验运行版本在此区间内,不符直接报错退出,防止团队混用 3.19/3.20 行为差异。

六、生产配置模板(本文实测全套)

适用场景:16GB 内存控制机、目标服务器若干(同构)、内网/公网 SSH 可达、git 仓库目标机可达、保留 3 份历史 release 的中小型 Web 应用发布。

config/deploy.rb:

# config valid for current version and patch releases of Capistrano
lock "~> 3.20.1"

set :application, "demo-app"
set :repo_url, "http://controller.local:8000/demo-app.git"
set :branch, "master"
set :deploy_to, "/var/www/demo-app"
set :format, :airbrussh
set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto
append :linked_dirs, "log"
append :linked_files, "config/app.conf"
set :keep_releases, 3
set :local_user, "admin"
set :ssh_options, { verify_host_key: :never }

config/deploy/production.rb:

server "web1.local", user: "admin", roles: %w{app web}
server "web2.local", user: "admin", roles: %w{app web}
set :stage, :production

关键取舍解读:公开仓库协议按目标机可达性选(本文 HTTP 内网裸仓库;私有仓库换 SSH 协议+部署密钥);verify_host_key 生产改 :secure;keep_releases 3 份兼顾回滚空间与磁盘;local_user 显式设置保证审计日志署名(原因见七、Q1)。多 stage 时复制 production.rb 为 staging.rb,改服务器清单即可,cap staging deploy 独立发布。

七、常见问题

Q1:revisions.log 里部署者署名是空的(deployed as release ... by 后面没人名)?

原因:local_user 默认取 ENV['USER'],控制机以非登录 shell(cron、CI agent)运行时该变量为空。实测修复前后对比(revisions.log 末两行):

Branch master (at e417cb264e541ce26b0a2d667ece77e71a2efa15) deployed as release 20261027091726 by 
Branch master (at 0f135f586e7f5b1df8fbd7f7f7343471c0bcf313) deployed as release 20261027092158 by admin

修复:deploy.rb 加 set :local_user, "admin"(或 -> { \git config user.name`.chomp }`)。

Q2:linked_files 报错 ERROR linked file ... does not exist on <host>?

部署在 deploy:check:linked_files 阶段中止。逐台补建 shared 下的实体文件(见 5.3 排障实录),或先在目标机手工 mkdir -p + 落文件再部署。可用 cap production deploy:check 在正式发布前预检。

Q3:首次部署卡在 host key 确认或直接连接失败?

verify_host_key 默认策略要求 known_hosts 已有目标机记录。内网演示设 :never;生产推荐 :secure 并预先用 ssh-keyscan web1.local >> /root/.ssh/known_hosts 入库。

Q4:远端命令需要 sudo,tty 报错 sudo: sorry, you must have a tty to run sudo?

设 set :pty, true 分配伪终端;更彻底的做法是给部署账号配置免密 sudo 白名单(/etc/sudoers.d/deploy),避免交互。

Q5:多 stage 怎么组织?

cap install STAGES=staging,production 一次生成多个 stage 文件;公共参数留在 deploy.rb,stage 差异(服务器、分支、deploy_to)写在各自 stage 文件里覆盖。调用时 cap staging deploy / cap production deploy。

八、参考链接