Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/wikioasis/salt/llms.txt

Use this file to discover all available pages before exploring further.

The mediawiki Salt state tree manages the full lifecycle infrastructure for deploying MediaWiki across the WikiOasis fleet. It is split into four sub-states: the main mediawiki state that sets up the staging server with the mwdeploy system user, deployment script, and config; mediawiki.proxy which provisions the deploy user on proxy* servers with HAProxy socket access; mediawiki.target which prepares mw* application servers to receive rsync pushes; and mediawiki.jobrunner which installs and runs the Wikimedia jobrunner and jobchron services for async job processing.

Pillar keys

All keys live under the top-level mediawiki: map in pillar/mediawiki/init.sls.
KeyDefaultDescription
deploy_usermwdeploySystem user that runs all deploy operations
staging_path/srv/mediawiki-stagingPath for the git-managed staging checkout
prod_path/srv/mediawikiProduction MediaWiki path on each server
canary_vhosttest.wikioasis.orgVhost used for canary validation before fleet rollout
log_file/var/log/mwdeploy.logPath to the mwdeploy operation log
haproxy_backendmediawikiHAProxy backend name to drain/restore during deploy
haproxy_socket/run/haproxy/admin.sockPath to the HAProxy admin socket
backup_path/srv/mediawiki-backupPath where the pre-deploy backup is stored
mw_servers[]List of application server FQDNs to push to
proxy_servers[]List of HAProxy server FQDNs to update routing on
ssh_identity/home/mwdeploy/.ssh/id_ed25519SSH private key used for all rsync/SSH operations
deploy_ssh_public_key(none)Public key to authorize on the deploy user’s account
webhooks{}Map of webhook targets; supports discord and slack URL keys

Production pillar values

# pillar/mediawiki/init.sls
mediawiki:
  deploy_user: mwdeploy
  staging_path: /srv/mediawiki-staging
  prod_path: /srv/mediawiki
  canary_vhost: test.wikioasis.org
  log_file: /var/log/mwdeploy.log
  haproxy_backend: mediawiki
  haproxy_socket: /run/haproxy/admin.sock
  backup_path: /srv/mediawiki-backup
  mw_servers:
    - task-us-east-011.ovvin.wonet
    - mw-us-east-011.ovvin.wonet
    - mw-us-east-012.ovvin.wonet
    - mw-us-east-021.ovvin.wonet
    - mw-us-east-022.ovvin.wonet
  proxy_servers:
    - proxy-us-east-011.ovvin.wonet
    - proxy-us-east-021.ovvin.wonet
  deploy_ssh_public_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBw+m6ZQT6Q7MPgfd5STamRLSUADflff/9uKVtbBZluM mwdeploy@staging-us-east-021"
  ssh_identity: /home/mwdeploy/.ssh/id_ed25519
  webhooks:
    discord: ""
    slack: ""

mediawiki (staging server)

Applied to the staging server, this state sets up everything the mwdeploy script needs to operate.
1

Create the deploy user

Creates the mwdeploy system user with a home directory and shell. If deploy_ssh_public_key is set in pillar, that key is added to the user’s authorized_keys so the deploy user can be reached via SSH from the master.
2

Install the mwdeploy script

Manages /usr/local/bin/mwdeploy (mode 0755) from the Salt fileserver.
3

Write /etc/mwdeploy/config.yaml

Renders the Jinja config template with all pillar values. The deploy user is given group read access (0640, group mwdeploy).
4

Pre-create the log file

Creates /var/log/mwdeploy.log owned by the deploy user (replace: false — existing log is never truncated).
5

Create staging and production directories

Creates staging_path and prod_path owned by www-data so git operations (which run as www-data) can write to them.
6

Write sudoers for staging

Creates /etc/sudoers.d/mwdeploy_staging allowing the deploy user to run git and patch as www-data, rsync as root, and the mwscript.php maintenance runner as www-data.

Staging sudoers

mwdeploy ALL=(www-data) NOPASSWD: /usr/bin/git
mwdeploy ALL=(root) NOPASSWD: /usr/bin/rsync
mwdeploy ALL=(www-data) NOPASSWD: /usr/bin/patch
mwdeploy ALL=(www-data) NOPASSWD: /srv/mediawiki/scripts/mwscript.php

config.yaml template

The rendered /etc/mwdeploy/config.yaml is generated from salt://mediawiki/files/config.yaml.jinja:
# mwdeploy configuration — managed by Salt, do not edit manually
{%- set mw = salt['pillar.get']('mediawiki', {}) %}
{%- set webhooks = mw.get('webhooks', {}) %}

