Docker Compose for Mediawiki + OpenSemanticLab
Minimal Setup
- 4 CPUs ( > 2 GHz)
- 4GB RAM
- 50 GB HDD
Recommended:
- 8 CPUs,
- 8 GB RAM
- 100 GB SSD
OS: Any OS with support for Docker, e.g. Ubuntu in its current LTS version (24.04.3)
Required
Recommended to follow instructions:
Optional SSL Termination
- Nginx for SSL Termination + Certbot to create SSL/TSL certs with Let's Encrypt
- or (recommended) caddy-docker-proxy as integrated service, example config see
docker-compose.caddy.example.override.yml
Clone & init the repo
git clone https://github.com/OpenSemanticLab/docker-compose-osl-wiki
cd docker-compose-osl-wiki
sudo chown -R www-data:www-data mediawiki/dataCopy .env.example to .env and CustomSettings.php.example to CustomSettings.php
cp .env.example .env
cp mediawiki/config/CustomSettings.example.php mediawiki/config/CustomSettings.php
Set the config parameters in .env
Example:
COMPOSE_PROJECT_NAME=osl-1 # unique project name (change when running multiple instances)
MW_IMAGE_TAG=main # Docker image tag, see table below
MW_HOST_PORT=8081 # the port mediawiki exposes on the host (localhost only)
MW_SITE_SERVER=http://localhost:8081 # the public URL of your wiki
MW_SITE_NAME=Wiki # the name of your instance
MW_SITE_LANG=en # the site language
MW_TIME_ZONE=Europe/Berlin # your time zone
MW_ADMIN_PASS=change_me123 # the password of the 'Admin' account
MW_DB_PASS=change_me123 # the db password of the user 'mediawiki'
# the packages to install (multi-line)
MW_PAGE_PACKAGES="
world.opensemantic.core;
world.opensemantic.base;
world.opensemantic.demo.common;
"
MW_AUTOIMPORT_PAGES=true # if true, packages are installed / updated at start
MW_AUTOBUILD_SITEMAP=false # if true, the sitemap is periodically built
MYSQL_ROOT_PASSWORD=change_me123 # the password of the 'root' accountAvailable image tags (MW_IMAGE_TAG):
| Tag | MediaWiki | Description |
|---|---|---|
main |
REL1_43 | Latest stable build (default) |
latest |
REL1_43 | Alias for main |
v2.x.x (e.g. v2.0.0-alpha.1) |
REL1_43 | Versioned releases for MW 1.43 |
v1.x.x (e.g. v1.0.0-beta.23) |
REL1_39 | Versioned releases for MW 1.39 |
v0.x.x (e.g. v0.8.0) |
REL1_35 | Legacy releases for MW 1.35 |
Note
By default, no ports are exposed on the host. Services communicate over the internal Docker network. Use a reverse proxy (nginx/caddy) to expose mediawiki publicly, or use the local override for development:
cp docker-compose.local.example.override.yml docker-compose.override.ymlThis exposes mediawiki (:8081), mysql (:3307), blazegraph (:9999), and drawio (:8082) on the host.
You can customize the stack further with docker-compose.override.yml. Example overrides:
Expose ports for local development — see docker-compose.local.example.override.yml
Add caddy as reverse proxy — see docker-compose.caddy.example.override.yml
Mount custom volumes (logos, extensions):
services:
mediawiki:
volumes:
- ./mediawiki/config/logo.png:/var/www/html/w/logo.png
- ./mediawiki/config/logo.svg:/var/www/html/w/logo.svg
- ./mediawiki/extensions/MyCustomExtension:/var/www/html/w/extensions/MyCustomExtensiondocker compose upDepending on the size of the packages defined in MW_PAGE_PACKAGES it will take some time to install them in the background.
You can now login (e. g. at http://localhost:8081/wiki/Main_Page) with user 'Admin' and the MW_ADMIN_PASS you set in the .env file.
You can add or overwrite mediawiki settings by editing mediawiki/config/CustomSettings.php.
You need to re-run docker compose up to apply them
To make your instance public readable add:
####### Make it public ########
$wgGroupPermissions['*']['read'] = true;Default settings allow only members of the sysadmin group to delete pages.
# allow every user to delete pages
$wgGroupPermissions['user']['delete'] = true;Please note that deleted pages are still available in the pages archive.
Further settings and custom groups can also be defined, see Manual:User_rights. Example:
// revoke edit-right for standard users
$wgGroupPermissions['user']['edit'] = false;
$wgGroupPermissions['sysop']['edit'] = true;
$wgGroupPermissions['active-user'] = $wgGroupPermissions['user'];
// grant edit-right and delete-right for custom-group
$wgGroupPermissions['custom-group']['edit'] = true;
$wgGroupPermissions['custom-group']['delete'] = true;Pages installed via packages are default only editable for sysadmin. Custom schemas in the namespace Category can be further restricted by
// create schema-edit right
$wgAvailableRights[] = 'schema-edit';
// grant it to custom-group and sysop
$wgGroupPermissions['custom-group']['schema-edit'] = true;
$wgGroupPermissions['sysop']['schema-edit'] = true;
// restrict the creation of new categories outside installed packages
$wgNamespaceProtection[NS_CATEGORY] = ['schema-edit'];Please note: Content packages defined by MW_PAGE_PACKAGES will be install automatically.
Optional packages listed here can be installed under <your wiki domain>/wiki/Special:Packages. Package sources are hosted here.
To add additional optional packages, add
$wgPageExchangePackageFiles[] = 'packages.json url';e. g.
$wgPageExchangePackageFiles[] = 'https://raw.githubusercontent.com/OpenSemanticWorld-Packages/world.opensemantic.meta.docs/main/packages.json';
to mediawiki/config/CustomSettings.php
In order to add multiple packages that are listed in an index file, add it to the config as follows:
$wgPageExchangeFileDirectories[] = 'https://raw.githubusercontent.com/<MyOrg>/PagePackages/refs/heads/main/package_index.txt';For private repos generate a Github private repo access token with permission "Content" (read)
$wgPageExchangeGitHubAccessToken = [
'<MyOrg>' => 'github_pat_...', # org-level
'<MyOrg>/'<repo>' => 'github_pat_...', # repo-level
];In all cases additional packages are now available for installation. Use <your wiki domain>/wiki/Special:Packages or the API to actually install them (more information see Extension:Page_Exchange).
Insecure in public instances!
Example:
$additionalFileExtensions = [ 'py', 'exe' ];
$wgFileExtensions = array_merge( $wgFileExtensions, $additionalFileExtensions );
$wgProhibitedFileExtensions = array_diff( $wgProhibitedFileExtensions, $additionalFileExtensions );
$wgMimeTypeExclusions = array_diff( $wgMimeTypeExclusions, [ 'application/x-msdownload' ]); # for .exe
# allow any upload - insecure in public instances!
# $wgStrictFileExtensions = false;
# $wgCheckFileExtensions = false;
# $wgVerifyMimeType = false;If your instance is public, make sure to add a privacy policy to /wiki/Site:Privacy_policy and legal informations to /wiki/Site:General_disclaimer.
You may also create a single page with all necessary informations and point with a redirect from other pages to it: #REDIRECT [[Site:General_disclaimer]]
If you don't have an email server yet (optional, but necessary for notification and password resets, etc.), you can use docker-mailserver
The following extensions are bundled but not enabled by default. Enable them by adding the corresponding line to mediawiki/config/CustomSettings.php:
# Authentication
wfLoadExtension( 'OATHAuth' ); # Two-factor authentication (see Two-Factor-Authentication section)
wfLoadExtension( 'PluggableAuth' ); # Pluggable authentication framework
wfLoadExtension( 'OpenIDConnect' ); # OpenID Connect login (e.g. via Keycloak)
wfLoadExtension( 'Realnames' ); # Display real names beside user IDs
# Content & Moderation
wfLoadExtension( 'ApprovedRevs' ); # Allows setting approved revisions of pages
wfLoadExtension( 'CommentStreams' ); # Discussion comments on pages
wfLoadExtension( 'Lockdown' ); # Restrict namespace access per group
wfLoadExtension( 'HitCounters' ); # Page view counters
wfLoadExtension( 'UrlGetParameters' ); # Access URL parameters in wiki pages
# UI & Display
wfLoadExtension( 'Iframe' ); # Embed external content via iframes (see Iframes section)
wfLoadExtension( 'PagedTiffHandler' ); # Multi-page TIFF file support
wfLoadExtension( 'InteractiveSemanticGraph2' ); # Interactive graph visualization (v2)
# Data & Export
wfLoadExtension( 'WebDAV' ); # Access uploaded files via WebDAV (e.g. directly with MS Word)
wfLoadExtension( 'RdfExport' ); # DCAT catalog at /api.php?action=catalog&format=json&rdf_format=turtle and OWL ontology export (use only in public instances, requires SPARQL-Store)
wfLoadExtension( 'Chatbot' ); # AI chatbot integration
wfLoadExtension( 'ApiGateway' ); # API gateway for external service integrationDrawIO diagrams may contain embedded SVG images (mostly from the built in icon library) using data:image/svg+xml URIs. MediaWiki blocks these by default as a security measure (embedded SVGs could contain scripts). If you need this feature, add to mediawiki/config/CustomSettings.php:
$wgAllowSvgDataUriInSvg = true;Warning: This reduces upload security. Only enable this on trusted instances where all uploaders are authenticated.
sudo cp misc/reverse_proxy_nginx.conf /etc/nginx/sites-enabled/default
sudo nano /etc/nginx/sites-enabled/default-> set domain and cert paths
Extension:Iframe enabled. To do so, add the following to your CustomSettings.php
wfLoadExtension( 'Iframe' );
$wgIframe['width'] = "100%"; # example for a default setting
$wgIframe['server']['example'] = [ 'scheme' => 'https', 'domain' => 'example.com' ];
$wgIframe['server']['dashboard'] = [ 'scheme' => 'https', 'domain' => 'user:password@example.com' ]; # example for basic auth
$wgIframe['server']['localhost:20200'] = [ 'scheme' => 'http', 'domain' => 'localhost:20200' ]; # to allow users to test a local running webappTo make use of the whitelisted domains, e.g. as https://subdomain.example.com/example/page&hl=e, add the following to any wiki page or template:
<iframe key="example" level="subdomain" path="example/page&hl=en" /># 2FA, see https://www.mediawiki.org/wiki/Extension:OATHAuth
wfLoadExtension( 'OATHAuth' );
$wgGroupPermissions['user']['oathauth-enable'] = true;
# $wgOATHRequiredForGroups = ['user']; # this will enforce 2FA but can only be applied in private wikis after every user activated it
# make sure to persist $wgSecretKey between updates, otherwise user need to re-register
$wgSecretKey = "...";Currently the default is blazegraph as SPARQL-Store. Since blazegraph is no longer maintained we are transitioning to use Apache Jena Fuseki. To switch to Fuseki, add the following settings to your CustomSettings.php file:
$smwgSparqlRepositoryConnector = 'fuseki';
$smwgSparqlEndpoint["query"] = 'http://fuseki:3030/ds/sparql';
$smwgSparqlEndpoint["update"] = 'http://fuseki:3030/ds/update';and run the stack with
docker compose --profile fuseki upNote: A full data rebuild is required to populate the new store.
to run include sparklis SPARQL editor, run
docker compose --profile fuseki --profile sparklis upor
COMPOSE_PROFILES=fuseki,sparklis docker compose upIf you do not need a SPARQL endpoint, you can switch to SMWElasticStore by reusing the elasticsearch container:
$smwgDefaultStore = 'SMWElasticStore';
$smwgElasticsearchEndpoints = [
[
'host' => 'elasticsearch',
'port' => 9200,
'scheme' => 'http'
]
];Note: Switch store types requires to re-setup the store.
php /var/www/html/w/extensions/SemanticMediaWiki/maintenance/setupStore.php
php /var/www/html/w/extensions/SemanticMediaWiki/maintenance/rebuildElasticIndex.php
php /var/www/html/w/extensions/SemanticMediaWiki/maintenance/rebuildData.phpRun the following commands inside the mediawiki container if you run in one of the following problems
- missing semantic properties after backup restore
php /var/www/html/w/extensions/SemanticMediaWiki/maintenance/rebuildData.php- no search results after backup restore
php /var/www/html/w/extensions/CirrusSearch/maintenance/ForceSearchIndex.php- incorrect link labels (page name instead of display name) after template changes or large imports
php /var/www/html/w/maintenance/refreshLinks.php- missing thumbnails for tif images
php /var/www/html/w/maintenance/refreshImageMetadata.php --force- Error when deleting a file
Error deleting file: Could not create directory "metastore/local-backend/local-deleted/v1/"
Fix the permission on the host
sudo chown -R www-data:www-data mediawiki/dataLarge mysql binlog files (see https://askubuntu.com/questions/1322041/how-to-solve-increasing-size-of-mysql-binlog-files-problem)
List files
docker compose exec db /bin/bash -c 'exec echo "SHOW BINARY LOGS;" | mysql -uroot -p"$MYSQL_ROOT_PASSWORD"'Delete files
docker compose exec db /bin/bash -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD"'
mysql> PURGE BINARY LOGS TO 'binlog.000123';Docker log file size is unlimited in the default settings, see https://stackoverflow.com/questions/42510002/docker-how-to-clear-the-logs-properly-for-a-docker-container
To inspect the file size, run
du -sh -- /var/lib/docker/containers/*/*-json.logTo reset those file (remove all content), run
truncate -s 0 /var/lib/docker/containers/**/*-json.logTo change the setting, adapt /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "1g",
"max-file": "1"
}
}mkdir backup
docker compose exec db /bin/bash -c 'mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD" 2>/dev/null | gzip | base64 -w 0' | base64 -d > backup/db_backup_$(date +"%Y%m%d_%H%M%S").sql.gz
tar -zcf backup/file_backup_$(date +"%Y%m%d_%H%M%S").tar mediawiki/dataTo reset your instance and destroy all data run
docker compose down -v
sudo rm -R mysql/data/* && sudo rm -R blazegraph/data/* && sudo rm -R mediawiki/data/*
docker compose upThis is also required if you change the database passwords after the first run.
Reset your instance first, then import your backup
zcat backup/db_backup_<date>.sql.gz | docker compose exec -T db sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD"'
tar -xf backup/file_backup_<date>.tar
chown -R www-data:www-data mediawiki/dataTo build the MediaWiki image locally instead of pulling the pre-built one:
docker compose build
docker compose upcd /mediawiki/build
docker buildx build --platform=linux/amd64,linux/arm64 --push -t docker.io/opensemanticlab/osl-mw:main-arm64 .MediaWiki's core config file LocalSettings.php is created dynamically on every container by merging
- InstallSettings.php
- DockerSettings.php
- CustomSettings.php
InstallSettings.php is created by running maintenance/install.php with parameters defined in .env on the first run. To recreate this file after change settings in .env set
environment:
- MW_REINSTALL=trueDockerSettings.php is copied from mediawiki/config/DockerSettings.php into the container during build.
CustomSettings.php can be mounted to the container (optional)
volumes:
- ./mediawiki/config/CustomSettings.php:/var/www/html/w/CustomSettings.phpTo modify LocalSettings.php without restarting the container, copy the merged file and mount it, this will skip the dynamical creation:
docker compose cp mediawiki:/var/www/html/w/LocalSettings.php mediawiki/config/LocalSettings.phpin docker-compose.yml:
volumes:
- ./mediawiki/config/LocalSettings.php:/var/www/html/w/LocalSettings.phpcheck for modificated extensions
cd /var/www/html/w/extensions/
find . -maxdepth 1 -mindepth 1 -type d -exec sh -c '(echo {} && cd {} && git status -s && echo)' \;create debug file
touch /var/www/html/w/my-custom-debug.log
chown www-data:www-data /var/www/html/w/my-custom-debug.login LocalSettings.php:
$wgDebugLogFile = "/var/www/html/w/my-custom-debug.log";in PHP source code:
wfDebug( "\n[tag] some debug message: $somevar.\n" );remove and recreate logfile
rm /var/www/html/w/my-custom-debug.log && touch /var/www/html/w/my-custom-debug.log && chown www-data:www-data /var/www/html/w/my-custom-debug.logcopy files
docker compose cp mediawiki/config/pub/. mediawiki:/var/www/html/w/pub/backup extensions
docker compose exec -T mediawiki tar -czf - -C /var/www/html/w/extensions/ . > backup/extensions_backup_$(date +"%Y%m%d_%H%M%S").tartriggers CI/CD workflow and pushes image with tags to docker registry (see also stackoverflow: push-git-commits-tags-simultaneously )
git tag <tag>
git push --atomic origin main --tags
Note: You may have to wait 15 - 30 min for all page packages to be installed on the first run
Pull reqired images (see ./tests/codecept/browsers.json) from docker registry before running codeceptjs:
manually
docker pull selenoid/video-recorder:latest-release;
docker pull selenoid/firefox:latest;
...automated by parsing ./tests/codecept/browsers.json (replace docker run --rm -i imega/jq with jq if installed on your host), see docs
docker pull selenoid/video-recorder:latest-release && cat ./tests/codecept/browsers.json | docker run --rm -i imega/jq -r '..|.image?|strings' | xargs -I{} docker pull {}Note: use kiosk mode for demo video recording
Run all tests with a single browser
docker compose run --rm codeceptjsRun only test with tag @<tag> a single browser
docker compose run --rm codeceptjs codeceptjs run --grep "@<tag>"Run only test with without @<tag> a single browser
docker compose run --rm codeceptjs codeceptjs run --grep "@<tag>" --invertRun multi-browser tests
docker compose run --rm codeceptjs codeceptjs run-multiple --allMore options: https://codecept.io/commands/
You can follow the test execution on selenoid-ui at "http://localhost:8080". Run with autopause to interact with the browser in a state where test have failed
- codeceptjs: container name
- codeceptjs: shell command inside container
docker compose run --rm codeceptjs codeceptjs run -p pauseOnFailCreate a new file ./tests/codecept/tests/_test.js
Follow the existing examples or https://codecept.io/tutorial/
To find XPath expressions and test them in the browser: https://stackoverflow.com/questions/41857614/how-to-find-xpath-of-an-element-in-firefox-inspector
To compare / assert values: https://github.com/SitamJana/codeceptjs-chai