Jump to content

MediaWiki Setup Guide Portainer-Docker-251215-00: Difference between revisions

From Game in the Brain Wiki
No edit summary
Line 1: Line 1:
= MediaWiki Setup Guide (Portainer & Docker) =


= MediaWiki Setup Guide (Portainer & Docker) =
This guide documents how to deploy, configure, and secure a '''MediaWiki''' instance using '''Portainer''' and '''Docker Compose'''. Designed as a general tutorial for any user, it walks through the process of setting up a wiki from scratch. It covers initial deployment, resolving common extension folder issues, and applying production configurations (using <code>wiki.gi7b.org</code> as the example domain).
This guide documents how to deploy, configure, and secure a '''MediaWiki''' instance using '''Portainer''' and '''Docker Compose'''. Designed as a general tutorial for any user, it walks through the process of setting up a wiki from scratch. It covers initial deployment, resolving common extension folder issues, and applying production configurations (using <code>wiki.gi7b.org</code> as the example domain).


'''Reference:''' Official MediaWiki Docker Image https://hub.docker.com/_/mediawiki  
'''Reference:''' Official MediaWiki Docker Image [https://hub.docker.com//mediawiki https://hub.docker.com//mediawiki]


== 1. Prerequisites ==
== 1. Prerequisites ==
Before starting, ensure you have:
Before starting, ensure you have:


1. Docker & Docker Compose
'''Docker & Docker Compose'''
 
#* Installation (Linux/Windows/Mac): [https://docs.docker.com/engine/install/ https://docs.docker.com/engine/install/]
#* Docker Compose Standalone (if needed separately): [https://docs.docker.com/compose/install/ https://docs.docker.com/compose/install/]


    Installation (Linux/Windows/Mac): https://docs.docker.com/engine/install/
'''Portainer CE (Community Edition)'''


    Docker Compose Standalone (if needed separately): https://docs.docker.com/compose/install/
#* Docker Standalone Install Guide: [https://docs.portainer.io/start/install-ce/server/docker/linux https://docs.portainer.io/start/install-ce/server/docker/linux]


2. Portainer CE (Community Edition)
'''Domain & Cloudflare Setup'''


    Docker Standalone Install Guide: https://docs.portainer.io/start/install-ce/server/docker/linux
#* How to Register a Domain with Cloudflare: [https://developers.cloudflare.com/registrar/get-started/register-domain/ https://developers.cloudflare.com/registrar/get-started/register-domain/]
#* Cloudflare Tunnel (Cloudflared) Setup Guide: [https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel/ https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel/]


3. Domain & Cloudflare Setup
'''NGINX Reverse Proxy'''


    How to Register a Domain with Cloudflare: https://developers.cloudflare.com/registrar/get-started/register-domain/
#* Nginx Proxy Manager (Docker Setup): [https://nginxproxymanager.com/guide/#quick-setup https://nginxproxymanager.com/guide/#quick-setup] (This is the standard GUI-based Nginx used in Docker stacks)
#* Nginx Official Docker Image (for raw configuration): [https://hub.docker.com//nginx https://hub.docker.com//nginx]


    Cloudflare Tunnel (Cloudflared) Setup Guide: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel/
== 2. Host Folder Setup ==


4. NGINX Reverse Proxy
Create a dedicated folder for your stack on the '''Docker host'''. This path is critical as it will store your configuration and extensions.


    Nginx Proxy Manager (Docker Setup): [https://nginxproxymanager.com/guide/#quick-setup&#x20;(This&#x20;is&#x20;the&#x20;standard&#x20;GUI-based&#x20;Nginx&#x20;used&#x20;in&#x20;Docker&#x20;stacks) <nowiki>https://nginxproxymanager.com/guide/#quick-setup</nowiki> (This is the standard GUI-based Nginx used in Docker stacks)]
<syntaxhighlight lang="bash">


    Nginx Official Docker Image (for raw configuration): https://hub.docker.com/_/nginx
Run on host terminal


== 2. Host Folder Setup ==
sudo mkdir -p /opt/stacks/mediawiki
Create a dedicated folder for your stack on the '''Docker host'''. This path is critical as it will store your configuration and extensions.
sudo mkdir -p /opt/stacks/mediawiki/extensions
# Run on host terminal
cd /opt/stacks/mediawiki
sudo mkdir -p /opt/stacks/mediawiki
</syntaxhighlight>
sudo mkdir -p /opt/stacks/mediawiki/extensions
cd /opt/stacks/mediawiki


== 3. Deployment (Portainer / Docker Compose) ==
== 3. Deployment (Portainer / Docker Compose) ==
Use the following YAML configuration.
Use the following YAML configuration.


=== In Portainer: ===
=== In Portainer: ===


# Go to '''Stacks''' → '''Add stack'''.
Go to '''Stacks''' → '''Add stack'''.
# Name it mediawiki.
 
# Paste the configuration below into the Web editor.
Name it <code>mediawiki</code>.
# Click '''Deploy the stack'''.
 
Paste the configuration below into the Web editor.
 
Click '''Deploy the stack'''.


=== docker-compose.yml ===
=== docker-compose.yml ===
services:
 
  mediawiki:
<syntaxhighlight lang="yaml">
    image: mediawiki
services:
    container_name: mediawiki
mediawiki:
    restart: always
image: mediawiki
    ports:
container_name: mediawiki
      - 8595:80
restart: always
    depends_on:
ports:
      - database
- 8595:80
    volumes:
depends_on:
      - 230912_images:/var/www/html/images
- database
      # EXTENSIONS: Mounts host folder to container (Requires "Magic Command" step below)
volumes:
      - /opt/stacks/mediawiki/extensions:/var/www/html/extensions
- 230912_images:/var/www/html/images
      # CONFIG: Uncomment the line below AFTER generating LocalSettings.php
# EXTENSIONS: Mounts host folder to container (Requires "Magic Command" step below)
      # - /opt/stacks/mediawiki/LocalSettings.php:/var/www/html/LocalSettings.php:ro
- /opt/stacks/mediawiki/extensions:/var/www/html/extensions
# CONFIG: Uncomment the line below AFTER generating LocalSettings.php
  database:
# - /opt/stacks/mediawiki/LocalSettings.php:/var/www/html/LocalSettings.php:ro
    image: mariadb
 
    container_name: mediawiki-db
database:
    restart: always
image: mariadb
    environment:
container_name: mediawiki-db
      MYSQL_DATABASE: mediawiki
restart: always
      MYSQL_USER: mediawiki
environment:
      MYSQL_PASSWORD: mediawiki
MYSQL_DATABASE: mediawiki
      MYSQL_ROOT_PASSWORD: mediawiki
MYSQL_USER: mediawiki
    volumes:
MYSQL_PASSWORD: mediawiki
      - 230912_db:/var/lib/mysql
MYSQL_ROOT_PASSWORD: mediawiki
volumes:
volumes:
- 230912_db:/var/lib/mysql
  230912_images:
 
  230912_db:
volumes:
230912_images:
230912_db:
</syntaxhighlight>


== 4. First-Time Setup Wizard ==
== 4. First-Time Setup Wizard ==


# Open '''<nowiki>http://localhost:8191</nowiki>''' (or your server IP:8191).
Open <code>http://localhost:8191</code> (or your server IP:8191).
# Follow the prompts. When asked for '''Database Settings''', use:
 
Follow the prompts. When asked for '''Database Settings''', use:


* '''Host:''' database
#* '''Host:''' <code>database</code>
* '''Name:''' mediawiki
#* '''Name:''' <code>mediawiki</code>
* '''User:''' mediawiki
#* '''User:''' <code>mediawiki</code>
* '''Password:''' mediawiki
#* '''Password:''' <code>mediawiki</code>


# Complete the wizard and '''Download LocalSettings.php''' to your computer.
Complete the wizard and '''Download LocalSettings.php''' to your computer.


== 5. Fixing Extensions (The "Magic Command") ==
== 5. Fixing Extensions (The "Magic Command") ==
'''Crucial Step:''' Because we mounted a volume to /extensions, the container's default extensions (VisualEditor, WikiEditor, etc.) are hidden. We must copy them from the image to the host.
 
'''Crucial Step:''' Because we mounted a volume to <code>/extensions</code>, the container's default extensions (VisualEditor, WikiEditor, '''SyntaxHighlight_GeSHi''', etc.) are hidden. We must copy them from the image to the host.
 
=== Step A: Extract Extensions ===


'''Run this on your Host Terminal:'''
'''Run this on your Host Terminal:'''
docker run --rm --entrypoint tar mediawiki -c -C /var/www/html/extensions . | tar -x -C /opt/stacks/mediawiki/extensions
 
<syntaxhighlight lang="bash">
docker run --rm --entrypoint tar mediawiki -c -C /var/www/html/extensions . | tar -x -C /opt/stacks/mediawiki/extensions
</syntaxhighlight>
 
=== Step B: Fix Permissions for Code Highlighting ===
 
The <code>SyntaxHighlight_GeSHi</code> extension (which creates code blocks) requires a specific Python file (<code>pygmentize</code>) to be executable. This permission is often lost during the copy.
 
'''Run this command to fix it:'''
 
<syntaxhighlight lang="bash">
chmod a+x /opt/stacks/mediawiki/extensions/SyntaxHighlight_GeSHi/pygments/pygmentize
</syntaxhighlight>
 
If you skip this, code blocks will not render or will show errors.
 
=== Step C: Add External Extensions ===
 
'''Download Mermaid (External Extension):'''
'''Download Mermaid (External Extension):'''
<cd /opt/stacks/mediawiki/extensions
git clone <nowiki>https://github.com/SemanticMediaWiki/Mermaid.git</nowiki> Mermaid</code>
Verify the folder content: You should see a mix of default extensions and Mermaid:


ls -F /opt/stacks/mediawiki/extensions/
<syntaxhighlight lang="bash">
cd /opt/stacks/mediawiki/extensions
git clone https://github.com/SemanticMediaWiki/Mermaid.git Mermaid
</syntaxhighlight>
 
Verify the folder content. You should see a mix of default extensions and Mermaid:
<code>ls -F /opt/stacks/mediawiki/extensions/</code>


''Output should look like this:''
Output should include: <code>SyntaxHighlight_GeSHi/</code>, <code>VisualEditor/</code>, <code>Mermaid/</code>, <code>WikiEditor/</code>, etc.
<code>AbuseFilter/   CiteThisPage/     Echo/         Interwiki/     Mermaid/         PageImages/       README          SpamBlacklist/          TextExtracts/      WikiEditor/
CategoryTree/  CodeEditor/      Gadgets/    Linter/      MultimediaViewer/  ParserFunctions/  ReplaceText/      SyntaxHighlight_GeSHi/  Thanks/
CheckUser/      ConfirmEdit/      ImageMap/  LoginNotify/  Nuke/              PdfHandler/        Scribunto/      TemplateData/          TitleBlacklist/
Cite/            DiscussionTools/  InputBox/  Math/      OATHAuth/          Poem/            SecureLinkFixer/  TemplateStyles/      VisualEditor/</code>


== 6. Configuring LocalSettings.php ==
== 6. Configuring LocalSettings.php ==
Move the downloaded LocalSettings.php to /opt/stacks/mediawiki/LocalSettings.php.
 
# in linux terminal
Move the downloaded <code>LocalSettings.php</code> to <code>/opt/stacks/mediawiki/LocalSettings.php</code>.
sudo nano /opt/stacks/mediawiki/LocalSettings.php
 
Edit the file (sudo nano /opt/stacks/mediawiki/LocalSettings.php) and make the following changes:
<syntaxhighlight lang="bash">
 
in linux terminal
 
sudo nano /opt/stacks/mediawiki/LocalSettings.php
</syntaxhighlight>
 
Edit the file (<code>sudo nano /opt/stacks/mediawiki/LocalSettings.php</code>) and make the following changes:


=== A. Set the Custom Domain ===
=== A. Set the Custom Domain ===
Find the $wgServer line (around line 30) and change it to your actual domain:
 
## The protocol and server name to use in fully-qualified URLs  
Find the <code>$wgServer</code> line (around line 30) and change it to your actual domain:
$wgServer = "<nowiki>https://wiki.gi7b.org</nowiki>";
 
<syntaxhighlight lang="php">
 
The protocol and server name to use in fully-qualified URLs
 
$wgServer = "https://wiki.gi7b.org";
</syntaxhighlight>


=== B. Add Permissions & Extensions ===
=== B. Add Permissions & Extensions ===
Paste this block at the '''very bottom''' of the file to enable security and extensions.
Paste this block at the '''very bottom''' of the file to enable security and extensions.
<code># -----------------------------------------------------------------------
 
# CUSTOM PERMISSIONS & EXTENSIONS
<syntaxhighlight lang="php">
# -----------------------------------------------------------------------
 
-----------------------------------------------------------------------
# 1. SECURITY: Prevent anonymous editing and account creation
 
$wgGroupPermissions['*']['edit'] = false;
CUSTOM PERMISSIONS & EXTENSIONS
$wgGroupPermissions['*']['createaccount'] = false;
 
-----------------------------------------------------------------------
# 2. BUNDLED EXTENSIONS (Included in Docker image)
 
wfLoadExtension( 'WikiEditor' );
1. SECURITY: Prevent anonymous editing and account creation
wfLoadExtension( 'VisualEditor' );
 
wfLoadExtension( 'CodeEditor' );
$wgGroupPermissions['']['edit'] = false;
wfLoadExtension( 'SyntaxHighlight_GeSHi' );
$wgGroupPermissions['']['createaccount'] = false;
wfLoadExtension( 'Cite' );
 
wfLoadExtension( 'InputBox' );
2. BUNDLED EXTENSIONS (Included in Docker image)
wfLoadExtension( 'Scribunto' );
 
wfLoadExtension( 'AbuseFilter' );
wfLoadExtension( 'WikiEditor' );
wfLoadExtension( 'Gadgets' );
wfLoadExtension( 'VisualEditor' );
wfLoadExtension( 'ParserFunctions' );
wfLoadExtension( 'CodeEditor' );
wfLoadExtension( 'Interwiki' );
wfLoadExtension( 'SyntaxHighlight_GeSHi' ); # REQUIRED for Code Blocks
wfLoadExtension( 'Cite' );
# 3. EXTERNAL EXTENSIONS (Must be manually downloaded to /extensions folder)
wfLoadExtension( 'InputBox' );
wfLoadExtension( 'Mermaid' );
wfLoadExtension( 'Scribunto' );
wfLoadExtension( 'AbuseFilter' );
# 4. VISUALEDITOR CONFIGURATION
wfLoadExtension( 'Gadgets' );
# Enable by default for everyone
wfLoadExtension( 'ParserFunctions' );
$wgDefaultUserOptions['visualeditor-enable'] = 1;
wfLoadExtension( 'Interwiki' );
# Allow VE to work in Docker containers (Fixes "Error contacting Parsoid")
 
$wgVisualEditorParsoidForwardCookies = true;
3. EXTERNAL EXTENSIONS (Must be manually downloaded to /extensions folder)
 
# 5. LUA CONFIGURATION (Required for Scribunto)
wfLoadExtension( 'Mermaid' );
$wgScribuntoDefaultEngine = 'luastandalone';</code>
 
4. VISUALEDITOR CONFIGURATION
 
Enable by default for everyone
 
$wgDefaultUserOptions['visualeditor-enable'] = 1;
 
Allow VE to work in Docker containers (Fixes "Error contacting Parsoid")
 
$wgVisualEditorParsoidForwardCookies = true;
 
5. LUA CONFIGURATION (Required for Scribunto)
 
$wgScribuntoDefaultEngine = 'luastandalone';
</syntaxhighlight>


=== C. Adding additional Extensions (WIP - waiting till v1.46 is released this does not work) ===
=== C. Adding additional Extensions (WIP - waiting till v1.46 is released this does not work) ===
go to the HOST PC running the docker mediawiki. you can ssh into it. as an example lets add [[mediawikiwiki:Extension:Collection|Collections]] and [[mediawikiwiki:Extension:PdfBook|PdFBook]] these two extensions allow you 1: Create a Personal Collection of topics, and 2: Compose a Book - this way the wiki can easily generate a book.


on the terminal or ssh you can:  
Go to the HOST PC running the docker mediawiki. You can ssh into it. As an example, let's add [https://www.mediawiki.org/wiki/Extension:Collection Collections] and [https://www.mediawiki.org/wiki/Extension:PdfBook PdfBook].
cd /opt/stacks/mediawiki/extensions
 
Then in the extensions directory
<syntaxhighlight lang="bash">
git clone <nowiki>https://gerrit.wikimedia.org/r/mediawiki/extensions/Collection</nowiki>
cd /opt/stacks/mediawiki/extensions
you should see Collections directory when you
git clone https://gerrit.wikimedia.org/r/mediawiki/extensions/Collection
ls Collection
git clone https://gerrit.wikimedia.org/r/mediawiki/extensions/PdfBook
then repeat the same steps doing the for PDFbook
</syntaxhighlight>
git clone <nowiki>https://gerrit.wikimedia.org/r/mediawiki/extensions/PdfBook</nowiki>
 
then check if the folder appears
Then edit <code>LocalSettings.php</code>:
ls PdfBook
 
Then go to localsettings.php and edit it.
<syntaxhighlight lang="php">
sudo nano /opt/stacks/mediawiki/LocalSettings.php
 
then you will see:
3. EXTERNAL EXTENSIONS (Must be manually downloaded to /extensions folder)
<code># 3. EXTERNAL EXTENSIONS (Must be manually downloaded to /extensions folder)
 
wfLoadExtension( 'Mermaid' );</code>
wfLoadExtension( 'Mermaid' );
add the other extensions and it should appear like this:
 
<code># 3. EXTERNAL EXTENSIONS (Must be manually downloaded to /extensions folder)
wfLoadExtension( 'Collection' ); // wait till version 1.46
  wfLoadExtension( 'Mermaid' );</code>
 
''wfLoadExtension( 'Collection' ); // wait till version 1.46
wfLoadExtension( 'PdfBook' );
wfLoadExtension( 'PdfBook' ); // or ElectronPdfService
</syntaxhighlight>


== 7. Apply Changes ==
== 7. Apply Changes ==


# '''Mount the settings:''' In Portainer, go to the Stack Editor and '''uncomment''' the LocalSettings.php line.
'''Mount the settings:''' In Portainer, go to the Stack Editor and '''uncomment''' the <code>LocalSettings.php</code> line.
# '''Update the Stack:''' Click "Update the stack".
# '''Run Database Update:''' Run this command to initialize tables for the new extensions:  docker exec -it mediawiki php maintenance/update.php --quick


Your wiki is now live at '''<nowiki>https://wiki.gi7b.org</nowiki>''' with VisualEditor and Mermaid enabled!
'''Update the Stack:''' Click "Update the stack".


== 8. Email Configuration and Admin Set up ==
'''Run Database Update:''' Run this command to initialize tables for the new extensions:
----
 
<syntaxhighlight lang="bash">
docker exec -it mediawiki php maintenance/update.php --quick
</syntaxhighlight>
 
Your wiki is now live at '''https://wiki.gi7b.org''' with VisualEditor, Mermaid, and Code Highlighting enabled!
 
== 8. Email Configuration and Admin Setup ==


=== 1. Email goal (what we were trying to achieve) ===
=== 1. Email goal (what we were trying to achieve) ===
* MediaWiki must be able to '''send email reliably'''
** account confirmation
** password reset
** notifications
* Emails should appear as coming from '''<code>info@gi7b.org</code>'''
* Authentication must work with '''Gmail / Google Workspace'''
* No local SMTP server, no Docker mail container


Success criterion (important to remember):<blockquote>MediaWiki explicitly says '''“email sent”''' — not just “no error”.</blockquote>
MediaWiki must be able to '''send email reliably''' (account confirmation, password reset, notifications).
----


=== 2. App Password (Google side) ===
Emails should appear as coming from <code>info@gi7b.org</code>.


=== Where it lives ===
Authentication must work with '''Gmail / Google Workspace'''.


* '''Google Account settings''', not Gmail UI  <nowiki>https://myaccount.google.com</nowiki>
No local SMTP server, no Docker mail container.


* → Security → App passwords
'''Success criterion:''' MediaWiki explicitly says '''“email sent”''' — not just “no error”.


=== Preconditions ===
=== 2. App Password (Google side) ===


* 2-Step Verification '''must be enabled'''
'''Where it lives:''' Google Account settings (Security → App passwords).
* App type: '''Mail'''
* Device: '''Other''' (e.g. <code>MediaWiki wiki.gi7b.org</code>)


=== Critical detail (easy to miss, caused real failure) ===
'''Preconditions:''' 2-Step Verification '''must be enabled'''.
Google shows the password like this:


But MediaWiki / PHP '''must receive it without spaces''':
'''Critical Detail (The "Space" Trap):'''
: Google displays the password like this: <code>xxxx xxxx xxxx xxxx</code>
: But MediaWiki / PHP '''must receive it without spaces''': <code>xxxxxxxxxxxxxxxx</code>


If you keep the spaces:
:If you keep the spaces, Google returns <code>SMTP 535 5.7.8 BadCredentials</code>.


* Google returns <code>SMTP 535 5.7.8 BadCredentials</code>
=== 3. Real email vs Alias (Authentication Reality) ===
* MediaWiki misleadingly says “invalid characters”


'''This was one of the key fixes.'''
'''Core Rule:''' SMTP login must use a '''real mailbox''', not an alias.
----


=== 3. Real email vs alias (authentication reality) ===
=== Core rule (non-negotiable) ===
<blockquote>'''SMTP login must use a real mailbox, not an alias.'''</blockquote>Examples:
{| class="wikitable"
{| class="wikitable"
!Address type
! Address type !! Can authenticate?
!Can authenticate?
|-
|-
|Real Gmail / Workspace mailbox
| Real Gmail / Workspace mailbox || ✅ Yes
|✅ Yes
|-
|-
|Alias (Send mail as)
| Alias (Send mail as) || ❌ No
|❌ No
|-
|-
|Forward-only address
| Forward-only address || ❌ No
|❌ No
|}
|}
So:


* '''Login / username''' → real mailbox (e.g. <code>admin@gi7b.org</code>, <code>justin@gi7b.org</code>)
'''Login / username''' → Real mailbox (e.g., <code>admin@gi7b.org</code>)
* '''Sender address''' → can be <code>info@gi7b.org</code>


This is how Google enforces auditability and DMARC integrity.
'''Sender address''' → Can be the alias (e.g., <code>info@gi7b.org</code>)
----


=== 4. “Send as” using Cloudflare Email Routing (alias setup) ===
=== 4. "Send as" using Cloudflare Email Routing ===
Your <code>info@gi7b.org</code> address was '''not''' a real mailbox.


It was configured as an alias using '''Cloudflare Email Routing'''.
If <code>info@gi7b.org</code> is an alias using '''Cloudflare Email Routing''':


That’s fine — and correct — for sending.
Cloudflare routes incoming mail for <code>info</code> to your real mailbox.


Reference (as requested, naked link only):
Gmail authenticates as the '''real mailbox'''.


https://www.cloudflare.com/developer-platform/products/email-routing/
MediaWiki sends "from" the alias.


Key idea:
This combination is valid and correct.


* Cloudflare routes mail for <code>info@gi7b.org</code>
=== 5. Final MediaWiki SMTP Configuration ===
* Gmail authenticates as the '''real mailbox'''
* MediaWiki sends “from” the alias


This combination is valid and common.
Add this to <code>LocalSettings.php</code>:
----


=== 5. Final MediaWiki SMTP configuration (conceptual) ===
<syntaxhighlight lang="php">
$wgEnableEmail = true;
$wgEnableEmail = true;
$wgEnableUserEmail = true;
$wgEnableUserEmail = true;
$wgSMTP = [
  'host'    => 'ssl://smtp.gmail.com',
  'IDHost'  => 'wiki.gi7b.org',
  'port'    => 465,
  'auth'    => true,
  'username' => 'admin@gi7b.org',  // REAL mailbox
  'password' => 'APP_PASSWORD_OF_ADMIN',
];
$wgPasswordSender = 'info@gi7b.org'; // ALIAS is fine here
What mattered conceptually (not copying exact secrets):


* <code>username</code> = '''real mailbox'''
$wgSMTP = [
* <code>password</code> = '''App Password, no spaces'''
'host'     => 'ssl://smtp.gmail.com',
* <code>wgPasswordSender</code> = alias (<code>info@gi7b.org</code>)
'IDHost'   => 'wiki.gi7b.org',
* SMTP host = <code>smtp.gmail.com</code>
'port'     => 465,
* TLS/SSL enabled
'auth'     => true,
'username' => 'admin@gi7b.org',  // REAL mailbox (replace with yours)
'password' => 'APP_PASSWORD_NO_SPACES', // Your Google App Password
];


Once all three conditions were true:
$wgPasswordSender = 'info@gi7b.org'; // ALIAS is fine here
</syntaxhighlight>


# real mailbox for login
=== 6. Testing and Defining "Success" ===
# app password without spaces
# alias only used for sending


→ Google accepted authentication.
'''Failure:''' MediaWiki shows "authentication failure".
----


=== 6. Testing and defining “success” ===
'''Success:''' MediaWiki returns '''"email sent"'''.


=== What failed before ===
=== 7. Final Step: Making your Account Admin ===


* Account confirmation attempted
Once email is stable, use the MediaWiki maintenance runner to promote your account.
* Gmail rejected auth
* MediaWiki showed:


=== What changed after fixes ===
'''Command:'''
<syntaxhighlight lang="bash">
docker exec -it mediawiki php maintenance/createAndPromote.php --sysop --bureaucrat YourUsername
</syntaxhighlight>


* Retried account confirmation
(Note: If the user already exists, you may need to use <code>maintenance/run.php userOptions</code> or edit the database directly if <code>createAndPromote</code> complains, but often <code>createAndPromote</code> handles existing users gracefully or you can use the UserRights page if you have another admin).
* MediaWiki returned:<blockquote>'''“email sent”'''</blockquote>


=== This is the success criterion ===
'''Alternative (UI Method):'''
Not:
If you are the first user (Sysop) created during installation, go to <code>Special:UserRights</code> to manage permissions.


* “no error”
=== 8. Final Mental Model ===
* “page didn’t crash”


But explicitly:<blockquote>'''MediaWiki confirms the email was sent'''</blockquote>That tells you:
'''Gmail''' = SMTP server.


* SMTP auth works
'''MediaWiki''' = SMTP client.
* Mailer pipeline is functional
* Future account/password flows will work


----
'''App Password''' = Machine credential (no spaces!).


=== 7. Final step: making your account admin ===
'''Real Mailbox''' = Authenticates (Security).
Once email was stable, we finalized control.


=== What we did ===
'''Alias''' = Presentation (What users see).


* Used the MediaWiki maintenance runner
== 9. Protect your account from brute force ==
* Promoted your existing account
* Forced promotion because the user already existed


Command (conceptually):
Add to <code>LocalSettings.php</code>:


* promote <code>justinaquino</code>
<syntaxhighlight lang="php">
* assign <code>sysop</code> + <code>bureaucrat</code>
* confirmed by:


=== Important nuance ===
Login attempt throttling


* Some helper scripts (<code>showUserRights</code>, <code>version</code>) are missing in newer MediaWiki builds
$wgRateLimits['user']['login'] = [ 5, 60 ];  // 5 attempts per minute
* That '''does not invalidate''' the promotion
$wgRateLimits['ip']['login']  = [ 20, 300 ]; // 20 attempts per 5 minutes
* UI verification (<code>Special:UserRights</code>, <code>Special:CreateAccount</code>) is canonical
</syntaxhighlight>


----
== 10. How to use Code Blocks ==


=== 8. Final mental model (so this sticks) ===
Once <code>SyntaxHighlight_GeSHi</code> is active (see Section 5):
* '''Email'''
** Gmail = SMTP server
** MediaWiki = SMTP client
** App Password = machine credential
** Alias ≠ login identity
* '''Security'''
** Real mailbox authenticates
** Alias is presentation only
** Cloudflare handles routing, not auth
* '''Success'''
** “Email sent” is the only signal that matters
** Admin rights are confirmed via UI, not missing CLI helpers


== 9. Protect your account from brute force (real defenses) ==
'''VisualEditor:''' Click '''Insert''' > '''More''' > '''Code Block'''.
MediaWiki already has '''some protection''', but we’ll harden it properly.


=== Layer 1 — Enable built-in throttling (do this) ===
'''Source Editor:''' Use the tag:
Add to <code>LocalSettings.php</code>:
 
# Login attempt throttling
<syntaxhighlight lang="html">
$wgRateLimits['user']['login'] = [ 5, 60 ];  // 5 attempts per minute
<syntaxhighlight lang="python" copy>
$wgRateLimits['ip']['login']  = [ 20, 300 ]; // 20 attempts per 5 minutes
print("This code has a copy button!")
This alone stops most brute-force scripts.
</syntaxhighlight>
</syntaxhighlight>
 
:*Note: The <code>copy</code> attribute adds a copy-to-clipboard button (available in newer MediaWiki versions).
 
== 11. List of Extensions ==


== 10. List of Extensions ==
[https://www.youtube.com/watch?v=4xYbqbabTwI Youtube: Introduction to MediaWiki: Wikipedia's extensions (Part 2)]
[https://www.youtube.com/watch?v=4xYbqbabTwI Youtube: Introduction to MediaWiki: Wikipedia's extensions (Part 2)]
# '''[[wikipedia:Special:Version|The installed Extensions of Wikipedia]]'''
 
#
[https://en.wikipedia.org/wiki/Special:Version The installed Extensions of Wikipedia]
 
== 12. Quick Guide: Adding or Disabling Extensions (Refresher) ==
 
If you have already set up the wiki and just need to add or remove a feature, follow this checklist.
 
'''1. Go to the Extensions Folder'''
Run this on your host machine:
 
<syntaxhighlight lang="bash">
cd /opt/stacks/mediawiki/extensions
</syntaxhighlight>
 
'''2. Download the Extension'''
Find the extension on [https://www.mediawiki.org/wiki/Special:ExtensionDistributor MediaWiki.org]. Use <code>git</code> to download it (replace <code>ExtensionName</code> with the actual name).
 
<syntaxhighlight lang="bash">
 
Example: Downloading "Math" extension
 
git clone https://www.google.com/search?q=https://gerrit.wikimedia.org/r/mediawiki/extensions/Math.git
</syntaxhighlight>
 
''Tip: Ensure the folder name matches exactly what <code>wfLoadExtension</code> expects.''
 
'''3. Edit Configuration'''
Open your settings file:
 
<syntaxhighlight lang="bash">
sudo nano /opt/stacks/mediawiki/LocalSettings.php
</syntaxhighlight>
 
'''4. Enable or Disable'''
Scroll to the bottom.
 
'''To Enable:''' Add <code>wfLoadExtension( 'ExtensionName' );</code>
 
'''To Disable:''' Add a <code>#</code> at the start of the line: <code># wfLoadExtension( 'ExtensionName' );</code>
 
'''5. Apply Changes'''
Save the file (<code>Ctrl+O</code>, <code>Enter</code>, <code>Ctrl+X</code>). Then run the update script inside the container:
 
<syntaxhighlight lang="bash">
docker exec -it mediawiki php maintenance/update.php --quick
</syntaxhighlight>
 
Note: This step is required whenever you add an extension that modifies the database.

Revision as of 09:03, 23 January 2026

MediaWiki Setup Guide (Portainer & Docker)

This guide documents how to deploy, configure, and secure a MediaWiki instance using Portainer and Docker Compose. Designed as a general tutorial for any user, it walks through the process of setting up a wiki from scratch. It covers initial deployment, resolving common extension folder issues, and applying production configurations (using wiki.gi7b.org as the example domain).

Reference: Official MediaWiki Docker Image https://hub.docker.com//mediawiki

1. Prerequisites

Before starting, ensure you have:

Docker & Docker Compose

Portainer CE (Community Edition)

Domain & Cloudflare Setup

NGINX Reverse Proxy

2. Host Folder Setup

Create a dedicated folder for your stack on the Docker host. This path is critical as it will store your configuration and extensions.

Run on host terminal

sudo mkdir -p /opt/stacks/mediawiki
sudo mkdir -p /opt/stacks/mediawiki/extensions
cd /opt/stacks/mediawiki

3. Deployment (Portainer / Docker Compose)

Use the following YAML configuration.

In Portainer:

Go to StacksAdd stack.

Name it mediawiki.

Paste the configuration below into the Web editor.

Click Deploy the stack.

docker-compose.yml

services:
mediawiki:
image: mediawiki
container_name: mediawiki
restart: always
ports:
- 8595:80
depends_on:
- database
volumes:
- 230912_images:/var/www/html/images
# EXTENSIONS: Mounts host folder to container (Requires "Magic Command" step below)
- /opt/stacks/mediawiki/extensions:/var/www/html/extensions
# CONFIG: Uncomment the line below AFTER generating LocalSettings.php
# - /opt/stacks/mediawiki/LocalSettings.php:/var/www/html/LocalSettings.php:ro

database:
image: mariadb
container_name: mediawiki-db
restart: always
environment:
MYSQL_DATABASE: mediawiki
MYSQL_USER: mediawiki
MYSQL_PASSWORD: mediawiki
MYSQL_ROOT_PASSWORD: mediawiki
volumes:
- 230912_db:/var/lib/mysql

volumes:
230912_images:
230912_db:

4. First-Time Setup Wizard

Open http://localhost:8191 (or your server IP:8191).

Follow the prompts. When asked for Database Settings, use:

    • Host: database
    • Name: mediawiki
    • User: mediawiki
    • Password: mediawiki

Complete the wizard and Download LocalSettings.php to your computer.

5. Fixing Extensions (The "Magic Command")

Crucial Step: Because we mounted a volume to /extensions, the container's default extensions (VisualEditor, WikiEditor, SyntaxHighlight_GeSHi, etc.) are hidden. We must copy them from the image to the host.

Step A: Extract Extensions

Run this on your Host Terminal:

docker run --rm --entrypoint tar mediawiki -c -C /var/www/html/extensions . | tar -x -C /opt/stacks/mediawiki/extensions

Step B: Fix Permissions for Code Highlighting

The SyntaxHighlight_GeSHi extension (which creates code blocks) requires a specific Python file (pygmentize) to be executable. This permission is often lost during the copy.

Run this command to fix it:

chmod a+x /opt/stacks/mediawiki/extensions/SyntaxHighlight_GeSHi/pygments/pygmentize

If you skip this, code blocks will not render or will show errors.

Step C: Add External Extensions

Download Mermaid (External Extension):

cd /opt/stacks/mediawiki/extensions
git clone https://github.com/SemanticMediaWiki/Mermaid.git Mermaid

Verify the folder content. You should see a mix of default extensions and Mermaid: ls -F /opt/stacks/mediawiki/extensions/

Output should include: SyntaxHighlight_GeSHi/, VisualEditor/, Mermaid/, WikiEditor/, etc.

6. Configuring LocalSettings.php

Move the downloaded LocalSettings.php to /opt/stacks/mediawiki/LocalSettings.php.

in linux terminal

sudo nano /opt/stacks/mediawiki/LocalSettings.php

Edit the file (sudo nano /opt/stacks/mediawiki/LocalSettings.php) and make the following changes:

A. Set the Custom Domain

Find the $wgServer line (around line 30) and change it to your actual domain:

The protocol and server name to use in fully-qualified URLs

$wgServer = "https://wiki.gi7b.org";

B. Add Permissions & Extensions

Paste this block at the very bottom of the file to enable security and extensions.

-----------------------------------------------------------------------

CUSTOM PERMISSIONS & EXTENSIONS

-----------------------------------------------------------------------

1. SECURITY: Prevent anonymous editing and account creation

$wgGroupPermissions['']['edit'] = false;
$wgGroupPermissions['']['createaccount'] = false;

2. BUNDLED EXTENSIONS (Included in Docker image)

wfLoadExtension( 'WikiEditor' );
wfLoadExtension( 'VisualEditor' );
wfLoadExtension( 'CodeEditor' );
wfLoadExtension( 'SyntaxHighlight_GeSHi' ); # REQUIRED for Code Blocks
wfLoadExtension( 'Cite' );
wfLoadExtension( 'InputBox' );
wfLoadExtension( 'Scribunto' );
wfLoadExtension( 'AbuseFilter' );
wfLoadExtension( 'Gadgets' );
wfLoadExtension( 'ParserFunctions' );
wfLoadExtension( 'Interwiki' );

3. EXTERNAL EXTENSIONS (Must be manually downloaded to /extensions folder)

wfLoadExtension( 'Mermaid' );

4. VISUALEDITOR CONFIGURATION

Enable by default for everyone

$wgDefaultUserOptions['visualeditor-enable'] = 1;

Allow VE to work in Docker containers (Fixes "Error contacting Parsoid")

$wgVisualEditorParsoidForwardCookies = true;

5. LUA CONFIGURATION (Required for Scribunto)

$wgScribuntoDefaultEngine = 'luastandalone';

C. Adding additional Extensions (WIP - waiting till v1.46 is released this does not work)

Go to the HOST PC running the docker mediawiki. You can ssh into it. As an example, let's add Collections and PdfBook.

cd /opt/stacks/mediawiki/extensions
git clone https://gerrit.wikimedia.org/r/mediawiki/extensions/Collection
git clone https://gerrit.wikimedia.org/r/mediawiki/extensions/PdfBook

Then edit LocalSettings.php:

3. EXTERNAL EXTENSIONS (Must be manually downloaded to /extensions folder)

wfLoadExtension( 'Mermaid' );

wfLoadExtension( 'Collection' ); // wait till version 1.46

wfLoadExtension( 'PdfBook' );

7. Apply Changes

Mount the settings: In Portainer, go to the Stack Editor and uncomment the LocalSettings.php line.

Update the Stack: Click "Update the stack".

Run Database Update: Run this command to initialize tables for the new extensions:

docker exec -it mediawiki php maintenance/update.php --quick

Your wiki is now live at https://wiki.gi7b.org with VisualEditor, Mermaid, and Code Highlighting enabled!

8. Email Configuration and Admin Setup

1. Email goal (what we were trying to achieve)

MediaWiki must be able to send email reliably (account confirmation, password reset, notifications).

Emails should appear as coming from info@gi7b.org.

Authentication must work with Gmail / Google Workspace.

No local SMTP server, no Docker mail container.

Success criterion: MediaWiki explicitly says “email sent” — not just “no error”.

2. App Password (Google side)

Where it lives: Google Account settings (Security → App passwords).

Preconditions: 2-Step Verification must be enabled.

Critical Detail (The "Space" Trap):

Google displays the password like this: xxxx xxxx xxxx xxxx
But MediaWiki / PHP must receive it without spaces: xxxxxxxxxxxxxxxx
If you keep the spaces, Google returns SMTP 535 5.7.8 BadCredentials.

3. Real email vs Alias (Authentication Reality)

Core Rule: SMTP login must use a real mailbox, not an alias.

Address type Can authenticate?
Real Gmail / Workspace mailbox ✅ Yes
Alias (Send mail as) ❌ No
Forward-only address ❌ No

Login / username → Real mailbox (e.g., admin@gi7b.org)

Sender address → Can be the alias (e.g., info@gi7b.org)

4. "Send as" using Cloudflare Email Routing

If info@gi7b.org is an alias using Cloudflare Email Routing:

Cloudflare routes incoming mail for info to your real mailbox.

Gmail authenticates as the real mailbox.

MediaWiki sends "from" the alias.

This combination is valid and correct.

5. Final MediaWiki SMTP Configuration

Add this to LocalSettings.php:

$wgEnableEmail = true;
$wgEnableUserEmail = true;

$wgSMTP = [
'host'     => 'ssl://smtp.gmail.com',
'IDHost'   => 'wiki.gi7b.org',
'port'     => 465,
'auth'     => true,
'username' => 'admin@gi7b.org',   // REAL mailbox (replace with yours)
'password' => 'APP_PASSWORD_NO_SPACES', // Your Google App Password
];

$wgPasswordSender = 'info@gi7b.org'; // ALIAS is fine here

6. Testing and Defining "Success"

Failure: MediaWiki shows "authentication failure".

Success: MediaWiki returns "email sent".

7. Final Step: Making your Account Admin

Once email is stable, use the MediaWiki maintenance runner to promote your account.

Command:

docker exec -it mediawiki php maintenance/createAndPromote.php --sysop --bureaucrat YourUsername

(Note: If the user already exists, you may need to use maintenance/run.php userOptions or edit the database directly if createAndPromote complains, but often createAndPromote handles existing users gracefully or you can use the UserRights page if you have another admin).

Alternative (UI Method): If you are the first user (Sysop) created during installation, go to Special:UserRights to manage permissions.

8. Final Mental Model

Gmail = SMTP server.

MediaWiki = SMTP client.

App Password = Machine credential (no spaces!).

Real Mailbox = Authenticates (Security).

Alias = Presentation (What users see).

9. Protect your account from brute force

Add to LocalSettings.php:

Login attempt throttling

$wgRateLimits['user']['login'] = [ 5, 60 ];   // 5 attempts per minute
$wgRateLimits['ip']['login']   = [ 20, 300 ]; // 20 attempts per 5 minutes

10. How to use Code Blocks

Once SyntaxHighlight_GeSHi is active (see Section 5):

VisualEditor: Click Insert > More > Code Block.

Source Editor: Use the tag:

<syntaxhighlight lang="python" copy>
print("This code has a copy button!")

</syntaxhighlight>

  • Note: The copy attribute adds a copy-to-clipboard button (available in newer MediaWiki versions).

11. List of Extensions

Youtube: Introduction to MediaWiki: Wikipedia's extensions (Part 2)

The installed Extensions of Wikipedia

12. Quick Guide: Adding or Disabling Extensions (Refresher)

If you have already set up the wiki and just need to add or remove a feature, follow this checklist.

1. Go to the Extensions Folder Run this on your host machine:

cd /opt/stacks/mediawiki/extensions

2. Download the Extension Find the extension on MediaWiki.org. Use git to download it (replace ExtensionName with the actual name).

Example: Downloading "Math" extension

git clone https://www.google.com/search?q=https://gerrit.wikimedia.org/r/mediawiki/extensions/Math.git

Tip: Ensure the folder name matches exactly what wfLoadExtension expects.

3. Edit Configuration Open your settings file:

sudo nano /opt/stacks/mediawiki/LocalSettings.php

4. Enable or Disable Scroll to the bottom.

To Enable: Add wfLoadExtension( 'ExtensionName' );

To Disable: Add a # at the start of the line: # wfLoadExtension( 'ExtensionName' );

5. Apply Changes Save the file (Ctrl+O, Enter, Ctrl+X). Then run the update script inside the container:

docker exec -it mediawiki php maintenance/update.php --quick

Note: This step is required whenever you add an extension that modifies the database.