Skip to content

Deployment Guide

WebMCP Auto-UI deployment relies on a centralized script (scripts/deploy.sh) that manages all seven monorepo apps with their specific paths and configurations. This guide covers the complete procedure, from local build to production verification.

graph LR
DEV[Local machine<br/>build + deploy.sh] -->|SSH + SCP| SERVER[demo.hyperskills.net]
subgraph SERVER["Production server"]
NGINX[nginx<br/>:80/:443] -->|proxy| FLEX[flex :3007]
NGINX -->|proxy| VIEWER[viewer :3008]
NGINX -->|proxy| RECIPES[recipes :3009]
NGINX -->|proxy| SHOWCASE[showcase :3010]
NGINX -->|proxy| BOILER[boilerplate :3011]
NGINX -->|static| HOME[home/]
NGINX -->|static| TODO[todo/]
end
ElementValue
Serverdemo.hyperskills.net (via SSH alias ssh bot)
Base path/opt/webmcp-demos/
Node.js appsflex (:3007), viewer, recipes, showcase, boilerplate
Static appshome, todo (served by nginx)
Reverse proxynginx on ports 80/443
Deployment pipeline: local repo to server via deploy.sh

Each app has a specific deployment path. The deploy.sh script knows these paths and handles them automatically.

AppTypeBuild dir (local)Deploy dest (server)ExecStartNotes
flexNode.js (SvelteKit)apps/flex/build//opt/webmcp-demos/flex/node index.jsMain composer
viewerNode.js (SvelteKit)apps/viewer/build//opt/webmcp-demos/viewer/node index.jsHyperSkills viewer
showcaseNode.js (SvelteKit)apps/showcase/build//opt/webmcp-demos/showcase/node index.jsWidget gallery
recipesNode.js (SvelteKit)apps/recipes/build//opt/webmcp-demos/recipes/node index.jsRecipe explorer
boilerplateNode.js (SvelteKit)apps/boilerplate/build//opt/webmcp-demos/boilerplate/node index.jsStarter template
homeStatic (SvelteKit)apps/home/build//opt/webmcp-demos/home/N/A (nginx)Homepage
todoStatic (SvelteKit)apps/todo/build//opt/webmcp-demos/todo/N/A (nginx)Todo demo

The script handles everything: package builds, app builds, cleanup, transfer, integrity verification, and service restart.

Terminal window
# Deploy all apps
./scripts/deploy.sh
# Deploy a specific app
./scripts/deploy.sh flex
# Deploy multiple apps
./scripts/deploy.sh flex viewer home
# Preview what would be deployed without making changes
./scripts/deploy.sh --dry-run
# Deploy with documentation update
./scripts/deploy.sh --with-docs
Terminal window
ssh bot
# Check Node.js services
systemctl status webmcp-flex
systemctl status webmcp-viewer
# Check static apps
curl -s -o /dev/null -w "%{http_code}" http://localhost/home/
# Check nginx
sudo systemctl status nginx

The deploy.sh script executes these steps in order:

flowchart TD
A[Sync versions<br/>across all workspaces] --> B[Build packages<br/>core → sdk → ui → agent]
B --> C{Apps to deploy?}
C -->|Each app| D[Back up app<br/>on server]
D --> E[Build app<br/>npm run build]
E --> F[Clean old files<br/>on server]
F --> G[Copy build<br/>via SCP]
G --> H{SHA-256<br/>check?}
H -->|Mismatch| I[Automatic rollback<br/>from backup]
H -->|OK| J[Restart service<br/>systemd]
J --> K[Final verification<br/>systemctl is-active]

After each transfer, the script compares the SHA-256 hash of the local file with the deployed file:

Terminal window
expected=$(sha256sum apps/flex/build/index.js | cut -d' ' -f1)
actual=$(ssh bot "sha256sum /opt/webmcp-demos/flex/index.js | cut -d' ' -f1")
if [ "$expected" != "$actual" ]; then
echo "INTEGRITY ERROR — sha256 mismatch, rolling back"
rollback_app "flex"
fi

