SuperAPI AI Directory

deploy-server

Deployment & operations

View repository

Updated 10-15 20:12

About this skill

deploy-server incremental deployment runbook

Roll the run-related changes from the local repositoryonto an already-running production server in a controlled way:.claude/skills/deploy-server/deploy.sh A single command runs through diff → backup → sync → rebuild → conditional reload → verify. By default, first --dry-run read-only preview, then after manual confirmation --apply. Real assertions throughout (container status / peers ls / domain rendering), not relying on "it should deploy".

This is arelease/opsskill; it touches theonline running stack, and never clears data.

  • Is the cluster design correct (a one-off local test stack, discarded after use) → kubo-deploy-e2e。
  • Is the release chain correct (token write entry + publish.sh e2e) → kubo-publish-e2e。
  • Publish HTML/attachments as share links (publish content, do not touch infrastructure) → publish-artifact。

The real form of the production server: the remote directoryis not a git repository, but a subset of run files manually copied over. The container name is fixed as cl-caddy、cl-ipfs0/1/2、cl-cluster0/1/2、cl-cloudflared. This skill only doesincremental updates to an already-running server, not initial bring-up (for bring-up see docs/SINGLE_HOST_DEPLOYMENT.md)。

0. Prerequisite: fill in the skill's own .env

Skillself-contained, configuration lives in the skill directory's own .env,does not touchthe project root .env。

cd .claude/skills/deploy-server
cp .env.example .env      # 首次
$EDITOR .env              # 填服务器地址与凭据

Configuration resolution order:process environment variables take precedence → skill .env as fallback(in CI only environment variables can be used; locally no need to export every time). But .env must exist, otherwise the script stops and prompts cp .env.example .env。

Key Meaning Default
DEPLOY_SSH_HOST server IP/domain required
DEPLOY_SSH_USER login user root
DEPLOY_SSH_PORT SSH port 22
DEPLOY_REMOTE_DIR remote deployment directory /data/ipfs
DEPLOY_SSH_KEY private key path (optional, preferred) empty
DEPLOY_SSH_PASSWORD password (optional, fallback; leave empty if using a key) empty
IPFS_BASE_URL external read domain, for read-only rendering verification if empty, skip domain verification

Credentialsnever go into git:.claude/skills/**/.env is already blocked by the gitignore fallback. Only commit SKILL.md / deploy.sh / .env.example。

Connection method (auto-detected)

At the start of each run, the script prints the connection method used this time; detection order:

  1. Explicit DEPLOY_SSH_KEY → use that private key (-i <key> -o IdentitiesOnly=yes)。
  2. otherwise BatchMode=yes probe whether the default key / ssh-agent can connect without a password → if so, use it.
  3. Otherwise if there is DEPLOY_SSH_PASSWORD → sshpass -e(the password is passed via the $SSHPASS environment variable, not through the command line/logs). Missing sshpass will prompt brew install sshpass or switch to using a key.
  4. If none of these exist → error and exit.

All ssh/scp include -o StrictHostKeyChecking=accept-new(automatically trust the host key on first connection).

1. dry-run (default, read-only, never modifies anything)

.claude/skills/deploy-server/deploy.sh              # 等价 --dry-run
.claude/skills/deploy-server/deploy.sh --dry-run

Per-file local↔remote sha256 comparison, print:

  • Which files will be overwritten/added (~ changed / + added, with abbreviated local/remote sha).
  • Whether the compose file changed → make up-cloudflare whether services will be rebuilt as needed.
  • caddy/Caddyfile changed → whether apply needs caddy validate + reload。
  • Connection method for this run, and which verification assertions will be made after apply.

2. apply (executed after manual confirmation)

Once dry-run looks correct, execute:

.claude/skills/deploy-server/deploy.sh --apply

Do it in order,stop at the first failed step:

  1. Get remote timestamp date +%Y%m%d-%H%M%S, back up the files thatwill be overwrittento <REMOTE_DIR>/.backup/<ts>/(preserve directory structure,cp -p). Pure new files need no backup.

  2. scp Sync differing files (create remote directories first).

  3. After syncre-check with sha256local↔remote match one by one; stop on any mismatch.

  4. cd <REMOTE_DIR> && make up-cloudflare(= docker compose -f docker-compose.cluster.yml -f docker-compose.cloudflare.yml up -d, idempotent, only rebuild services whose command/config changed).

  5. Only when caddy/Caddyfile changedhandle Caddy. Key point:validate the new config before overwriting the mounted file—put the new Caddyfile docker cp into cl-caddy 's temporary path caddy validate,if it fails, abort and do not even overwrite the mounted file(otherwise, once a bad config lands on disk, any subsequent caddy restart/rebuild will load it → read path outage). After validation passes and the file is synced into place, because Caddyfile is a mounted file (in-container /etc/caddy/Caddyfile)、up -d will not rebuild because its content changed cl-caddy, then reload separately:

    # 覆盖前:在容器内校验尚未落盘的新配置
    docker cp <新Caddyfile> cl-caddy:/tmp/Caddyfile.new
    docker exec cl-caddy caddy validate --config /tmp/Caddyfile.new --adapter caddyfile
    # 通过并同步落盘后:热重载(reload 自身也会校验,坏配置会保留旧配置不生效)
    docker exec cl-caddy caddy reload  --config /etc/caddy/Caddyfile --adapter caddyfile
    
  6. Verify (see §3).

