Skip to content

Deploying the backend & agent

One script, run from your Mac: bz-backend/scripts/deploy.gcp.sh.

Commands

bash
cd bz-backend

./scripts/deploy.gcp.sh deploy bz-backend              # sync + install + build + restart + health
./scripts/deploy.gcp.sh deploy bz-backend no-install   # skip npm install (code-only change)
./scripts/deploy.gcp.sh push   bz-agent                # force rsync even if the VM dir is a repo
./scripts/deploy.gcp.sh pull   bz-backend              # git pull ON the VM (repo mode only)
./scripts/deploy.gcp.sh restart bz-tokens
./scripts/deploy.gcp.sh logs   bz-backend 200
./scripts/deploy.gcp.sh status                         # pm2 table + all four ports
./scripts/deploy.gcp.sh ssh

Apps: bz-backend · bz-agent · bz-tokens · qube-backend.

Deploy modes

deploy checks whether the VM's app directory is a git repo and picks a mode:

is_git_repo?  →  yes  →  git fetch + checkout + reset --hard origin/$BRANCH + clean -fd
              →  no   →  rsync from your local checkout

As of 2026-07, bz-backend on the VM is not a git repo, so it uses rsync.

What rsync copies

bash
rsync -az \
  --exclude='.git/' --exclude='node_modules/' --exclude='.env' \
  --exclude='dist/' --exclude='build/' --exclude='logs/' \
  --exclude='.DS_Store' --exclude='*.log' \
  "$LOCAL_DIR/" "$VM_USER@$VM_IP:$VM_DIR/"

Three consequences of rsync mode

  1. Uncommitted local work goes to production. Your working tree is the deploy artefact.
  2. There is no --delete. Files you removed locally persist on the VM. So does anything you copied there once and forgot.
  3. Untracked scratch files ship too — debug scripts, notes, anything not matching an exclude.

archive/, new-backend/, microservices-backend/ and gateway/ all ship on every deploy. They are not mounted, but they are there.

The fix is one-off

Clone the repo on the VM with a PAT. deploy then auto-switches to git-pull mode for that app with no script edit — is_git_repo does the detection. That removes the whole class of problem.

Git-pull mode

bash
git merge --abort  || true
git rebase --abort || true
git fetch origin $BRANCH
git checkout $BRANCH
git reset --hard origin/$BRANCH
git clean -fd -e .env -e node_modules

A hard reset. Anything edited directly on the VM is discarded — which is the point.

The install step

Unless you pass no-install:

bash
rm -rf node_modules/swap-sdk          # so npm re-fetches the tagged version
npm install --no-audit --no-fund
node -p "require('./node_modules/swap-sdk/package.json').version"   # printed

rm -rf node_modules/swap-sdk is not sufficient on its own

package-lock.json pins swap-sdk by resolved commit. npm install obeys the lock and reinstalls the old commit even after the directory is deleted.

You must have already run, and committed:

bash
npm install github:blazpay/swap-sdk#vX.Y.Z --package-lock-only

This exact trap hit production on 2026-07-26: the deploy installed 1.9.1 despite a v1.12.0 pin. The script prints the installed version — read that line.

Build

AppBUILDWhat runs
bz-backendnoNothing — it is plain Node ESM
bz-agentyesnpm run build (tsc → build/)
bz-tokensyesnpm run build
qube-backendno

Restart

bash
pm2 restart '$PM2_NAME' --update-env
pm2 save

Hard restart, never reload — and this is deliberate

swap-sdk is a git-tagged dependency. pm2 reload keeps the parent process and its require cache alive, so an upgraded SDK is silently ignored. pm2 restart recycles the process tree so new module code loads.

--update-env picks up .env changes without a separate step.

Health check

bash
sleep 6
ss -tlnp | grep ':$PORT '        # is it listening?
pm2 logs $PM2_NAME --lines 12 --nostream

Exits non-zero if the port is not bound. Read the twelve log lines — a process that binds the port can still be throwing on every request.

Environment variables

.env is excluded from the rsync, deliberately

A new environment variable does not travel with the code. Add it on the VM manually:

bash
./scripts/deploy.gcp.sh ssh
nano ~/bz-backend/.env
pm2 restart bz-backend --update-env

"The code deployed but the feature does not work" is almost always this. UNISWAP_API_KEY is the canonical case — the provider integration shipped and silently produced no routes.

Full list: environment variables.

Standard deploy

bash
# 1. commit on bishtNew
cd bz-backend
git add -A && git commit -m "…" && git push origin bishtNew

# 2. if swap-sdk changed, bump the pin AND the lock (see above)

# 3. add any new env vars on the VM FIRST

# 4. deploy
./scripts/deploy.gcp.sh deploy bz-backend

# 5. verify
./scripts/deploy.gcp.sh status
./scripts/deploy.gcp.sh logs bz-backend 100

Step 3 before step 4

A restart with a missing variable means a broken feature until you notice and restart again.

Code-only change

bash
./scripts/deploy.gcp.sh deploy bz-backend no-install

Skips npm install. Faster, and safe when package.json did not change.

Deploying the agent

bash
./scripts/deploy.gcp.sh deploy bz-agent

Compiles on the VM (BUILD=yes). Verify types locally first:

bash
cd bz-agent && npm run check-types

bz-agent is the safest thing to deploy

It has no chain access, no keys that move funds, and nothing depends on it except the chat path. A bad agent deploy degrades classification; it cannot lose money.

defi-set/restart_agent.sh exists as a convenience wrapper.

After a deploy

□ ./scripts/deploy.gcp.sh status                  — all four ports bound
□ Installed swap-sdk version matches the pin (the script prints it)
□ logs show no repeated errors
□ A quote request returns routes
□ Crons ticking (grep the logs for cron activity)
□ Executor wallet has gas
□ If a contract was involved: fee accruing (/defi/fees/summary?force=1)

Rollback

There is no automated rollback.

ModeRollback
rsyncgit checkout <previous> locally, deploy again
git-pullBRANCH is hardcoded per app; reset the branch, or SSH and git reset --hard <sha>
Env varEdit .env on the VM, pm2 restart --update-env
swap-sdkBump the pin and lock back, deploy

Contract upgrades do not roll back this way

A UUPS upgrade is a chain transaction. Rolling back means deploying the previous implementation and upgrading to it — and the storage layout must still be compatible. See contracts & upgrades.

Logs

bash
./scripts/deploy.gcp.sh logs bz-backend 500

On the VM: /home/bisht/.pm2/logs/bz-backend-{error,out}.log, merged, timestamped.

Every request logs [timestamp] METHOD url - IP: … - Origin: …. Useful greps:

GrepFinds
/defi/quotesQuote requests and per-aggregator failures
Route viaExecution-engine route rejections
Not swap calldataCalldata-guard hits
delivers no in-tx outputProbe rejections (solver providers)
error creating recordDCA creation failures
A wallet addressThat user's whole session

Deprecated scripts

Still present. Do not run them.

ScriptWas
scripts/deploy.shAzure 20.198.176.70
scripts/deploy.kamatera.shKamatera 194.113.195.167

Internal + developer documentation. Usage figures are point-in-time snapshots — re-verify before publishing them.