Deploy script — one command from anywhere
Source:
/usr/local/bin/deploy-wikion r-that.com Category: Snippet — deployment
Deploy script via ssh — one shell script on the VPS that does the whole deploy. From your laptop: ssh root@host deploy-wiki. Simple, reliable, scriptable, and serves as documentation.
#!/bin/bash# Deploy R-That Wiki to /var/www/wiki. Pulls, installs, builds, syncs.
set -euo pipefail
REPO_DIR=/opt/r-that-wikiWEB_DIR=/var/www/wiki
# First-run clone if the repo isn't there yetif [ ! -d "$REPO_DIR" ]; then git clone https://github.com/RogerSquare/r-that-wiki.git "$REPO_DIR"fi
cd "$REPO_DIR"git pull --ff-onlynpm ci --no-audit --no-fundnpm run buildrsync -a --delete dist/ "$WEB_DIR/"chown -R www-data:www-data "$WEB_DIR"
echo "Deployed: $(git rev-parse --short HEAD)"Install
Section titled “Install”sudo nano /usr/local/bin/deploy-wiki # paste the scriptsudo chmod +x /usr/local/bin/deploy-wikiUse from laptop
Section titled “Use from laptop”# Manual trigger
# With an alias (~/.ssh/config)# Host vps# HostName r-that.com# User root# Port 2200# Then:ssh vps deploy-wikiOutput includes the deployed commit sha — confirmation the right version is live.
Shape for multiple apps
Section titled “Shape for multiple apps”One script per app keeps things explicit:
/usr/local/bin/deploy-wiki # static site, rsync/usr/local/bin/deploy-portfolio # Node service, git pull + systemctl restart/usr/local/bin/deploy-gallery # app with DB migrations, different flowDon’t try to make one universal “deploy” script with flags — the differences between apps become hidden behind knobs you have to remember.
Variant: service-restart flavor (not rsync)
Section titled “Variant: service-restart flavor (not rsync)”For a Node service rather than static files:
#!/bin/bashset -euo pipefail
cd /opt/portfoliogit pull --ff-onlycd tsnpm ci --no-audit --no-fundsudo systemctl restart portfolio.service portfolio-web.serviceecho "Deployed: $(git rev-parse --short HEAD)"Skip the npm run build line — Cairn’s TS runs directly via tsx; no build step. Include or skip per-app.
Gotchas
Section titled “Gotchas”set -euo pipefailis non-optional. Without-e, a failedgit pulldoesn’t stop the script and younpm cion the wrong commit.-ucatches typos in variable names.-o pipefailcatches failures in pipelines.git pull --ff-only. Never accept a merge during a deploy. If history has diverged, the deploy aborts and you fix it manually — better than silently creating a merge commit in prod.npm ci, notnpm install.cirespects the lockfile exactly;installcan update it. Deploys should be deterministic.- Ownership after sync.
rsync -apreserves the local owner, which is usually your ssh user. Web servers needwww-dataor equivalent —chownafter the sync. - Running as root. Deploy script often runs as root. Dangerous if it has a bug. Drop privileges where possible:
sudo -u deploy-user. - Log where it ran. Echo the commit sha at the end; prepend timestamps if you redirect output.
deploy-wiki 2>&1 | tee -a /var/log/deploy.logfor an audit trail. - Idempotence. Running the script twice in a row should produce the same end state. Avoid anything that mutates state non-idempotently (e.g.
echo >> configwithout a dedupe check). - Env var compatibility. ssh commands don’t inherit your laptop’s
$PATHunless/usr/local/binis in the remote user’s default path.ssh host deploy-wikimay fail “command not found” if deploy-wiki isn’t in PATH for the ssh user’s shell. - Don’t auto-trigger from CI without review. A broken push to main triggers the deploy; bad code ships. Either require a manual trigger or add CI gating.
See also
Section titled “See also”- patterns/vps-deploy-runbook-shape — the doc this script implements
- patterns/rsync-atomic-swap-dist — upgrade path for zero-window deploys
- snippets/systemd-drop-in-env-var