Sync whitelist (only touch runtime-related files)

docker-compose.cluster.yml  docker-compose.cloudflare.yml  docker-compose.node.yml
Makefile  caddy/**  scripts/**  skills/publish-artifact/**  e2e/**

Usewhitelistrather than blacklist: safest, and prefer using git ls-files enumeration (naturally excludes .env/runtime//fixtures). When new ops files need to be synced, manually add them to deploy.sh 's WHITELIST。

Never touch remote:.env、runtime/、.backup/、docs/、plans/、.git。

3. Verification (real assertions; read-only)

Automatically run at the end of apply; also the manual three steps for judging whether production is healthy:

# ① 容器状态:cl-* 应全部 Up/healthy
docker ps -a --filter name=cl- --format '{{.Names}} {{.Status}}'

# ② 集群成形:peers 数 = 集群节点数(脚本从本地 compose 推导期望值)
docker exec cl-cluster0 ipfs-cluster-ctl peers ls

# ③ 域名只读渲染(配了 IPFS_BASE_URL 才做):取一个真实已 pin CID,走对外域名应得 200 text/html
CID=$(docker exec cl-cluster0 ipfs-cluster-ctl pin ls | awk '{print $1}' | head -1)
curl -sS -o /dev/null -w '%{http_code} %{content_type}\n' "https://<IPFS_BASE_URL 的 host>/artifact/$CID/"

The cluster currently has no pinned CID, or this machine has no curl, or IPFS_BASE_URL is not configured → domain verification skipped (not counted as failure).

4. Failure handling and manual rollback

  • Stop at the first failed step, do not continue further. The scriptalways prints the backup directory path <REMOTE_DIR>/.backup/<ts>/。
  • Manual rollback(no --rollback subcommand in phase one): using the printed .backup/<ts>/ path, restore the whitelist files to their corresponding locations in <REMOTE_DIR> , then on the remote cd <REMOTE_DIR> && make up-cloudflare; if what was restored is Caddyfile, manually validate + reload。
  • When Caddyfile validation fails the script will not reload; the remote in-memory config is still the old one, so it is safe.

Not done (YAGNI)

  • Do not do the --rollback subcommand (phase one only guarantees the backup exists + prints the path; rollback is manual).
  • Do not touch remote .env / runtime/ data, do not do data backup (for data backup see docs/CAPABILITIES_AND_OPERATIONS.md)。
  • Do not do first bring-up (this skill is foralready runningserver performs incremental updates; for bring-up see docs/SINGLE_HOST_DEPLOYMENT.md)。
  • does not do multi-machine batch deployment (multi-machine goes via docs/MULTI_HOST_DEPLOYMENT.md)。

Minimal usage example

# 0) 首次填配置
cd .claude/skills/deploy-server && cp .env.example .env && $EDITOR .env

# 1) dry-run 看清将改什么、会不会 reload Caddy
./deploy.sh --dry-run
# 输出示例:
#   将覆盖/新增的文件(local / remote sha 缩略):
#     ~ caddy/Caddyfile        local:9f2a...  remote:1c04...
#   Caddyfile: 有变更 → apply 时将 caddy validate + caddy reload(校验不过则中止)
#   确认无误后执行: ./deploy.sh --apply

# 2) 人工确认无误后 apply(备份 → 同步 → 复核 → make up-cloudflare → Caddy reload → 验证)
./deploy.sh --apply
# 末尾打印:部署完成。备份路径: /data/ipfs/.backup/20260710-142530

Related:kubo-deploy-e2e(one-off local test stack) · kubo-publish-e2e(release pipeline e2e) · publish-artifact(publish content and get a share link) · docs/SINGLE_HOST_DEPLOYMENT.md · docs/MULTI_HOST_DEPLOYMENT.md · docs/CAPABILITIES_AND_OPERATIONS.md