User:Tim/NethackWikiLocalUpgrade
Local NetHackWiki Docker Setup
This documents how to create a local Docker-based mirror of nethackwiki.com, using the same MediaWiki version and extensions as production.
End Goal Specs
- MediaWiki: 1.45.1 (upgraded from 1.32.5 — see Upgrade section below)
- PHP: 8.3.30 (apache2handler)
- MySQL: 8.4.5 (production) — we use 8.0 locally (see MySQL note below)
- ICU: 63.1
Prerequisites
- Docker + Docker Compose v2 (
docker compose) - The NetHackWiki dumps in
./nethackwiki-dumps/:nethackwiki_current.xml(XML article dump)nethackwiki_current_images.tar.gz(images)
- The files in this repo:
docker-compose.yamlLocalSettings.phpinstall-extensions.shextensions/(custom Paxed extensions)
MySQL version note
Production runs MySQL 8.4. However, MySQL 8.4 removed mysql_native_password entirely, and PHP 7.2’s mysqli driver cannot authenticate with caching_sha2_password. We therefore use mysql:8.0 which still supports mysql_native_password. The --default-authentication-plugin=mysql_native_password flag is set in docker-compose.yaml.
Directory structure
nethack-zim/
├── docker-compose.yaml
├── LocalSettings.php
├── install-extensions.sh (clones gerrit extensions + skins to host)
├── local-docker-setup.md (this file)
├── extensions/ (custom Paxed extensions, bind-mounted)
│ ├── MixedSyntaxHighlight/
│ ├── ReplaceCharsBlock/
│ ├── SimpleTabs/
│ └── RandomPageTitle/
├── extensions-gerrit/ (gerrit-cloned extensions, bind-mounted)
│ ├── AbuseFilter/
│ ├── ArticleMetaDescription/
│ ├── CharInsert/
│ ├── DPLforum/
│ ├── EasyTimeline/
│ ├── Editcount/
│ ├── LabeledSectionTransclusion/
│ ├── Math/
│ ├── NewUserMessage/
│ ├── RandomSelection/
│ ├── Scribunto/
│ ├── TextExtracts/
│ └── TitleKey/
├── skins/ (manually downloaded skins, bind-mounted)
│ └── DarkVector/ (downloaded from github.com/dolfinus/DarkVector @ 617616f)
├── skins-gerrit/ (gerrit-cloned skins, bind-mounted)
│ ├── Modern/
│ └── CologneBlue/
└── nethackwiki-dumps/
├── nethackwiki_current.xml
└── nethackwiki_current_images.tar.gz
Setup steps
1. Clone extensions and skins to host
All extensions and skins are bind-mounted from the host into the container, so they survive container recreates without needing to re-clone.
bash install-extensions.sh
This clones all gerrit extensions into ./extensions-gerrit/ and the Modern + CologneBlue skins into ./skins-gerrit/. Existing dirs are skipped on re-run.
See Code Here: https://nethackwiki.com/wiki/User:Tim/NethackWikiLocalUpgrade/install-extensions.sh
2. Download DarkVector skin
DarkVector is not on gerrit. Download from GitHub at the commit matching production:
mkdir -p skins/DarkVector
curl -L https://github.com/dolfinus/DarkVector/archive/617616f.tar.gz \
| tar -xz --strip-components=1 -C skins/DarkVector
3. Start containers (without LocalSettings.php mount)
MW’s install.php refuses to run if LocalSettings.php already exists. Temporarily comment out the LocalSettings.php bind-mount in docker-compose.yaml:
# - ./LocalSettings.php:/var/www/html/LocalSettings.php:ro
Then start:
docker compose up -d
4. Run the MW installer
This creates the database schema and admin user:
docker exec nethack-zim-mediawiki-1 php /var/www/html/maintenance/install.php \
--dbtype mysql \
--dbserver db \
--dbname nethackwiki \
--dbuser wikiman \
--dbpass wikiman \
--pass adminpassword \
"NetHackWiki" "Admin"
The installer generates a LocalSettings.php inside the container — discard it, we use our own.
5. Add the LocalSettings.php mount and recreate the container
Uncomment the LocalSettings.php line in docker-compose.yaml, then:
docker compose up -d
/wiki/URL format:docker-compose.yamlbind-mountsmediawiki-vhost.confinto the container as the default Apache vhost. That config adds aRewriteRulemapping/wiki/Article_Name→/index.php/Article_Name, matching the production URL format.LocalSettings.phpmust have$wgArticlePath = "/wiki/$1";(already set) for internal links to use this format. Without these two together, wiki links use the ugly/index.php/Article_Nameformat.
6. Run update.php to apply extension DB schemas
docker exec nethack-zim-mediawiki-1 php /var/www/html/maintenance/update.php --quick
7. Import the XML dump
This takes approximately 1.5–2 hours for the full NetHackWiki dump (~37,500 pages):
docker exec nethack-zim-mediawiki-1 php /var/www/html/maintenance/importDump.php \
/mnt/dumps/nethackwiki_current.xml
8. Extract images
docker exec nethack-zim-mediawiki-1 bash -c \
"tar -xzf /mnt/dumps/nethackwiki_current_images.tar.gz \
-C /var/www/html/images/ --strip-components=1"
9. Register images in the database
Extracting the tarball puts images on disk, but MediaWiki also needs them registered in the image DB table for File: pages to work. Run rebuildImages.php to scan the images directory and populate the table (~10 min for the full set):
docker exec -d nethack-zim-mediawiki-1 bash -c \
'php /var/www/html/maintenance/run.php rebuildImages.php --missing \
> /tmp/rebuildImages.log 2>&1'
# Tail progress:
docker exec nethack-zim-mediawiki-1 tail -f /tmp/rebuildImages.log
10. Post-import maintenance
docker exec nethack-zim-mediawiki-1 php /var/www/html/maintenance/run.php rebuildall.php
docker exec nethack-zim-mediawiki-1 php /var/www/html/maintenance/run.php initSiteStats.php --update
11. Access the wiki
Open http://localhost:8080 in your browser.
Database backup and restore
Export (dump to host)
docker exec nethack-zim-db-1 mysqldump -u wikiman -pwikiman nethackwiki \
| gzip > nethackwiki-dumps/nethackwiki_db.sql.gz
The PROCESS privilege warning is harmless — tablespace metadata is skipped but all table data is intact.
Restore from dump
gunzip -c nethackwiki-dumps/nethackwiki_db.sql.gz \
| docker exec -i nethack-zim-db-1 mysql -u wikiman -pwikiman nethackwiki
Extensions
Bundled in mediawiki:1.45 (no install needed)
CategoryTree, Cite, ConfirmEdit, ConfirmEdit/QuestyCaptcha, ImageMap, InputBox, Nuke, ParserFunctions, PdfHandler, Poem, ReplaceText, SyntaxHighlight_GeSHi, WikiEditor
Note: Interwiki was bundled in MW 1.32 but was removed from the core bundle in MW 1.45.
Cloned by install-extensions.sh (gerrit REL1_43, except Scribunto → ./extensions-gerrit/)
| Extension | Gerrit repo |
|---|---|
| AbuseFilter | mediawiki/extensions/AbuseFilter |
| ArticleMetaDescription | mediawiki/extensions/ArticleMetaDescription |
| CharInsert | mediawiki/extensions/CharInsert |
| DPLforum | mediawiki/extensions/DPLforum |
| EasyTimeline | mediawiki/extensions/timeline (cloned to EasyTimeline/) |
| Editcount | mediawiki/extensions/Editcount |
| LabeledSectionTransclusion | mediawiki/extensions/LabeledSectionTransclusion |
| Math | mediawiki/extensions/Math |
| NewUserMessage | mediawiki/extensions/NewUserMessage |
| RandomSelection | mediawiki/extensions/RandomSelection |
| Scribunto | mediawiki/extensions/Scribunto (REL1_45 — see note below) |
| TextExtracts | mediawiki/extensions/TextExtracts |
| TitleKey | mediawiki/extensions/TitleKey |
Note: EasyTimeline’s gerrit repo is named timeline, not EasyTimeline.
Scribunto branch note: All gerrit extensions are cloned at REL1_45 (matching the MediaWiki version). Earlier REL1_32 and REL1_43 Scribunto builds produce a
TypeErroron pages using Lua modules due to aMediaWiki\Title\Titlenamespace issue that was fixed in the REL1_45 branch.
Custom Paxed extensions (bind-mounted from ./extensions/)
These are NethackWiki-specific extensions written by Pasi Kallinen (Paxed). Source was reconstructed from Paxed’s user page on nethackwiki.com.
| Extension | Description |
|---|---|
| MixedSyntaxHighlight | magic word to auto-tag C code blocks
|
| ReplaceCharsBlock | Template block for character/item substitution tables |
| SimpleTabs | <tabs> tag for tabbed content
|
| RandomPageTitle | parser function
|
Compatibility notes for Paxed extensions (by version hop):
- MixedSyntaxHighlight (broke at 1.35→1.42): Replaced
ParserBeforeStriphook (removed MW 1.36, deprecated 1.35) withParserBeforeInternalParse. Also replacedInternalParseBeforeSanitizehook (removed MW 1.40, deprecated 1.35) — this was already gone before 1.42. ReplacedMagicWord::get()static (removed MW 1.40) withMagicWordFactory. - ReplaceCharsBlock (broke at 1.35→1.42 and 1.42→1.45): Replaced
Article::getRevision()+Revision::RAW(removed ~MW 1.36) withRevisionStore::getRevisionByTitle()andSlotRecord::MAIN. UpdatedTitle::to\MediaWiki\Title\Title::. Replaced staticContentHandler::getContentText($content)(removed MW 1.45) with$content->getText(). - RandomPageTitle (broke at 1.35→1.42 and 1.42→1.45): Replaced
wfGetDB()(deprecated MW 1.42) withDBLoadBalancer::getConnection(). Updatedselect()/fetchObject()tonewSelectQueryBuilder(). Replacedcategorylinks.cl_tocolumn access (removed MW 1.43) with a join via thelinktargettable. UpdatedTitle::to\MediaWiki\Title\Title::. - SimpleTabs: No changes needed, works as-is in MW 1.45.
Skins
Bundled in mediawiki:1.45
MonoBook, Vector, Timeless, MinervaNeue, CologneBlue, Modern (loaded via wfLoadSkin())
Cloned by install-extensions.sh (gerrit REL1_43 → ./skins-gerrit/)
| Skin | Gerrit repo |
|---|---|
| Modern | mediawiki/skins/Modern |
| CologneBlue | mediawiki/skins/CologneBlue |
DarkVector (manually downloaded → ./skins/)
Downloaded from github.com/dolfinus/DarkVector at commit 617616f (matches production). Uses skin.json for registration; loaded in LocalSettings.php with:
wfLoadSkin('DarkVector');
Note: DarkVector uses the old SkinTemplate-based API removed in MW 1.40. It loads without crashing the wiki but fails if selected as the active skin. The default skin is Vector — DarkVector remains registered in the skin list for reference.
Upgrade from MW 1.32.5 to MW 1.45
The wiki was originally set up with MediaWiki 1.32.5 (matching production at the time). In Feb 2026, it was upgraded to MW 1.45.1.
Upgrade path
Direct 1.32 → 1.45 is not supported. The upgrade was done in hops:
1.32.5 → 1.35 → 1.42 → 1.45
Each hop: update the image tag in docker-compose.yaml, run docker compose up -d, then run update.php.
Running update.php safely
Launch in background so you can tail the log without risk of interruption:
docker exec -d nethack-zim-mediawiki-1 bash -c \
'php /var/www/html/maintenance/update.php --quick > /tmp/update.log 2>&1'
# Check it's still running:
docker exec nethack-zim-mediawiki-1 pgrep -a php
# Tail the log:
docker exec nethack-zim-mediawiki-1 tail -f /tmp/update.log
MW 1.35 and 1.42 both crash mid-run on already-applied patches (harmless). A second run exits cleanly with code 0 — that is the confirmed-success state.
Breaking changes encountered
| Change | Version hop | Fixed by |
|---|---|---|
Paxed extension hook removals (ParserBeforeStrip, InternalParseBeforeSanitize, MagicWord::get())
|
1.35→1.42 | Updated PHP in MixedSyntaxHighlight |
Article::getRevision() + Revision::RAW removed (~MW 1.36)
|
1.35→1.42 | Updated ReplaceCharsBlock to use RevisionStore API |
wfGetDB() deprecated (MW 1.42)
|
1.35→1.42 | Updated RandomPageTitle to use DBLoadBalancer |
Title class moved to MediaWiki\Title\Title namespace
|
1.35→1.42 | Updated all usages to fully-qualified name |
div.thumb.tright float dropped from mediawiki.legacy.shared CSS
|
1.35→1.42 | Added inline style="float:right; ..." to Template:Monster outer div (see Wiki template changes below)
|
Scribunto REL1_43 TypeError with MediaWiki\Title\Title
|
1.42→1.45 | Re-cloned Scribunto at REL1_45 |
categorylinks.cl_to column removed (MW 1.43)
|
1.42→1.45 | Updated RandomPageTitle to join via linktarget table
|
ContentHandler::getContentText($content) static removed (MW 1.45)
|
1.42→1.45 | Updated ReplaceCharsBlock to use $content->getText()
|
Interwiki extension removed from MW core bundle (MW 1.45)
|
1.42→1.45 | Commented out in LocalSettings.php
|
ConfirmEdit/QuestyCaptcha subextension load syntax changed
|
1.42→1.45 | Changed to wfLoadExtension('ConfirmEdit/QuestyCaptcha')
|
Re-cloning extensions at REL1_45
After upgrading to MW 1.42+, all gerrit extensions must be re-cloned at REL1_45:
rm -rf extensions-gerrit/ skins-gerrit/
bash install-extensions.sh
docker compose restart mediawiki
Troubleshooting
500 error on first load
Enable exception details temporarily to diagnose:
// Add to bottom of LocalSettings.php
$wgShowExceptionDetails = true;
Then restart the container (docker compose restart mediawiki) and visit the page. Remove the setting once resolved.
Common causes: - Extension magic word not registered (add .i18n.magic.php) - Extension requires binary not present in container (e.g. texvc for Math) - DB schema not applied (run update.php)
PHP OPcache stale after file edit
After editing a bind-mounted PHP file, restart the container to clear OPcache:
docker compose restart mediawiki
install.php fails with “LocalSettings.php already exists”
Remove or temporarily comment the LocalSettings.php volume mount from docker-compose.yaml, then docker compose up -d before running install.php.
MySQL auth error
If you see caching_sha2_password errors, ensure the db service uses mysql:8.0 (not 8.4) with --default-authentication-plugin=mysql_native_password in the command flags.
Wiki template changes (production replication required)
These are changes made to wiki templates/pages stored in the database. They are not captured in git and must be manually replicated to production.
Template:Monster — MW 1.45 float fix
Problem: In MW 1.45, the mediawiki.legacy.shared CSS module was dropped. That module provided div.thumb.tright { float: right }, which the monster infobox template relied on to float right next to article text.
Symptom: On any monster page (e.g. Fog_cloud, Killer_bee), the infobox table appeared full-width above the article text instead of floating right.
Fix applied locally: Edited Template:Monster to add inline float styles to the outer wrapper <div>:
-<div class="thumb tright">
+<div class="thumb tright" style="float:right; clear:right; margin:0.5em 0 1.3em 1.4em;">
Why not <figure>: MediaWiki’s wikitext parser does not allow <figure> as raw HTML in templates — it gets escaped to <figure>. Inline styles on the existing div are the correct workaround for templates that cannot use Lua/raw HTML.
To replicate on production: 1. Go to https://nethackwiki.com/wiki/Template:Monster 2. Edit the template (requires admin/template-editor rights) 3. On the first line inside <includeonly>, change: <div class="thumb tright"> to: <div class="thumb tright" style="float:right; clear:right; margin:0.5em 0 1.3em 1.4em;"> 4. Save with summary: MW 1.45 compat: restore float:right lost when mediawiki.legacy.shared was dropped
Note:
Template:Infobox_Monsteris just a redirect toTemplate:Monster— onlyTemplate:Monsterneeds to be edited.