# Runbook: Uuro staging server, Laravel 10 to Laravel 11 The checklist for upgrading the Uuro staging server. Work through it top to bottom and tick the boxes. The background, every question the script asks and the troubleshooting table are in [UPGRADE_LARAVEL_10_TO_11.md](UPGRADE_LARAVEL_10_TO_11.md). Timebank.cc runs Laravel 11 since 2026-10-15; Uuro is the last installation on Laravel 10. | | | |---|---| | From | branch `uuro`, Laravel 10.48 | | To | `origin/laravel-11`, Laravel 11.57, on branch `main` | | Tool | `scripts/upgrade.sh --leave-branch`, from the target branch, run from `/tmp` | | Duration | about 30 minutes; the site shows the maintenance page for 15 to 20 of them (search is rebuilt) | | Rollback | `bash storage/app/upgrades//upgrade.sh --rollback`: back to branch `uuro` and its files | **Why the branch goes.** Uuro ran a branch of its own with its config, logos, favicons and Open Graph tags. Those now live in the platform folder `platform/` (outside git, see `MULTI_PLATFORM_INSTANCES.md`, section 3), and the Open Graph tags and extra favicons are in the code for every platform. So the installation moves to `main`, and later upgrades are the same as for Timebank.cc. The upgrade cannot fast-forward the branch; `--leave-branch` moves the installation instead: - it lists the commits of `uuro` that will no longer be in the installation and keeps them in a tag `pre-leave-uuro-`; `origin/uuro` stays on Gitea as an archive; - it lists the tracked files the move deletes, among them `config/uuro.php`, and refuses to move while `platform/config.php` is missing; - it asks you to type `uuro`, then moves the installation to branch `main` at the target; - the rollback returns to branch `uuro` with all its files. Rehearsed on a copy of `origin/uuro` on 2026-10-19: the move, the site from the platform folder, the rollback. Start every terminal session by setting the application root once (the server's own path): ```bash export APP=/var/www/CHANGE-ME # the application root on this server ``` Every block below is read-only unless it says otherwise. --- ## T-1 day: preparation ### 1. Pick the moment - [ ] A quiet hour; the Uuro developer and someone who can test (log in, pay, chat) are available. ### 2. Check the server As root: ```bash ( cd "${APP:?set APP first}" || exit 1 echo "== versions"; php artisan --version; php -v | head -1; git branch --show-current echo "== tmux"; tmux -V 2>/dev/null || echo "tmux MISSING: apt-get install -y tmux" echo "== disk"; df -h . | tail -1; du -sh vendor echo "== local changes (a changed public/storage and public/build are normal)" git -c core.fileMode=false status --short --untracked-files=no echo "== commits not on Gitea (should be empty)" git fetch -q origin uuro && git log --oneline origin/uuro..HEAD echo "== .env keys" grep -E '^(APP_URL|DEPLOY_ENVIRONMENT|DEPLOY_APP_DIR|DEPLOY_SERVER_TYPE|DEPLOY_WS_URL|TIMEBANK_CONFIG|TIMEBANK_THEME|DB_DEPLOY_USERNAME|SCOUT_QUEUE_TIMEOUT|PLATFORM_PATH)=' .env echo "DB_DEPLOY_PASSWORD set: $(grep -c '^DB_DEPLOY_PASSWORD=.' .env)" echo "== apt (look for GPG or EXPKEYSIG errors)" apt-get update 2>&1 | grep -iE 'err|gpg|expkeysig' || echo "apt ok" echo "== worker units: folder and PHP must exist" for u in /etc/systemd/system/*.service; do grep -q "artisan" "$u" 2>/dev/null || continue wd=$(grep -oP '^WorkingDirectory=\K.*' "$u"); bin=$(grep -oP '^ExecStart=\K\S+' "$u") printf '%-45s %-28s %s %s\n' "$(basename "$u")" "$wd" "$([ -d "$wd" ] && echo ok || echo MISSING)" "$bin" done ) ``` - [ ] Laravel 10.x, branch `uuro`, PHP 8.2 or newer; tmux installed. - [ ] Free disk space at least twice `vendor/` plus 2 GB. - [ ] Local changes: only `public/storage` and `public/build` (and files under them). Anything else: find out why first. - [ ] No commits that are not on Gitea. If there are, push them to `origin/uuro` first: they stay in the archive. - [ ] `.env`: `TIMEBANK_CONFIG=uuro`, `TIMEBANK_THEME=uuro`, `DEPLOY_ENVIRONMENT=server` with the other `DEPLOY_*` keys; `DB_DEPLOY_PASSWORD set: 1` or the MySQL root password at hand; `SCOUT_QUEUE_TIMEOUT=1800` (add it if missing). No `PLATFORM_PATH`: the folder is `platform/` in the application root. - [ ] `apt ok`. An `EXPKEYSIG` for packages.sury.org: fix it first (guide, Troubleshooting, last row). - [ ] Every worker unit points at `$APP` and an existing PHP (on Timebank.cc four workers pointed at a removed folder for months). Fix a unit before the upgrade (backup first), then `systemctl daemon-reload`. If `git fetch` asks for the Gitea login every time, cache it for an hour: `git config --global credential.helper 'cache --timeout=3600'`. ### 3. Build the platform folder (creates `platform/`, changes nothing else) The move replaces or deletes the files of branch `uuro`, so first copy what makes Uuro Uuro into the platform folder. The old code ignores the folder; it is used from the moment the new code runs. ```bash ( cd "${APP:?set APP first}" || exit 1 [ ! -e platform ] || { echo "platform/ exists already: not touched"; exit 1; } mkdir -p platform/public platform/views/logos platform/lang cp config/uuro.php platform/config.php cp resources/views/logos/uuro.blade.php platform/views/logos/uuro.blade.php cp storage/app/public/app-images/app_mail_logo.png platform/public/mail-logo.png cp storage/app/public/app-images/app_mail_logo_inverted.png platform/public/mail-logo-inverted.png cp public/images/og-image.png platform/public/og-image.png for f in favicon.ico favicon.svg favicon-96x96.png apple-touch-icon.png web-app-manifest-192x192.png web-app-manifest-512x512.png; do [ -f "public/$f" ] && cp "public/$f" "platform/public/$f" done chown -R --reference=config platform find platform -type f | sort ) ``` - [ ] It lists `config.php`, `views/logos/uuro.blade.php` and `public/` with the mail logos, `og-image.png` and the favicons. ### 4. Dry run (changes nothing) ```bash tmux new -s upgrade ``` ```bash ( cd "${APP:?set APP first}" || exit 1 git fetch origin || { echo "git fetch failed"; exit 1; } git show origin/laravel-11:scripts/upgrade.sh > /tmp/timebank-upgrade.sh lines=$(wc -l < /tmp/timebank-upgrade.sh) [ "$lines" -gt 100 ] || { echo "upgrade.sh did not come through ($lines lines)"; exit 1; } bash /tmp/timebank-upgrade.sh --dry-run --leave-branch --target origin/laravel-11 ) ``` - [ ] `On branch 'uuro', which this upgrade leaves`; the list of commits that leave (37 on 2026-10-19, more if the branch got new ones); no warning about unpushed commits. - [ ] `Tracked files the move deletes` includes `config/uuro.php`, and the script does not refuse. - [ ] PHP accepted for the command line and the website; enough disk space; `Platform folder: ... (config.php lang public views)`. - [ ] `2. Plan`: `Leaves branch 'uuro' for main`, `Laravel major upgrade (10 -> 11)`, 6 new migrations that add tables only (polls). - [ ] Ends with `Dry run: nothing was changed`. If the dry run stops, fix the cause today, not on the day itself. --- ## Day of the upgrade ### 5. Start In tmux, the same block as in step 4 but without `--dry-run`, as a single line (the script asks questions): ```bash cd "$APP" && git fetch origin && git show origin/laravel-11:scripts/upgrade.sh > /tmp/timebank-upgrade.sh && bash /tmp/timebank-upgrade.sh --leave-branch --target origin/laravel-11 ``` Open a second tmux window now (`Ctrl-b c`, then `cd "$APP"`). Switch with `Ctrl-b n`. If the SSH connection drops, log in again and run `tmux attach -t upgrade`. ### 6. Work through the script - [ ] **2. Plan**: `Upgrade this installation to ?` → `y`; `Type the branch name 'uuro'` → `uuro`. - [ ] **3. Backup**: `Make a database backup now?` → `y`. Wait for `Backup verified`. - [ ] **4. Maintenance**: run the printed `php artisan down --retry=60 --secret=` in window 2 and **write down the secret**. Press Enter in window 1. - [ ] **5. Deploy**: `Left branch 'uuro' (tag pre-leave-uuro-); now on main.`, then `deploy.sh` runs: - `Would you like to merge new keys now? (y/N)` → `y`; then `yes` for each file. The new keys for Uuro go into `platform/config.php` (65 on 2026-10-19); the footer and other lists stay as they are. - MySQL user and password: only without `DB_DEPLOY_*` in `.env`. - If deploy.sh fails: the site stays in maintenance. See the guide, Troubleshooting. - After it: `public/storage linked to this server's storage.` - [ ] **6. Version check**: `Laravel Framework 11.x`. - [ ] **7. Restart**: run the printed restart commands in window 2 (check they report `active (running)`), then press Enter in window 1. ### 7. Test, while visitors still see the maintenance page Open `/` once in your browser. That browser now bypasses the maintenance page. - [ ] The site is in the Uuro theme, with Uuro's logo and favicon. - [ ] Log in; switch to an organisation profile and to a bank profile, and back. - [ ] Make a small payment, and check it in the transaction list. - [ ] Send a chat message with an image. - [ ] Search for a person and for a post (search was rebuilt during the deploy). - [ ] Open the admin pages: profiles, statistics, mailings. - [ ] Request a password reset for an admin or bank profile: it works now (on branch `uuro` it failed). - [ ] The page source has ``. - [ ] In window 2: ```bash php artisan upgrade:verify php artisan platform:check tail -n 50 storage/logs/laravel-$(date +%F).log ``` `upgrade:verify` reports `platform config: platform/config.php` and no failures; `platform:check` reports `Platform config: platform/config.php` and `linked at /platform` (the logo copied by hand gives an `i` line: fine). Good to know: queue workers do not run in maintenance mode, so like and star counters (updated by queued jobs) lag behind during this test. A count that looks one off after liking and unliking is that; it corrects itself once the site is live (seen on Timebank.cc on 2026-10-19). ### 8. Go or no-go | Go (`g`) if | No-go (`r`) if | |---|---| | every test above worked | log-in, payments or profile switching fail | | the Uuro theme, logo and favicons show | the site shows Timebank.cc's brand | | no new errors in the log | new errors appear on every page view | - **Go**: answer `g`, run `php artisan up` in window 2, press Enter. Check the public site once from a private window. - **No-go**: answer `r`. The rollback starts right away: back to branch `uuro` with `config/uuro.php` and its other files; restoring the database is safe now. Note the stamp the script prints (`storage/app/upgrades//`). --- ## After going live ### 9. The first hour - [ ] Watch the log: `tail -f storage/logs/laravel-$(date +%F).log`. - [ ] `php artisan config:merge --all --dry-run` reports nothing new. - [ ] Review the new keys in `platform/config.php`: they came from the default template, e.g. the email sign-off `platform_sign_off_signature` ("happy Timebanking!"). - [ ] Workers and Reverb are running; the queue drains: `php artisan queue:monitor high,messages,default,emails,mailing,low`. ### 10. The next three days: watch the logs Once a day, read-only: ```bash ( cd "${APP:?set APP first}"; for d in $(seq 0 3); do f=storage/logs/laravel-$(date -d "-$d day" +%F).log; [ -f $f ] && echo "== $f: $(grep -c '\.ERROR' $f) errors" && grep '\.ERROR' $f | cut -c1-160 | sed -E 's/\[[^]]*\] //' | sort | uniq -c | sort -rn | head -5; done ) ``` ### 11. After three quiet days: clean up and finish - [ ] Remove the snapshot of `vendor/`; keep the database backup a little longer: ```bash ( cd "${APP:?set APP first}"; s=storage/app/upgrades//; ls -lh $s; rm -v ${s}vendor.tar.gz ) ``` - [ ] Put `platform/` in a private git repository of its own: its history and its off-site copy (the server's backups stay on the server). No secrets in it; they stay in `.env`. - [ ] Keep `origin/uuro` on Gitea as an archive. - [ ] Developers: with Uuro on Laravel 11, every installation is. Fast-forward `main` to `laravel-11`, so routine `./deploy.sh -e server` deploys pull the new version again on both servers. From then on `deploy.sh` stops a routine deploy that would raise the Laravel major version (e.g. to 12) and points to `scripts/upgrade.sh`. --- ## Rollback | When | Command | Database | |---|---|---| | Before going live | answer `r` at the go/no-go question | restore offered; safe | | After going live | `bash storage/app/upgrades//upgrade.sh --rollback` | code only, unless you type `RESTORE` | The rollback returns to branch `uuro` at its old commit, with `config/uuro.php` and the other files of the branch; the platform folder stays (the old code ignores it). A database restore after going live deletes everything written since the backup; usually the code-only rollback is right, because the old code ignores new tables. After a rollback, do not run `./deploy.sh` until the problem is fixed. Send `storage/app/upgrades//upgrade.log`, `rollback.log` and `state.env` to the developers. ## Record | | | |---|---| | Date and start time | | | Previous commit (branch `uuro`) | | | New commit (branch `main`) | | | Upgrade stamp and tag `pre-leave-uuro-...` | | | Live at | | | Issues and how they were solved | |