deploy_user: {{ mw.get('deploy_user', 'mwdeploy') }}
ssh_identity: {{ mw.get('ssh_identity', '/home/' ~ mw.get('deploy_user', 'mwdeploy') ~ '/.ssh/id_ed25519') }}
staging_path: {{ mw.get('staging_path', '/srv/mediawiki-staging') }}
prod_path: {{ mw.get('prod_path', '/srv/mediawiki') }}
canary_vhost: {{ mw.get('canary_vhost', 'test.wikioasis.org') }}
log_file: {{ mw.get('log_file', '/var/log/mwdeploy.log') }}
haproxy_backend: {{ mw.get('haproxy_backend', 'mediawiki') }}
haproxy_socket: {{ mw.get('haproxy_socket', '/run/haproxy/admin.sock') }}
backup_path: {{ mw.get('backup_path', '/srv/mediawiki-backup') }}

mw_servers:
{%- for server in mw.get('mw_servers', []) %}
  - {{ server }}
{%- endfor %}

proxy_servers:
{%- for server in mw.get('proxy_servers', []) %}
  - {{ server }}
{%- endfor %}

webhooks:
  discord: {{ webhooks.get('discord', '') | tojson }}
  slack: {{ webhooks.get('slack', '') | tojson }}

mediawiki.proxy

Applied to proxy* servers. Creates the mwdeploy system user with membership in the haproxy group so it can write to the HAProxy admin socket at /run/haproxy/admin.sock. This allows the deploy script to drain and restore backends during rolling deploys without requiring root.
salt 'proxy*' state.apply mediawiki.proxy
The mwdeploy user must be in the haproxy group before a deploy is attempted. Run mediawiki.proxy after any new proxy server is provisioned.

mediawiki.target

Applied to mw* application servers. Prepares each backend to receive rsync pushes from the staging server.
  • Creates the mwdeploy system user and .ssh directory
  • Authorizes the deploy_ssh_public_key from pillar (the staging server’s public key)
  • Creates the production directory at prod_path owned by www-data
  • Writes /etc/sudoers.d/mwdeploy_target:
mwdeploy ALL=(root) NOPASSWD: /usr/bin/rsync
mwdeploy ALL=(www-data) NOPASSWD: /srv/mediawiki/scripts/mwscript.php
salt 'mw*' state.apply mediawiki.target

mediawiki.jobrunner

Applied to mw* servers that run async MediaWiki jobs. Installs and runs the Wikimedia mediawiki-services-jobrunner and a companion mediawiki-jobchron cron dispatcher.

What it does

1

Clone the jobrunner repository

Uses git.latest to keep /srv/mediawiki-services-jobrunner up to date from the upstream Wikimedia GitHub repository.
2

Write the config

Renders /etc/mediawiki-jobrunner/config.json from Jinja, populating Redis host, statsd endpoint, job group settings, and the PHP dispatcher command.
3

Install systemd units

Manages /etc/systemd/system/mediawiki-jobrunner.service and mediawiki-jobchron.service from the Salt fileserver.
4

Enable and start services

Starts both services, configured to reload automatically when the repository, config, or unit file changes.

Jobrunner pillar keys

KeyDefaultDescription
mediawiki_jobrunner:redis_hostredis-us-east-012.ovvin.wonetRedis host for job queues and aggregators
mediawiki_jobrunner:redis_port6379Redis port
mediawiki_jobrunner:mw_path/srv/mediawikiPath to the MediaWiki installation
mediawiki_jobrunner:php_binary/usr/bin/phpPHP binary used by the dispatcher

config.json template

{
    "groups": {
        "basic": {
            "runners": 6,
            "include": ["*"],
            "low-priority": ["htmlCacheUpdate", "refreshLinks"]
        }
    },
    "limits": {
        "attempts": { "*": 3 },
        "claimTTL": { "*": 3600 },
        "real": { "*": 300 },
        "memory": { "*": "300M" }
    },
    "redis": {
        "aggregators": ["{{ jr.get('redis_host', 'redis-us-east-012.ovvin.wonet') }}"],
        "queues":      ["{{ jr.get('redis_host', 'redis-us-east-012.ovvin.wonet') }}"]
    },
    "statsd": "{{ monitoring_ip }}:9125",
    "dispatcher": "php /srv/mediawiki/scripts/mwscript.php maintenance/run.php runJobs --wiki=%(db)x --type=%(type)x --maxtime=%(maxtime)x --memory-limit=%(maxmem)x --result=json"
}
salt 'mw*' state.apply mediawiki.jobrunner

Build docs developers (and LLMs) love