Jump to content

Wiki.tetrain.com Upgrade Plan

From TetraWiki

Status: live in production (2026-07-25). wiki.tetrain.com now serves the migrated MediaWiki 1.43.9 site on Rocky 9; the old CentOS 7 box is reachable at wiki-old.tetrain.com pending decommission. This document is being written and updated as work proceeds, same living-document pattern as the tornedo.tetrain.com plan.

This is the decision log (why each choice was made, problems hit, alternatives considered). For a reproducible, command-by-command sequence you could actually follow to redo this migration, see wiki.tetrain.com_RUNBOOK.md instead.

1. Source state (read-only discovery, 2026-07-24)[edit]

wiki.tetrain.com (172.105.56.64, aka li2092-64.members.linode.com):

Layer Version Status
OS CentOS Linux 7.7.1908 EOL
Kernel 3.10.0-1062 (2019) EOL
Web server Apache httpd 2.4.6 HTTP only, no TLS
PHP 5.4.16 EOL since Sept 2015
Database MariaDB 5.5.64 EOL since April 2020
MediaWiki 1.18.4 (Feb 2012) ~14 years old, ~20+ major versions behind

Data footprint: my_wiki DB 81.6 MB; images/ 444 MB (209 MB thumb cache — regeneratable, 18 MB archive, rest originals); whole install 736 MB. VM itself is tiny (990 MB RAM, 25 GB disk, 4.3 GB used) — data-light migration, most of the risk is in code/extension compatibility, not data volume.

Extensions (12 installed):

  • 9 standard/officially-maintained: CheckUser, ConfirmEdit, Gadgets, Nuke, ParserFunctions, Renameuser, TimedMediaHandler, Vector, WikiEditor — current versions exist for any modern MediaWiki release.
  • 3 old third-party, unmaintained since 2011-2015, will not run under PHP 8 as-is:
    • HTML5video (2011) — self-hosted video embed. Functionally superseded by TimedMediaHandler, already installed. Decision: drop, don't port.
    • MsUpload (drag-and-drop bulk upload, v10, Schwindl/Schenone) — has a real mediawiki.org page and known community forks. Needs a compatible-fork check.
    • UserAdmin (2012, Lance Gatlin) — admin panel: add/edit/purge users, mass block, password-reset/welcome emails, for private-wiki tighter control. Old, possibly abandoned upstream; core MediaWiki's own user-management special pages cover much of this now.

No TLS currently configured anywhere (no mod_ssl, no certbot).

2. Why this is not an in-place/leapp-style upgrade[edit]

  1. PHP 5.4 → 8.x is a language-level break — MediaWiki 1.18's own code will not run under PHP 8 (removed functions, changed OOP semantics). No version of "just bump PHP under the existing codebase" exists.
  2. MediaWiki's own upgrade policy does not support jumping ~20 major versions directly. Confirmed via mediawiki.org and the project's own UPGRADE file: "upgrading from releases older than two LTS releases is not supported... upgrade to a more recent LTS version first, then to this version." For a source as old as 1.18 (pre-dates the first LTS release in this scheme), the safe/documented path is to walk every LTS stop in sequence.

3. Chosen approach[edit]

  • Target OS: Rocky Linux 9 (chosen over 10 — more production-proven at this point).
  • Target MediaWiki: 1.43 LTS (supported through Dec 2027; requires PHP 8.1+, MariaDB 10.1+/MySQL 5.5.8+). Chosen over the newer 1.46 stable for production stability.
  • Migration mechanism: disposable Docker containers, one per LTS hop, since the hop chain needs several different PHP versions that can't sanely coexist natively on one Rocky 9 box. Official MediaWiki Docker Hub images only retain current-era tags, so each hop is built from the official php image (which does keep full historical tags) plus the matching MediaWiki release tarball downloaded directly from releases.wikimedia.org.
  • LTS hop chain (confirmed via mediawiki.org release history — Compatibility, Manual:Upgrading): 1.18.4 (source) → 1.23.161.27.71.31.161.35.131.39.111.43.2. One DB instance (MariaDB 10.11) persists across every hop; only the PHP runtime + MediaWiki codebase change per hop. update.php run at each stop reconciles the schema forward; extensions are deliberately *not* loaded during the hop chain (minimal LocalSettings.php, DB connection only) since the goal is walking the core schema, not extension data — extensions get reconciled once on the final 1.43 codebase.
  • Final production runtime: native (not Docker) on Rocky 9, matching the rest of the fleet's operational pattern — PHP 8.1+, MariaDB 10.11 LTS, httpd or nginx+php-fpm, TLS via Let's Encrypt (client has already pointed DNS at the new box).

