App Server Setup
The rs-app.richardkentgates.com server hosts the PWA web app, versioned app snapshots, Linux desktop packages (.deb), and the deploy webhook used by GitHub Actions CI. This page documents how to rebuild it from scratch on a fresh VPS.
What the server hosts
| URL path | What it serves |
|---|---|
/ | PWA web app (current version) — app.js, app.css, index.html, etc. |
/1.x.y/ | Versioned app snapshots — older plugin versions load their matching app here |
/desktop/ | Linux .deb packages (amd64 + arm64) and Tauri update.json manifest |
/_deploy/ | PHP webhook called by CI on every release tag to trigger a web app update |
Architecture
The deployment flow on every version tag:
- GitHub Actions builds the plugin ZIP and two
.debpackages (amd64, arm64). - The
build-desktopCI job pushes the.debfiles directly to the server via SSH (scp) and writesdesktop/update.json. - The
buildCI job commits versioned app snapshots todocs/app/<version>/on themainbranch. - The
ping-deployCI job callsPOST /_deploy/with a secret token. The PHP webhook verifies the token and fires/usr/local/bin/rsa-app-updateasynchronously. - The update script sparse-clones
docs/app/from the latest GitHub release tag and rsyncs it to/var/www/rs-app/.
Server spec
| Property | Value |
|---|---|
| Provider | Google Cloud Platform (Compute Engine) |
| Machine type | e2-micro (or equivalent — 1 vCPU, 1 GB RAM) |
| OS | Debian 12 (bookworm) |
| IP | 104.197.231.120 |
| OS user | richardkentgates |
| Web root | /var/www/rs-app/ |
| Web server | Apache 2.4 + PHP 8.2 (mod_php) |
| TLS | Let's Encrypt (certbot, auto-renews) |
Required GitHub secrets
These must be set at Repository → Settings → Secrets and variables → Actions before CI can deploy to the server.
| Secret name | What it is | Where it comes from |
|---|---|---|
APP_SERVER_SSH_KEY | ED25519 private key for SSH access to richardkentgates@<server> | Printed by the setup script |
DEPLOY_WEBHOOK_TOKEN | 32-byte hex token verified by the /_deploy/ webhook | Contents of /etc/rsa-webhook-token on the server — printed by the setup script |
TAURI_SIGNING_PRIVATE_KEY | Tauri code-signing key for .deb update signatures | Existing secret — carry over from the old repo/server |
Recovery procedure
Follow these steps in order to bring a replacement server online.
Step 1 — Provision the VPS
Create a new Debian 12 instance on Google Cloud (or any provider). Minimum spec: 1 vCPU, 1 GB RAM, 10 GB disk. Note the external IP address.
e2-micro qualifies for the free tier in us-central1.
Choose Debian 12 as the boot disk. Allow HTTP and HTTPS traffic in the firewall settings.
Step 2 — Create the OS user
The setup script will create the richardkentgates user if it doesn't exist, but you need to SSH in first. On Google Cloud the default SSH user depends on your Google account; run as root or via sudo:
# On the new server — create the user if it doesn't exist yet
sudo adduser --disabled-password --gecos "" richardkentgates
sudo usermod -aG sudo richardkentgates
Step 3 — Point DNS to the new server
Update the A record for rs-app.richardkentgates.com to the new server's IP address. Let's Encrypt will fail if DNS does not resolve correctly.
Verify propagation before continuing:
dig +short rs-app.richardkentgates.com
# should return the new server IP
Step 4 — Clone the repo and run the setup script
SSH into the new server as root (or a sudo-capable user), then:
git clone https://github.com/richardkentgates/rich-statistics.git
cd rich-statistics
sudo bash bin/setup-app-server.sh --email you@example.com
The script accepts the following options:
| Option | Default | Description |
|---|---|---|
--domain | rs-app.richardkentgates.com | FQDN to configure Apache and certbot for |
--email | required for SSL | Let's Encrypt registration address |
--user | richardkentgates | OS user who owns /var/www/rs-app |
--skip-ssl | off | Skip certbot — use this if DNS isn't ready yet; run sudo certbot --apache -d <domain> later |
--skip-deploy | off | Skip the initial rsa-app-update run |
The script will:
- Install Apache 2.4, PHP 8.2, certbot, git, rsync, fail2ban, ufw
- Enable Apache modules:
rewrite,headers,ssl,php8.2 - Create
/var/www/rs-app/with the correct ownership and subdirectory structure - Deploy the webhook handler to
/var/www/rs-app/_deploy/index.php - Generate a random 32-byte deploy token and store it at
/etc/rsa-webhook-token - Install the update script to
/usr/local/bin/rsa-app-update - Configure sudoers so
www-datacan run the update script - Write the Apache virtual-host config and obtain a Let's Encrypt certificate
- Generate a fresh ED25519 SSH keypair for CI and add the public key to
~/.ssh/authorized_keys - Run an initial deploy to pull the current app files from GitHub
- Print the
DEPLOY_WEBHOOK_TOKENandAPP_SERVER_SSH_KEYvalues for GitHub
Step 5 — Update GitHub secrets
At the end of the setup script, two secret values are printed to stdout. Copy them into GitHub:
Repository → Settings → Secrets and variables → Actions
- Update (or create)
APP_SERVER_SSH_KEYwith the new ED25519 private key. - Update (or create)
DEPLOY_WEBHOOK_TOKENwith the token string.
Step 6 — Verify the server
# App is reachable over HTTPS
curl -I https://rs-app.richardkentgates.com/
# .deb files are accessible
curl -I https://rs-app.richardkentgates.com/desktop/rich-statistics-linux-amd64.deb
# Tauri update manifest is valid JSON
curl -s https://rs-app.richardkentgates.com/desktop/update.json | python3 -m json.tool
# Webhook returns 405 on GET (method not allowed — correct)
curl -I https://rs-app.richardkentgates.com/_deploy/
Step 7 — Trigger a re-release (optional)
If you want to push the current version's .deb files and run the full CI pipeline against the new server, re-push the latest tag:
git fetch --tags
LATEST=$(git describe --tags $(git rev-list --tags --max-count=1))
git tag -d "${LATEST}" && git push origin ":refs/tags/${LATEST}"
git tag "${LATEST}" && git push origin "${LATEST}"
build-release workflow, which builds the plugin ZIP, both .deb packages, and creates a new GitHub Release. Only do this if you actually need fresh .deb files or want to test the new server end-to-end.
File layout on the server
/var/www/rs-app/
├── .deployed-version # tag of the last successful deploy, e.g. "v1.4.2"
├── app.js # current PWA JavaScript
├── app.css # current PWA styles
├── index.html # PWA entry point
├── config.js # PWA configuration (site URL list is user-specific)
├── sw.js # service worker
├── manifest.json # PWA manifest
├── chart.min.js # bundled Chart.js 4.x
├── versions.json # ordered list of all deployed version tags
├── icons/ # PWA icon set
├── 1.3.0/ # versioned snapshot (immutable, 1-year cache)
├── 1.4.0/ # …
├── 1.4.1/
├── 1.4.2/
├── desktop/
│ ├── rich-statistics-linux-amd64.deb
│ ├── rich-statistics-linux-amd64.deb.sig
│ ├── rich-statistics-linux-arm64.deb
│ ├── rich-statistics-linux-arm64.deb.sig
│ └── update.json # Tauri auto-update manifest
└── _deploy/
└── index.php # deploy webhook (from bin/server-webhook.php)
/etc/rsa-webhook-token # shared secret (root:www-data, mode 640)
/etc/sudoers.d/rsa-app-update
/usr/local/bin/rsa-app-update # deploy script (from bin/server-update-webapp.sh)
/etc/letsencrypt/ # certbot managed — do not touch manually
/etc/apache2/sites-available/rs-app.conf
/etc/apache2/sites-available/rs-app-le-ssl.conf # written by certbot
/var/log/apache2/rs-app-*.log
/var/log/rsa-deploy.log # output of rsa-app-update runs
Troubleshooting
Webhook returns 401
The X-Deploy-Token header doesn't match /etc/rsa-webhook-token. Check that the DEPLOY_WEBHOOK_TOKEN GitHub secret matches the file exactly (no trailing newline).
cat /etc/rsa-webhook-token # value on the server (no newline after hex string)
Webhook returns 202 but app files don't update
The update script runs asynchronously — check the deploy log:
tail -50 /var/log/rsa-deploy.log
Common causes:
sudopermission missing — verify/etc/sudoers.d/rsa-app-updateexists and is440.gitorrsyncnot installed — runsudo apt-get install git rsync.- GitHub API rate limit — check
curl -sf https://api.github.com/repos/richardkentgates/rich-statistics/tags.
SSL certificate not renewing
Certbot installs a systemd timer that auto-renews. Check it:
sudo systemctl status certbot.timer
sudo certbot renew --dry-run
.deb files are 404
The build-desktop CI job uploads .deb files directly via SSH (not via the webhook). If the job failed, the files won't be on the server. Check the GitHub Actions run for the tag and re-run the build-desktop job if needed.
Manual app update
Re-run the update script at any time as the server user:
sudo -u richardkentgates /usr/local/bin/rsa-app-update
Source files in the repo
These files in the repository are the authoritative source for the server-side components. The setup script installs them to the right places — do not edit them directly on the server.
| Repo path | Installed to | Purpose |
|---|---|---|
bin/setup-app-server.sh | run once on new server | Full server provisioning script |
bin/server-webhook.php | /var/www/rs-app/_deploy/index.php | Deploy webhook handler |
bin/server-update-webapp.sh | /usr/local/bin/rsa-app-update | Web app update script (called by webhook) |