# GPLX – Deployment Runbook: IONOS VPS (native Apache) This document records the **exact steps taken** to deploy GPLX on an IONOS VPS on 2026-06-05. It replaces a broken Docker-based MediaWiki install with a native Apache + PHP + MariaDB stack. Use it verbatim for a repeat deployment or hand it to another AI assistant. --- ## Environment | Item | Value | |-----------------|-------------------------------| | Server IP | `31.70.94.35` | | OS | Ubuntu 24.04 LTS | | Web root | `/var/www/gplx/mediawiki` | | Git bare repo | `/var/repo/gplx.git` | | DB name/user | `gplx` / `gplx` | | MW admin user | `Gxplex_admin` | | MW admin pass | `TempAdmin123` ← **change this** | | HTTP Basic Auth user | `testUser` | --- ## Starting situation - Two Docker containers were running on the server: - `mediawiki:1.41` on `0.0.0.0:8080->80` - `mysql:8.0` on `3306/tcp` - `nginx` was running on port 80 (served something else, now disabled) - `/var/www/gplx` did **not** exist - Git remote `ionos` pointed at `ssh://root@31.70.94.35/var/www/gplx` (wrong — no bare repo) --- ## Step 1 – Create bare git repo on the server Run locally: ```powershell ssh root@31.70.94.35 "git init --bare /var/repo/gplx.git && mkdir -p /var/www/gplx && printf '#!/bin/bash\nGIT_WORK_TREE=/var/www/gplx git checkout -f main\n' > /var/repo/gplx.git/hooks/post-receive && chmod +x /var/repo/gplx.git/hooks/post-receive && echo 'Done'" ``` This creates a bare repo at `/var/repo/gplx.git` and a `post-receive` hook that checks out the code into `/var/www/gplx` on every push. --- ## Step 2 – Fix local git remote and push ```powershell git remote set-url ionos ssh://root@31.70.94.35/var/repo/gplx.git git push ionos main ``` The hook fires automatically: output shows `Switched to branch 'main'`. --- ## Step 3 – Install system packages All packages were already present on this server. If doing a fresh install: ```bash ssh root@ apt update && apt install -y apache2 php8.3 php8.3-mysql php8.3-xml \ php8.3-mbstring php8.3-intl php8.3-curl php8.3-gd php8.3-zip \ mariadb-server git apache2-utils ``` --- ## Step 4 – Stop nginx (was holding port 80) ```bash systemctl stop nginx systemctl disable nginx ``` --- ## Step 5 – Create database and user ```bash mysql -u root <<'SQL' CREATE DATABASE IF NOT EXISTS gplx CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER IF NOT EXISTS 'gplx'@'localhost' IDENTIFIED BY 'YOUR_DB_PASSWORD'; GRANT ALL PRIVILEGES ON gplx.* TO 'gplx'@'localhost'; FLUSH PRIVILEGES; SQL ``` If the user already exists with a different password, reset it: ```bash mysql -u root <<'SQL' ALTER USER 'gplx'@'localhost' IDENTIFIED BY 'YOUR_DB_PASSWORD'; FLUSH PRIVILEGES; SQL ``` --- ## Step 6 – Configure Apache vhost with HTTP Basic Auth ```bash cat > /etc/apache2/sites-available/gplx.conf << 'EOF' ServerName 31.70.94.35 DocumentRoot /var/www/gplx/mediawiki AllowOverride All AuthType Basic AuthName "GxPlex – restricted access" AuthUserFile /etc/apache2/.htpasswd Require valid-user EOF a2enmod rewrite a2ensite gplx a2dissite 000-default systemctl start apache2 ``` > **Important:** Do NOT include `Require all granted` alongside `Require valid-user` — > they act as OR, which lets anonymous traffic through. Create the htpasswd file: ```bash htpasswd -c /etc/apache2/.htpasswd testUser # enter password at the prompt ``` --- ## Step 7 – Install Composer dependencies MediaWiki core: ```bash cd /var/www/gplx/mediawiki composer install --no-dev ``` SemanticMediaWiki (has its own vendor tree): ```bash composer install --no-dev -d extensions/SemanticMediaWiki ``` --- ## Step 8 – Deploy LocalSettings.php `LocalSettings.php` is gitignored and must be transferred separately. **On your local machine**, edit `mediawiki/LocalSettings.php` and set: ```php $wgServer = "http://31.70.94.35"; // or https://yourdomain.de once TLS is live $wgDBserver = "localhost"; $wgDBname = "gplx"; $wgDBuser = "gplx"; $wgDBpassword = "YOUR_DB_PASSWORD"; $wgSecretKey = ""; $wgShowExceptionDetails = false; // in the ExtensionFunctions hook: enableSemantics( '31.70.94.35' ); // change to domain once live ``` Then copy to the server: ```powershell scp mediawiki/LocalSettings.php root@31.70.94.35:/var/www/gplx/mediawiki/LocalSettings.php ``` Or patch it in-place on the server with `sed`: ```bash sed -i \ -e 's|$wgServer = "http://127.0.0.1:8080";|$wgServer = "http://31.70.94.35";|' \ -e 's|$wgDBserver = "127.0.0.1";|$wgDBserver = "localhost";|' \ -e 's|$wgDBname = "gflex_test";|$wgDBname = "gplx";|' \ -e 's|$wgDBuser = "root";|$wgDBuser = "gplx";|' \ -e "s|\$wgDBpassword = \"\";|\$wgDBpassword = \"YOUR_DB_PASSWORD\";|" \ -e "s|enableSemantics( '127.0.0.1:8080' );|enableSemantics( '31.70.94.35' );|" \ -e 's|$wgShowExceptionDetails = true;|$wgShowExceptionDetails = false;|' \ /var/www/gplx/mediawiki/LocalSettings.php ``` --- ## Step 9 – Create images directory ```bash mkdir -p /var/www/gplx/mediawiki/images chown -R www-data:www-data /var/www/gplx/mediawiki/images ``` --- ## Step 10 – Install MediaWiki database tables Because the database is empty, `update.php` fails with "Can not upgrade from versions older than 1.35". Use `install.php` instead, temporarily hiding `LocalSettings.php`: ```bash cd /var/www/gplx/mediawiki mv LocalSettings.php LocalSettings.php.bak php maintenance/run.php install \ --dbuser gplx \ --dbpass YOUR_DB_PASSWORD \ --dbname gplx \ --dbserver localhost \ --server 'http://31.70.94.35' \ --scriptpath '' \ --lang en \ --pass TempAdmin123 \ 'GxPlex' 'Gxplex_admin' mv LocalSettings.php.bak LocalSettings.php ``` Then run update to pick up extension schema changes: ```bash php maintenance/run.php update --quick ``` --- ## Step 11 – Set up SMW tables ```bash php extensions/SemanticMediaWiki/maintenance/setupStore.php --nochecks ``` --- ## Step 12 – Smoke test ```bash # Should return 401 (Basic Auth gate) curl -s -o /dev/null -w '%{http_code}' http://31.70.94.35/ # Should return 200 with correct credentials curl -sL -o /dev/null -w '%{http_code}' -u testUser:YOUR_HTPASSWD_PASSWORD http://31.70.94.35/ ``` Open in browser: `http://31.70.94.35` → login prompt → MediaWiki main page. --- ## Step 13 – Import local database dump The local MediaWiki DB was exported as `gflex_test.sql` (≈280 MB) and contains all user accounts, pages, and content from the dev instance. Upload the dump to the server: ```powershell scp D:\Work\Projekte\gplx\gflex_test.sql root@31.70.94.35:/root/gflex_test.sql ``` The `gplx` DB already had tables from the fresh install (Step 10), so drop and recreate it before importing — otherwise MySQL errors with "Table already exists": ```bash mysql -u root <<'SQL' DROP DATABASE gplx; CREATE DATABASE gplx CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; GRANT ALL PRIVILEGES ON gplx.* TO 'gplx'@'localhost'; FLUSH PRIVILEGES; SQL ``` Import the dump: ```bash mysql -ugplx -pYOUR_DB_PASSWORD gplx < /root/gflex_test.sql ``` Run update to apply any schema differences between the dump and the current MW version: ```bash cd /var/www/gplx/mediawiki php maintenance/run.php update --quick ``` All local user accounts and wiki content are now available on the server. --- ## Step 14 – Nutzeraccounts Die Accounts aus dem SQL-Import (Step 13) sind direkt nutzbar — lokale Zugangsdaten funktionieren ohne weitere Schritte. **Vorhandene User prüfen:** ```bash mysql -ugplx -pYOUR_DB_PASSWORD gplx \ -e 'SELECT user_name, user_registration FROM user ORDER BY user_registration DESC LIMIT 20;' ``` **Neuen Account anlegen** (Passwort wird interaktiv abgefragt): ```bash cd /var/www/gplx/mediawiki php maintenance/run.php createAndPromote NeuUser # mit Bureaucrat-Rechten: php maintenance/run.php createAndPromote --bureaucrat NeuUser ``` **Passwort zurücksetzen:** ```bash php maintenance/run.php changePassword --user="Benutzername" --password="NeuesPasswort" ``` **Account im Browser anlegen:** `http://31.70.94.35/index.php/Special:CreateAccount` (nur sichtbar wenn `$wgGroupPermissions['*']['createaccount'] = true`, was der Standard ist). --- ## Next steps (not yet done) - [ ] Change `Gxplex_admin` password via `Special:ChangePassword` - [ ] Point domain DNS A-record to `31.70.94.35` - [ ] Add TLS via Certbot: `certbot --apache -d yourdomain.de` - [ ] Update `$wgServer` and `enableSemantics()` to use the domain - [ ] Push wiki page templates: `Get-Content scripts/templates/.wiki -Raw | php mediawiki/maintenance/run.php edit ...` - [ ] Run `npm run a11y:all` against the live URL --- ## Gotchas encountered | Problem | Cause | Fix | |---------|-------|-----| | `git push ionos main` failed (exit 1) | Remote pointed at non-bare working dir | Created bare repo at `/var/repo/gplx.git` with post-receive hook | | `/var/www/gplx/mediawiki` not found after push | Directory was checked out by hook — actually worked, just needed to look again | `ls /var/www/gplx/mediawiki` confirmed it was there | | SMW fatal: `Class "Onoi\Cache\NullCache" not found` | SMW vendor deps not installed | `composer install --no-dev -d extensions/SemanticMediaWiki` | | `update.php`: "Can not upgrade from versions older than 1.35" | Completely empty DB — MW misreads it | Use `install.php` with `LocalSettings.php` temporarily moved away | | `install.php`: "A LocalSettings.php file has been detected" | Installer refuses when file exists | `mv LocalSettings.php LocalSettings.php.bak` before install, restore after | | Apache won't start | nginx was holding port 80 | `systemctl stop nginx && systemctl disable nginx` | | Basic Auth not blocking (returns 200 for anonymous) | `Require all granted` + `Require valid-user` = OR logic | Remove `Require all granted`; only keep `Require valid-user` | | DB "Access denied for user gplx" | Password in LocalSettings.php didn't match DB | `ALTER USER 'gplx'@'localhost' IDENTIFIED BY '...'` to sync them | | `ERROR 1050: Table 'actor' already exists` on SQL import | Fresh MW install already created tables; importing on top fails | `DROP DATABASE gplx` + recreate, then import |