===================================================================== GWS BACKUP — ENTERPRISE EDITION cPanel / Shared Hosting Installation Guide by KIKO Solutions — https://gwsbackup.com ===================================================================== Thank you for downloading GWS Backup. This guide walks you through installing the app on a cPanel server. The same steps apply to most Passenger-based Node.js hosting. Full illustrated guide + FAQ: https://gwsbackup.com/install.html Questions: admin@gwsbackup.com --------------------------------------------------------------------- 1. BEFORE YOU START — WHAT YOU NEED --------------------------------------------------------------------- * A cPanel account with "Setup Node.js App" (CloudLinux Node.js Selector / Phusion Passenger). Most quality shared hosts have it. * Node.js 22.5 or newer available in the selector. This is REQUIRED — GWS Backup uses the SQLite engine built into Node 22. The app will not start on Node 20 or older. * PHP with the ZipArchive extension (standard on cPanel) — used by the one-command update script. * Outbound HTTPS access (the server must reach Google's APIs and the GWS Backup licence server). * YOUR FREE LICENCE KEY. The software will not run without one. Keys are free, and for now they are issued personally — email admin@gwsbackup.com to request yours (usually answered within 1-2 days). Automatic key delivery by email is planned for the near future. One key = one running installation at a time. Disk / memory: the app itself is ~100 MB. Allow roughly 50 MB RAM per client workspace you back up. Backup data itself is stored in your own Google Drive / S3 / Azure / local destination — not on the web server. --------------------------------------------------------------------- 2. SECURITY FIRST — WHERE TO INSTALL (PLEASE READ) --------------------------------------------------------------------- ► NEVER install GWS Backup inside public_html. The application folder contains your encrypted credentials database (gws-backup.db), your .env encryption keys and your recovery keys. If those files live under public_html, a misconfiguration could let anyone on the internet download them. The app is served by Node.js/Passenger — it does not need to be in your web root, and it must not be. Correct: /home/youruser/gwsbackup <-- app root Wrong: /home/youruser/public_html/backup <-- never do this ► Use a RANDOM subdomain name. Create a subdomain that cannot be guessed, e.g.: vault-k7x93q.yourdomain.com console-2f8zt.yourdomain.com Do NOT use obvious names like backup.yourdomain.com or gws.yourdomain.com. Bots constantly scan common subdomain names looking for admin panels. A random name keeps automated scanners and casual snoopers away from your login page. Don't link to it from anywhere public. ► Always enable HTTPS (run AutoSSL on the new subdomain before you first log in). --------------------------------------------------------------------- 3. FRESH INSTALLATION (cPanel) --------------------------------------------------------------------- STEP 1 — Create the subdomain cPanel → Domains → Create a New Domain (or "Subdomains"). * Name: something random, e.g. vault-k7x93q.yourdomain.com * Document root: a folder OUTSIDE public_html, e.g. /home/youruser/gwsbackup_web Then run SSL/TLS Status → AutoSSL so the subdomain gets a certificate. STEP 2 — Create the Node.js application cPanel → Software → Setup Node.js App → Create Application: * Node.js version: 22.x (or newer) * Application mode: Production * Application root: gwsbackup (folder in your home dir, NOT inside public_html) * Application URL: the random subdomain from Step 1 * Application startup file: app.js Click CREATE, then STOP the app for now. STEP 3 — Upload the application files cPanel → File Manager → open the application root folder (e.g. /home/youruser/gwsbackup): * Upload update.zip (from this download) into that folder. * Also upload update.php. * Open "Terminal" in cPanel (or SSH), then: cd ~/gwsbackup php update.php The wizard extracts the application and then deletes update.php and update.zip automatically. (Alternative without a terminal: use File Manager's "Extract" on update.zip directly in the application root, then delete update.zip and update.php manually.) STEP 4 — Set the application URL variable Back in Setup Node.js App, add an environment variable: NEXTAUTH_URL = https://vault-k7x93q.yourdomain.com (your real subdomain, with https://). This is required for Google Drive OAuth callbacks to return to the right address. STEP 5 — Start the app In Setup Node.js App, click START (or RESTART). On first boot the app generates its own keys and writes them to .env in the app folder. Open that file and copy ENCRYPTION_KEY somewhere safe BEFORE you put any client data in — see section 7. STEP 6 — First-run setup in the browser Visit https://your-random-subdomain/ 1. You'll land on the Setup screen — create your administrator account and SAVE THE RECOVERY KEY it gives you somewhere safe (it is shown once). 2. You'll then be asked for your LICENCE KEY. Paste the key we emailed you. (No key yet? Email admin@gwsbackup.com — the app will wait on the activation screen until you have it.) The app stays locked until a valid key is activated, and validation needs an internet connection. 3. Follow the in-app Connection Wizard to link your first Google Workspace (service account + domain-wide delegation — the wizard gives you the exact scopes to paste). DONE. Set up your backup schedule and destination in Settings. --------------------------------------------------------------------- 4. UPDATING AN EXISTING INSTALLATION --------------------------------------------------------------------- 1. Download the latest release from gwsbackup.com. 2. Upload the new update.zip AND update.php into your existing application root (the folder with gws-backup.db in it). 3. In cPanel Terminal / SSH: cd ~/gwsbackup php update.php The script AUTOMATICALLY BACKS UP YOUR DATABASE first (to db_backups/backup_db_.zip), clears the old build, extracts the new one, and removes the update files. Your database, licence and settings are untouched. 4. Restart the app in Setup Node.js App. Housekeeping: once you've confirmed an update is stable, delete old archives from db_backups/ — they use your hosting quota. --------------------------------------------------------------------- 5. FILES YOU MUST PROTECT (AND BACK UP YOURSELF) --------------------------------------------------------------------- In your application root: * .env — contains ENCRYPTION_KEY. This is the master key to your BACKUPS, not just a setting: every email body, calendar and contacts backup is encrypted with it, as is every stored credential. Without it that data is permanently unreadable by any means. Copy it somewhere safe, off this server. Only ENCRYPTION_KEY is irreplaceable. NEXTAUTH_SECRET can be regenerated (it only signs everyone out), and DATABASE_URL, NEXTAUTH_URL, PORT and GWS_DEPLOYMENT are specific to THIS server — do not copy those onto a different one without checking them. * gws-backup.db — the application database (client configs, encrypted credentials, backup index). * db_backups/ — automatic pre-update database backups. If you ever migrate servers: move the whole application folder, keep the same .env, and remove any gws-backup.db-wal / gws-backup.db-shm files BEFORE first start on the new server. Remember: your licence key may only be live on one installation at a time — retire the old server when the new one goes live. --------------------------------------------------------------------- 5b. RUNNING MAINTENANCE SCRIPTS ON THE SERVER --------------------------------------------------------------------- Some tasks ship as .mjs scripts you run yourself (the database migrations, the backfills). They need the SAME Node.js that runs the application — cPanel installs it in a virtual environment, so a plain `node` in a terminal usually gives: command node not found This is normal and does not mean Node is missing. Which fix applies depends on which tool your cPanel has. Both exist in the wild and they put Node in different places: IF YOU HAVE "Setup Node.js App" (CloudLinux Node.js Selector) It runs each app in a virtual environment. In cPanel -> Software -> Setup Node.js App, copy the command shown next to "Enter to the virtual environment". It looks like: source /home/YOURUSER/nodevenv/gwsbackup/22/bin/activate && cd /home/YOURUSER/gwsbackup Paste that into the terminal. `node` then works for that session. IF YOU HAVE "Application Manager" (cPanel's own, EasyApache) There is no virtual environment to enter — Node is installed system-wide by EasyApache and simply is not on your PATH. Find it: ls -d /opt/cpanel/ea-nodejs*/bin/node \ /opt/alt/alt-nodejs*/root/usr/bin/node \ /usr/local/bin/node /usr/bin/node 2>/dev/null Then use that full path in place of `node`, e.g. cd ~/gwsbackup /opt/cpanel/ea-nodejs22/bin/node migrate-index-integer-ids.mjs Nothing needs enabling in WHM. The application is already running on one of these interpreters — if it starts at all, a suitable Node exists on the machine. IF NEITHER LISTS ANYTHING — ask the running app ps -u $USER -o pid,args | grep -i node The path in that output is the exact interpreter your application was launched with, which is always the right one to use. CHECK IT IS THE RIGHT NODE node --version # must be 22.5 or newer node -e "require('node:sqlite'); console.log('sqlite ok')" The application and these scripts both use Node's built-in SQLite, which does not exist before 22.5. If `node --version` shows an older release you are on a different Node than the app uses — go back and pick the interpreter the application itself runs on. ALWAYS STOP THE APP FIRST for anything that writes to the database. It holds gws-backup.db (and gws-backup-archive.db, and every file under clients/) open. --------------------------------------------------------------- 5d. SPLITTING THE INDEX INTO ONE DATABASE PER CLIENT --------------------------------------------------------------- The backup index is the overwhelming majority of the database — 826,119 of ~838,000 rows on a five-client install. Giving each client their own file bounds their growth, makes their data portable, and takes VACUUM and backup windows from whole-estate to per-client. THE ORDER IS NOT OPTIONAL. Run them in this sequence: 1. node migrate-index-integer-ids.mjs --apply 2. node split-client-databases.mjs --apply Step 1 converts the index key from a UUID to an integer and remaps the restore ledgers — it can only do that while every row is still in one file. If you have run it before it prints "already migrated — skipping" and changes nothing, so it is always safe to run. Step 2 refuses outright if step 1 has not been done, and says so. Both are dry runs unless you pass --apply. Both copy, verify row counts and a content checksum, and only then delete anything. If a run is interrupted, run it again — a client whose file already verifies is skipped rather than re-copied. BEFORE YOU START - Stop the app. It holds every one of these files open. - Copy gws-backup.db and gws-backup-archive.db somewhere safe, or take a console database backup from the Settings screen. That copy IS the rollback. - Free disk: roughly 2x the current database total. The client files are built while the originals still exist. HOW LONG About a minute per 800,000 index rows on a modest VPS, plus the VACUUM at the end. A five-client, 826,000-row install took under a minute in rehearsal. ROLLBACK Nothing is deleted until every client has been copied AND verified, so a failure before that point leaves the install exactly as it was — just start the app again. After that point: put the saved gws-backup.db and gws-backup-archive.db back, delete the clients/ directory, and start the app. The old file still says indexSplitVersion = 0, so the app returns to reading the single shared index. AFTERWARDS, CHECK - The Dashboard shows the same holdings as before. - Open a client and browse a restore — including one whose data is on a destination you have since switched away from. - Run ONE backup by hand before re-enabling schedules. AFTERWARDS the install looks like this: gws-backup.db the console: settings, users, clients, schedules, job log, audit log clients/.db one per client: that client's entire backup index gws-backup-archive.db emptied; keep until satisfied, then delete EVERY ONE OF THESE FILES MUST TRAVEL TOGETHER. update.php zips them all, the console's own database backup copies them all, and restore-console-db.mjs restores them all. A copy of gws-backup.db on its own is the settings and none of the backups. --------------------------------------------------------------------- 5c. RESTORING THE CONSOLE'S OWN DATABASE --------------------------------------------------------------------- The console can back ITSELF up (Settings -> Database Optimization). Each backup is TWO files with the same timestamp, stored at your backup destination under _gws-console-database/ : gws-console-.db the working database gws-console--archive.db the frozen index archive gws-console-.txt a manifest describing both They are one database in two files. Restoring only the .db loses the restore-only history of any destination you have switched away from, which includes items that exist nowhere else. TO RESTORE 1. Stop the console. 2. Download both files for the timestamp you want, into the app folder. (S3/R2: your provider's console. Google Drive: the _gws-console-database folder inside your backup folder.) 3. CHECK THE COPY BEFORE YOU TRUST IT: node restore-console-db.mjs --file gws-console-.db \ --archive gws-console--archive.db This verifies both files and — the part that matters — proves the credentials inside decrypt with the ENCRYPTION_KEY in this server's .env. Restore with the wrong key and the console comes up looking completely healthy, with every client and setting present, while not one connection works and nothing explains why. The check takes seconds and rules that out. 4. If everything passes, run it again with --install. It moves the current database files aside (.replaced-) rather than overwriting them, and removes the stale -wal / -shm sidecars that belong to the database being replaced. 5. Start the console. RESTORING ONTO A DIFFERENT SERVER Bring the ENCRYPTION_KEY with you. Everything else in .env can be regenerated; that one cannot, and without it the stored credentials are unrecoverable by any means. Set it in the new server's .env BEFORE running the check in step 3. --------------------------------------------------------------------- 6. TROUBLESHOOTING --------------------------------------------------------------------- * App won't start / crash-log.txt mentions "node:sqlite" → Your Node version is too old. Select Node 22+ in Setup Node.js App. * "Failed to connect to the License Telemetry Server" → The server has no outbound internet access, or a firewall is blocking HTTPS. Licence validation requires it. * Login page loads without styling / 404s on assets → The app was extracted into the wrong folder, or the application root doesn't match the folder you uploaded to. * Google Drive connection redirects to the wrong address → NEXTAUTH_URL is missing or wrong (Step 4 above). Still stuck? https://gwsbackup.com/faq.html Or email admin@gwsbackup.com (allow 1–2 days for free edition support) --------------------------------------------------------------------- LICENCE --------------------------------------------------------------------- GWS Backup is free to use under the GWS Backup Free Licence (https://gwsbackup.com/licence.html). It is not open source and may not be rebranded, resold or redistributed. One licence key permits one running installation at a time. Key usage is monitored; abused keys can be withdrawn. (c) KIKO Solutions Ltd — https://gwsbackup.com =====================================================================