Configuration
movefile.yml describes your local install and every remote. It is a YAML file, evaluated as an ERB template first, so it can read environment variables.
- File name and location
- Complete example
- First level keys
globallocaland each remote- Remote-only keys
- Environment variables and
.env - YAML and ERB tips
- Schema validation
File name and location
wordmove-ng looks for movefile.yml, movefile.yaml, movefile or Movefile in the current directory and then in each parent up to the one containing wp-config.php. -c other.yml selects another file inside the project.
Indentation defines the structure. Two spaces per level, no tabs. wordmove-ng doctor validates the file against a schema and tells you what is wrong.
Complete example
global:
maintenance_mode: false
local:
vhost: http://site.test
wordpress_path: /home/john/sites/site
database:
name: site
user: root
password: root
host: 127.0.0.1
# port: 3306
# socket: /path/to/mysqld.sock
staging:
vhost: https://staging.example.com
wordpress_path: /var/www/staging
database:
name: staging
user: staging
password: "<%= ENV['STAGING_DB_PASS'] %>"
host: localhost
ssh:
host: staging.example.com
user: deploy
exclude:
- ".git/"
- ".env"
- "node_modules/"
- "wp-config.php"
- "wp-content/*.sql.gz"
production:
vhost: https://example.com
wordpress_path: /var/www/example
database:
name: example
user: example
password: "<%= ENV['PROD_DB_PASS'] %>"
host: localhost
# mysqldump_options: --max_allowed_packet=50MB
# mysql_options: --protocol=TCP
ssh:
host: example.com
user: deploy
# port: 22
# password: only with sshpass installed; prefer keys
# rsync_options: --verbose
# gateway:
# host: bastion.example.com
# user: jump
exclude:
- ".git/"
- ".env"
- "node_modules/"
- "wp-config.php"
- "wp-content/*.sql.gz"
forbid:
push:
db: true # never push the database to production
hooks:
push:
after:
- command: wp cache flush
where: remote
raise: false
First level keys
local is mandatory and must keep that name. global is optional. Every other first level key is a remote environment, selected with -e. See Multiple environments.
global
| Key | Default | Meaning |
|---|---|---|
maintenance_mode |
false |
Wrap the target’s database import and adaptation in wp maintenance-mode activate/deactivate. WORDMOVE_MAINTENANCE_MODE=1 in the environment forces it on for one run. |
collation_fallbacks |
built-in table | Collations rewritten before import, e.g. utf8mb3_uca1400_ai_ci: utf8mb4_unicode_ci. Setting the key replaces the default table. |
charset_fallbacks |
utf8mb3: utf8mb4 |
Charsets rewritten before import. |
sql_adapter |
ignored | Accepted with a warning for 5.x movefiles. |
local and each remote
vhost
mandatory
The URL you use to reach the site, including any subdirectory WordPress is installed in. It should match wp option get home. Omit the trailing slash. init pre-fills it from wp-config.php or wp option get home.
wordpress_path
mandatory
The absolute path of the installation. Spaces and other shell characters are handled; no manual escaping. On a remote it is the path on that host.
Avoid values where one environment’s vhost or wordpress_path is a prefix of another’s (https://site.test and https://site.test.example.com): the search-replace of the shorter value would also rewrite the longer one. The doctor reports this.
database
mandatory keys:
name,user,password,host
| Key | Meaning |
|---|---|
name, user, password, host |
as in wp-config.php. For a remote, as seen from the remote host: wordmove-ng connects over SSH first and runs mysqldump/mysql/wp there. |
port |
optional; init splits DB_HOST values like localhost:3307. |
socket |
optional unix socket; init splits DB_HOST values like localhost:/path/mysqld.sock. |
mysqldump_options |
passed verbatim to mysqldump/mariadb-dump, e.g. --max_allowed_packet=50MB --ignore-table=db.wp_bigtable. |
mysql_options |
passed verbatim to mysql/mariadb on import, e.g. --protocol=TCP. |
mariadb/mariadb-dump are preferred when installed, falling back to mysql/mysqldump. Imports run with --binary-mode (unless mysql_options already sets it), SET FOREIGN_KEY_CHECKS=0 and a trailing COMMIT;; a MariaDB sandbox header is stripped.
paths
optional
Customise WordPress internal paths when they are not the defaults, relative to wordpress_path. Configure it on every environment whose layout differs.
paths:
wp_content: app
wp_config: config/wp-config.php
uploads: app/uploads
plugins: app/plugins
mu_plugins: app/mu-plugins
themes: app/themes
languages: app/languages
Remote-only keys
ssh
mandatory key:
host
ssh:
host: host
user: user
port: 22 # optional
password: secret # optional; needs sshpass, prefer keys
rsync_options: "--verbose" # optional, appended to rsync
gateway: # optional jump host, becomes `ssh -J`
host: bastion
user: jump
port: 22
Every operation uses the system ssh client: rsync for directories, ssh for remote commands and hooks, scp for dump files. Whatever works for ssh on your command line works here: ssh-agent, ~/.ssh/config, ProxyJump, hardware keys, modern key types. Only host is mandatory because the rest can live in ~/.ssh/config:
Host client_x_staging
Hostname 79.99.8.94
User welaika
Port 1337
ProxyJump bastion
staging:
ssh:
host: client_x_staging
- Without
password, connections run withBatchMode=yes: a failing key authentication is an error naming the failed command, never a prompt. passwordis passed throughsshpass -ptossh,scpandrsync. It requiressshpasslocally and exposes the password in the process list.rsync_optionsare appended to the defaults-rlpt --compress --omit-dir-times --delete. See rsync options for CI.gatewaybecomesssh -J [user@]host[:port]. Agateway.passwordcannot be honoured and is ignored; the doctor warns.- Remote commands always run through
sh -c, so the remote login shell can be bash, zsh, fish or csh. Programs only need to be in the$PATHof a non interactive login.
exclude
optional
Patterns ignored during push and pull, which also means matching files are not deleted on the destination when missing at the source. Each entry becomes an rsync --exclude, relative to wordpress_path, so all rsync pattern syntax applies; see working on specific plugins.
exclude:
- ".git/"
- ".gitignore"
- ".env"
- "node_modules/"
- "tmp/*"
- "movefile.yml"
- "wp-config.php"
- "wp-content/*.sql.gz"
hooks
optional
Commands run before or after a push or pull, locally or on the remote. See Hooks.
forbid
optional
Blocks actions by configuration; forbidden tasks ignore command line flags and are skipped with a warning. Values must be booleans.
forbid:
push:
db: true
uploads: false
plugins: false
themes: false
languages: false
mu_plugins: false
pull:
db: false
Removed: ftp
FTP, FTPS and SFTP transports were removed in 6.0. A remote with an ftp block fails validation with a clear message. See Upgrading.
Environment variables and .env
Because the movefile is an ERB template, secrets can come from the environment:
production:
database:
user: "<%= ENV['PROD_DB_USER'] %>"
password: "<%= ENV['PROD_DB_PASS'] %>"
Set them in the shell (export PROD_DB_PASS="…") or in a .env file next to the movefile:
PROD_DB_USER="username"
PROD_DB_PASS="password"
.env.<environment> (for example .env.production) is loaded when that environment is selected with -e. Add .env* to exclude and to .gitignore.
System variables work too: wordpress_path: "<%= ENV['HOME'] %>/sites/example".
YAML and ERB tips
Anchors to avoid repetition
global:
default: &default
vhost: "http://example.com"
wordpress_path: "/var/www/site"
database: &db
host: localhost
user: username
password: password
name: database
ssh: &ssh
host: server
user: foo
staging:
<<: *default
vhost: http://staging.example.com
database:
<<: *db
name: db_staging
ssh:
<<: *ssh
user: foobar
production:
<<: *default
vhost: https://www.example.com
database:
<<: *db
name: db_production
The default anchor lives under global because every other first level key is treated as an environment.
Variables inside the template
production:
<% prod_wp_path = "/home/site/public" %>
vhost: "https://example.com"
wordpress_path: <%= prod_wp_path %>
hooks:
pull:
after:
- command: 'cd <%= prod_wp_path %> && wp core version'
where: remote
Conditional sections
hooks:
pull:
after:
<% if ENV.fetch('NOTIFY', nil) %>
- command: 'bash ./scripts/notify.sh'
where: local
<% end %>
- command: 'wp cache flush'
where: remote
ENV.fetch('NOTIFY', nil) returns the variable or nil; you can also compare against values (<% if ENV.fetch('TARGET', '') == 'blue' %>).
Schema validation
wordmove-ng doctor validates every section against the schemas in lib/wordmove/assets, reports unknown or mistyped keys, rejects ftp blocks and flags prefix collisions between search terms.