4. Target server[edit]

wiki-new.tetrain.com, 172.105.39.56, Rocky Linux 9.8, provided by client 2026-07-24. DNS already pointed here by client. Root access via id_rsa_curio (fleet-standard key). 1 vCPU / 1.9 GB RAM / 49 GB disk.

5. Progress log[edit]

  • 2026-07-24: Base OS setup done — hostname set, dnf update run (kernel bumped to 5.14.0-687.29, reboot will be needed before go-live to run the current kernel), firewalld enabled, Docker CE 29.6.2 + compose installed.
  • 2026-07-24: Backup of source wiki.tetrain.com interrupted — its SSH port stopped responding mid-session (ICMP still fine, port 22 silently drops; not an auth issue, no other access path available). Cause unconfirmed (possible fail2ban-style ban, possible resource event on the tiny 990MB-RAM box, possibly provider-side). Backup poller running in background, will resume the backup the moment it's reachable again.
  • 2026-07-24: On wiki-new.tetrain.com: persistent MariaDB 10.11 container started for the hop chain (mw-mariadb, network mwmigrate, data volume /root/mw-hop-migration/db-data). Downloaded all 6 hop-chain release tarballs (1.23.16/1.27.7/1.31.16/1.35.13/1.39.11/1.43.2) into /root/mw-hop-migration/tarballs/. Building 6 Docker images (one per hop, PHP versions 5.6/5.6/7.2/7.4/8.0/8.1 matched to each MediaWiki version's supported range) — in progress.
  • Real hop-chain execution against actual production data is blocked on the backup (see above) — the mechanics can be smoke-tested on an empty schema, but the meaningful test needs the real 1.18.4 data since that's where any real incompatibility would surface.
  • 2026-07-24: All 6 hop Docker images built successfully (PHP 5.6/5.6/7.2/7.4/8.0/8.1 with mysqli, intl, gd, mbstring, calendar, opcache compiled in).
  • 2026-07-24: Extension decisions finalized:
    • HTML5video — drop, superseded by TimedMediaHandler (already installed).
    • MsUpload — turns out to be an official Wikimedia-mirrored extension (not truly third-party as first assessed), actively maintained, confirmed working on 1.43 by other users. Will pull wikimedia/mediawiki-extensions-MsUpload on the REL1_43 branch.
    • UserAdmin — confirmed abandoned upstream, no reliable PHP8-compatible fork. Its functionality (add/edit/delete users, group management, password-reset/welcome emails) is fully covered by core MediaWiki's own special pages (Special:UserRights, Special:Block, Special:PasswordReset, Special:CreateAccount). Decision: drop, don't port — flag this explicitly at handover so nobody goes looking for the old UserAdmin panel URL and finds it missing without knowing why.
  • 2026-07-24: Production stack built on wiki-new.tetrain.com, independent of the pending backup:
    • Apache 2.4.62 + PHP-FPM 8.3.31 (mysqlnd, gd, mbstring, xml, intl, opcache) wired via proxy:unix:/run/php-fpm/www.sock.
    • MariaDB 10.11.18, hardened (anonymous users removed, remote root removed, test DB dropped), my_wiki DB created with a dedicated scoped wikiuser account — not root like the old box. Credentials generated randomly, stored root-only-readable at /root/.mariadb_root_credentials and /root/.wiki_db_credentials on the box itself, never transmitted through chat.
    • firewalld opened for http/https.
    • Let's Encrypt cert issued and deployed via certbot (--redirect, HTTP→HTTPS enforced), auto-renewal configured, expires 2026-10-22. Confirmed serving HTTPS correctly (403 is expected — empty docroot, MediaWiki not deployed yet).
  • Only remaining blocker for real progress: wiki.tetrain.com SSH still unreachable (~26 min and counting as of this note) — site itself (HTTP) confirmed still up and serving fine, so this is SSH-specific, not a box-down event. Background poller watching for recovery.

6. Migration completed (2026-07-24) — ready for client testing[edit]

  • Backup recovered once wiki.tetrain.com SSH came back (client confirmed cause: their VPN had disconnected — nothing wrong with the box itself). DB (47MB), images (215MB), and extensions/config (161MB, archival only) transferred to wiki-new.tetrain.com, integrity verified via MD5.
  • Full 6-hop version-hop chain run against the real production data, not just a smoke test: 1.18.4 → 1.23.16 → 1.27.7 → 1.31.16 → 1.35.13 → 1.39.11 → 1.43.9 (switched from the initially-grabbed 1.43.2 to the latest 1.43.x patch after 1.43.2's pinned Composer dependencies hit security-advisory blocks — 1.43.9 resolved cleanly). Every hop's update.php completed with zero real errors. Data integrity confirmed identical before/after: 1707 pages, 3634 revisions, 163 users.
  • Migrated DB exported from the migration container and imported into the real production MariaDB (scoped wikiuser account, not root).
  • MediaWiki 1.43.9 codebase deployed to /var/www/wiki, Composer dependencies installed properly (composer install --no-dev).
  • All 9 standard extensions + MsUpload pulled at the REL1_43 branch (CheckUser, ConfirmEdit, Gadgets, Nuke, ParserFunctions, Renameuser, TimedMediaHandler, WikiEditor, Vector skin, MsUpload) — MediaWiki's own 1.43.9 tarball already bundles most of these, only Renameuser/TimedMediaHandler/MsUpload needed fetching separately. Confirmed all 9 loaded correctly via Special:Version.
  • Images copied in (originals + archive, 236MB total incl. fresh-install defaults), SELinux contexts set correctly (httpd_sys_rw_content_t on images/cache dirs — Rocky 9 is enforcing by default).
  • LocalSettings.php written fresh for the new environment: proper wgServer (HTTPS), freshly generated wgSecretKey/wgUpgradeKey (not the placeholder used during migration testing), scoped DB credentials pulled from /root/.wiki_db_credentials (never displayed in chat).
  • End-to-end verification, all passing: homepage loads (200, correct site title "TetraWiki"), a real migrated content page renders correctly (ISGEC_-_Next_Cloud_Deployment), direct image serving works, on-demand thumbnail generation confirmed working (GD-based, since $wgUseImageMagick = false).

What's deliberately dropped/changed vs. the old site[edit]

  • HTML5video — dropped from the migration itself (not ported, since it's PHP8-incompatible and functionally superseded by TimedMediaHandler for *uploaded* video files). This left every pre-existing YouTube embed on the wiki broken (the tag simply doesn't exist anymore), since HTML5video was actually being used for embedding external YouTube videos, not self-hosted file uploads — a distinct use case TimedMediaHandler doesn't cover. Fixed 2026-07-25, see Section 8 below — this line in earlier versions of this doc said "dropped, superseded by TimedMediaHandler," which undersold the actual user-facing impact and is superseded by that section.
  • UserAdmin — dropped (abandoned upstream, no PHP8-compatible fork). Equivalent functionality now lives in core Special:UserRights, Special:Block, Special:PasswordReset, Special:CreateAccount. Flag this at handover — anyone who goes looking for the old UserAdmin panel URL needs to know it's gone and why.
  • TLS is now enforced (old site was HTTP-only) — Let's Encrypt, auto-renewing.
  • DB access is now a scoped wikiuser account, not root like the old box.

Not yet done[edit]

  • Old wiki.tetrain.com box (now at wiki-old.tetrain.com, 172.105.56.64) decommission — DNS cutover is done (Section 9), but the box itself hasn't been shut down/reclaimed yet; still a distinct later decision pending client go-ahead, same gating pattern as every other destructive step in this engagement.
  • Outbound email is confirmed broken on wiki-new/wiki.tetrain.com — MediaWiki has $wgEnableEmail/$wgEnableUserEmail on and a sender address configured, but no MTA is installed at all (no sendmail, no Postfix, nothing on port 25; PHP's sendmail_path points at a binary that doesn't exist). Password resets, "email this user," and watchlist notifications all silently fail right now. Client explicitly deferred this (2026-07-25) — parked until they're ready to take it up. Open question when they do: route through Tetra's existing relay servers (server-relay1/2/3.tetrain.com, used for every other Tetra-hosted mail flow in this fleet) vs. a local Postfix — leaning relay for SPF/deliverability consistency, but needs a decision.
  • TimedMediaHandler needs ffmpeg for video transcoding if video *uploads* are actually used — not installed yet, wasn't blocking for the text/image content that makes up this wiki, can add if needed. (Separate from the YouTube-embed fix in Section 8, which doesn't touch file uploads.)
  • Cosmetic: wgLogo points at a placeholder path; the old site's custom logo (tetralogo.jpg from the old skins/common/) could be restored if wanted.
  • Rocky 9 kernel was bumped during initial setup (5.14.0-687.17 → .29) — a reboot is needed at some point to actually run the current kernel; not done yet since this was a live build in progress.

Production URL: https://wiki.tetrain.com/ (live since 2026-07-25). Test/staging alias https://wiki-new.tetrain.com/ still resolves to the same box and still works — both hostnames are on the same Apache vhost and TLS cert now (Section 9).

7. Post-handover fix: logo sizing (2026-07-24)[edit]

Client reported the logo looked wrong on the test site. Root cause was two separate things:

  1. $wgDefaultSkin = "vector" was selecting legacy Vector (SkinVectorLegacy), not modern Vector 2022 (vector-2022, SkinVector22) — MediaWiki 1.43's actual intended default. Legacy Vector uses an old fixed .mw-wiki-logo single-image class; Vector 2022 uses a proper icon+wordmark structure. Fixed: $wgDefaultSkin = "vector-2022".
  2. Whatever logo was configured (client had already changed $wgLogo to point at the real tetrain.com company logo, tetra_logo_2017Web.png, 198×399px — tall/narrow, not the old tetralogo.jpg from the original site) was being forced into Vector 2022's fixed 50×50px square icon slot via hardcoded width/height HTML attributes, stretching/distorting it. Fixed: downloaded the logo, hosted it locally (/var/www/wiki/resources/assets/logo/, avoiding a fragile cross-site hotlink to www.tetrain.com), and re-composited it onto properly-sized transparent square canvases (50/75/100px for 1x/1.5x/2x) with the logo letterboxed/centered at its correct aspect ratio rather than stretched. Configured via the modern $wgLogos array (replacing the legacy single-string $wgLogo).

Verified: served logo image is now genuinely 50×50px matching the skin's fixed slot exactly, no distortion. Cache cleared, confirmed live.

8. Post-handover: YouTube video embed restoration (2026-07-25)[edit]

Discovered as part of the separate Wiki Pages Cleaning project's content audit: dropping HTML5video during the migration (Section 6) broke every pre-existing YouTube embed on the wiki — 149 embeds across 44 pages, all internal KT/training recordings and client-architecture walkthroughs. TimedMediaHandler doesn't cover this use case (it's for self-hosted uploaded video files, not embedding external YouTube links), so this wasn't actually fixed by the original migration decision.

  • Platform fix: installed the third-party EmbedVideo extension (StarCitizenTools/mediawiki-extensions-EmbedVideo — not Wikimedia/Gerrit-hosted, no REL1_43 branch, cloned default branch, confirmed extension.json supports MediaWiki ≥1.29.0). Patched a MediaWiki-1.43-incompatibility in EmbedVideo.hooks.php (core ParserOutput::addModules()/addModuleStyles() now require strict array typing; EmbedVideo was passing bare strings) — two one-line fixes, verified via a scratch test page with $wgShowExceptionDetails temporarily enabled, reverted after. Loaded via wfLoadExtension('EmbedVideo') in LocalSettings.php. New embed syntax: {{#ev:youtube|VIDEO_ID|640}}, replacing the dead <HTML5video> tag.
  • Content fix: for every one of the 149 embeds, retrieved the video's actual YouTube transcript (via browser automation — auto-captions, not audio playback) and wrote an accurate summary paragraph published directly under each embed on its wiki page. Quality varies with source audio: most got full detailed summaries; ~25 are explicitly flagged [VERY POOR TRANSCRIPT QUALITY] or [PARTIAL TRANSCRIPT] where ASR/audio quality only allowed a partial or topic-level summary — flagged rather than guessed. 5 videos are outright inaccessible and flagged as such: 1 private, 4 removed by YouTube (3 for harassment/bullying policy, 1 general ToS violation — all 4 removals landed on the same Training_2022_Linux_team page, worth raising with whoever owns that YouTube account since repeated strikes risk the account itself). 2 duplicate-content pairs identified and cross-referenced instead of re-summarized. All findings are written directly into the paragraphs published on the wiki, so they're visible to anyone reading the page, not just in this doc.
  • Publish mechanism: pulled current wikitext per page via php maintenance/run.php getText.php "<title>", replaced each <HTML5video>...</HTML5video> tag with the {{#ev:...}} embed plus a '''Video summary:''' paragraph, applied via php maintenance/run.php edit.php -u Admin -b --nocreate "<title>" (stdin). Note: most of these 44 "pages" are actually Category:-namespace pages being used to hold real content (a pre-existing wiki quirk, not introduced by this work) — titles needed the correct namespace prefix (Category:, or Help: for one outlier, Editing) to resolve correctly; a plain-namespace title lookup silently reports "page does not exist" otherwise.
  • Verified: DB sweep confirms zero leftover <HTML5video> tags across all 44 pages; spot-checked in-browser that embeds render as real <iframe> elements (not just wikitext), including the largest page (50 videos, first/last IDs both confirmed rendering correctly).

9. Post-handover: production DNS cutover (2026-07-25)[edit]

Client updated DNS directly (no registrar/API access was available in this environment to do it here): wiki.tetrain.com A record repointed from the old CentOS 7 box (172.105.56.64) to wiki-new.tetrain.com (172.105.39.56), and a new wiki-old.tetrain.com A record created pointing at the old box so it stays reachable post-cutover.

DNS alone wasn't sufficient — two things on the server side would have made the real domain look broken to visitors even with DNS correct, both found and fixed same-day:

  1. TLS certificate didn't cover the real domain. The existing Let's Encrypt cert (Section 5) only had wiki-new.tetrain.com in its SAN list; hitting https://wiki.tetrain.com fell through to Apache's default self-signed cert (browser security warning). Expanded the same cert lineage via certbot --apache -d wiki-new.tetrain.com -d wiki.tetrain.com --expand. Certbot issued the expanded cert but couldn't auto-install it into the vhost (ambiguous vhost match, no ServerAlias declared) — fixed manually by adding ServerAlias wiki.tetrain.com to both the port-80 and port-443 vhost blocks (/etc/httpd/conf.d/wiki.conf, wiki-le-ssl.conf, both backed up before editing) and broadening the HTTP→HTTPS RewriteCond to match either hostname.
  2. $wgServer still pointed at the old hostname. MediaWiki would have kept generating every internal link, canonical URL, and redirect against wiki-new.tetrain.com even with the correct cert and DNS in place. Updated $wgServer to https://wiki.tetrain.com in LocalSettings.php (backed up first).

Verified end-to-end: valid cert presented for both hostnames (confirmed via SNI-specific TLS handshake against each), http://https:// redirect works for both, full redirect chain to Main_Page returns 200 OK, and a video-embed page (Section 8) renders correctly — iframe present — when loaded via https://wiki.tetrain.com. wiki-old.tetrain.com resolves to and serves the old box.

Outbound email was checked as part of this same pass and found broken (see "Not yet done" above) — client explicitly deferred fixing it.