This check catches incomplete transfers, read-only files, and stale builds.

Before each deployment, a full copy of the existing app is stored in /opt/webmcp-demos/.backups/. On failure, the script automatically restores the previous version.

Terminal window
# Manual rollback if needed
ssh bot
cp -a /opt/webmcp-demos/.backups/flex.prev /opt/webmcp-demos/flex
systemctl restart webmcp-flex

.env files must exist before the first deployment. They are created manually once and never deployed by the script.

The home app requires PUBLIC_BASE_URL at build time, not runtime:

Terminal window
# deploy.sh handles this automatically for static apps (home, todo)
PUBLIC_BASE_URL=https://demos.hyperskills.net npm run build

If you build manually (outside the script), remember this variable, otherwise internal links will point to localhost.

nginx serves as reverse proxy for Node.js apps and static file server for the rest:

# Node.js apps → proxy to local port
location /flex/ {
proxy_pass http://localhost:3007/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
}
# Static apps → serve from disk
location /home/ {
alias /opt/webmcp-demos/home/;
try_files $uri $uri/index.html =404;
}
# Default page = home
location / {
alias /opt/webmcp-demos/home/;
try_files $uri $uri/index.html =404;
}
Terminal window
ssh bot
# Follow logs in real time
journalctl -u webmcp-flex -f
# View the last 50 log entries
journalctl -u webmcp-flex -n 50 --no-pager
Terminal window
# Test all apps
for app in home flex viewer showcase recipes; do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://demos.hyperskills.net/$app/")
echo "$app: HTTP $code"
done
Terminal window
ssh bot
df -h # Disk space
free -h # Memory

Likely cause: orphaned JavaScript chunks. If you deployed manually (without the script), old .js files remain and the browser serves them from cache.

Solution:

Terminal window
ssh bot
# Clean old files
rm -rf /opt/webmcp-demos/flex/client /opt/webmcp-demos/flex/server
# Redeploy properly
./scripts/deploy.sh flex

Cause: .env files are not part of the deploy and must never be.

Solution: recreate the file manually on the server, then restart the service.

Diagnosis:

Terminal window
ssh bot
# Verify files exist
ls -la /opt/webmcp-demos/home/index.html
# Test nginx config
sudo nginx -t
# Check paths in config
grep -A 5 "location /home/" /etc/nginx/sites-available/default
# Reload after fix
sudo systemctl reload nginx

Symptom: 429 errors in flex logs.

Solutions:

  1. Wait (the client handles exponential backoff automatically)
  2. Reduce maxIterations in agent configuration
  3. Verify the API key is not shared across multiple deployments
Terminal window
ssh bot
# View detailed error
journalctl -u webmcp-flex -n 30 --no-pager
# Common causes:
# - Port already in use → lsof -i :3007
# - Missing .env → ls -la /opt/webmcp-demos/flex/.env
# - Missing npm deps → cd /opt/webmcp-demos/flex && npm install --production
Terminal window
ssh bot
sudo systemctl restart webmcp-flex webmcp-viewer webmcp-recipes webmcp-showcase nginx
Terminal window
ssh bot
journalctl --vacuum-time=7d # Keep 7 days
Terminal window
# Locally
npm update
npm run build
./scripts/deploy.sh

Before each deployment, verify:

  • Local build succeeds (npm run build without errors)
  • Tests pass (npm run test)
  • Code is committed and pushed
  • Using ./scripts/deploy.sh (never scp or rsync)
  • After deploy: check logs (journalctl -u webmcp-flex -n 20)
  • After deploy: test in browser
  • If deploying home: verify PUBLIC_BASE_URL is correct

If a deployment breaks something and the automatic rollback did not work:

Terminal window
# Option 1: Restore from backup
ssh bot
cp -a /opt/webmcp-demos/.backups/flex.prev /opt/webmcp-demos/flex
systemctl restart webmcp-flex
# Option 2: Redeploy an earlier version
git checkout <previous_commit>
./scripts/deploy.sh flex