README
WordPress Docker Local Environment CLI
The purpose of this library is for easy creation and management of local WordPress development environments with Docker. Essentially, this library uses visiblevc
's excellent Docker images as the base and adds CLI options which make environment management considerably easier. With most other tools the user must manually create/edit/delete docker-compose.yml
files. This library foregoes that option in favor of JSON
files specifically made for managing WordPress environments. The user does not need to have any prior knowledge of Docker to use this tool.
Features of this library include:
- Mounting plugins/themes as Docker volumes regardless of their path on the host system
- Automatic database creation from an SQL dump file on start-up
- Automatic URL replacement after an SQL dump on start-up
- Downloading and installing plugins/themes automatically on start-up
- Downloading and installing private plugins/themes via SSH/FTP automatically on start-up
- XDebug built-in for easy debugging of local environments using the same port number for every environment
- Easy exporting of an environments database as a
.sql
dump file ngrok
support for easy SSL testing on localhost- A single
phpMyAdmin
container for all of your environments - Configurable PHP environment variables
- Ability to specify PHP version (
7.2
,7.3
, or7.4
) - Ability to specify WordPress version
- Local SMTP mail server with MailHog
This library is only for managing development environments and is not intended for creating production ready containers.
Table of Contents
- Requirements
- Installation
- Quickstart Guide
- Examples
- CLI Usage
- Environments
- MailHog
- PHP Debugging
- JSON Structure
- FTP/SSH Config Files
Requirements
- nodeć»npm
- docker
Installation
Install globally with npm
.
$ npm -g install @aivec/wp-docker-dev-factory
Quickstart Guide
To spin-up a minimal environment, create a file named wp-instances.json
with the following contents:
{
"instanceName": "my-local-wordpress",
"containerPort": 8000,
"locale": "en_US",
"downloadPlugins": ["wordpress-plugin-1", "wordpress-plugin-2"],
"downloadThemes": ["wordpress-theme-1", "wordpress-theme-2"],
"localPlugins": [
"/absolute/path/to/plugin/directory",
"relative/path/to/plugin/directory",
"../relative/path/to/plugin/directory"
],
"localThemes": [
"/absolute/path/to/theme/directory",
"relative/path/to/theme/directory",
"../relative/path/to/theme/directory"
]
}
Where:
instanceName
is the title of your website.containerPort
is the port number the environment will expose. In this case the final URL will belocalhost:8000
.locale
is the language you want for the WordPress install.downloadPlugins
is a list of any number of publicly available WordPress plugins to be downloaded.downloadThemes
is a list of any number of publicly available WordPress themes to be downloaded.localPlugins
is a list of absolute or relative paths to any number of local plugin folders.localThemes
is a list of absolute or relative paths to any number of local theme folders.
After setting up your config file, invoke the CLI tool from within the folder where your wp-instances.json
file is saved:
$ aivec-wpdocker
A select prompt will appear:

Press the enter key on Run Containers
and wait for the containers to be created (if this is your first time it might take a while). After the containers are created, open your browser and navigate to localhost:8000/wp-admin.
You should see the WordPress login screen. Login with the default username and password root
.
That's it!
A full example config can be found here. For a detailed description of every setting, refer to the JSON Structure section.
CLI Usage
The CLI is completely interactive. There are two ways to use it:
- With no arguments
- With a relative/absolute path
If you invoke aivec-wpdocker
with no arguments, it will look for a wp-instances.json
file in the current directory.
Alternatively, you can pass a relative or absolute path as an argument to tell the CLI where it should look for a wp-instances.json
file. For example, assuming you have a wp-instances.json
file in a folder called configs
relative to the current directory, you would invoke the CLI like so:
$ aivec-wpdocker configs
The CLI has seven different operations:
| Operation | Description |
| ----- | ----------- |
| Run Containers
| This will create and run the MySQL
and phpMyAdmin
containers if they are not already created and running, as well as start the WordPress
container. If the environment's WordPress
container is already running the CLI will abort with an error. Note that exiting with Ctrl+c will only stop the log stream, not the containers |
| Stop WordPress Container
| This will stop the WordPress
container for the selected environment. It will not stop the MySQL
and phpMyAdmin
containers. |
| Launch NGROK (local SSL)
| This will start the ngrok
client for local SSL. Ctrl+c to stop the client. If you use ngrok
, we highly recommend creating a free account on ngrok.com so that you get more connections per minute. |
| Log WordPress Container
| By default, when you start a WordPress
container with Run Containers
, it will stream the Apache
logs to standard input. You can use this command to pipe the log stream to your console again if you have exited the stream. |
| Overwrite host dumpfile with DB of currently mounted volume
| This will only work if you specified mysqlDumpfile in your config. By invoking this command, the dumpfile, which is mounted as a volume in the container, will be overwritten with a dump of the database of the selected environment |
| Create new dumpfile with DB of currently mounted volume
| This will create a dumpfile from the database of the selected environment and prompt the user to name the dumpfile. The resultant dumpfile will be placed in a folder called dumpfiles
in the same folder as wp-instances.json
. If a dumpfiles
folder does not already exist, it will be created. |
| Replace plugin volume with deployment ready bundle (Toggle)
| NOT YET IMPLEMENTED. This operation will prompt the user to select a plugin from a list of mounted plugins for the selected environment. If the plugin contains a script named bundle.sh
, it will be executed and the contents of the generated .zip
file will temporarily replace the plugin folder contents. Running this command again will revert the plugin back to its original contents. If a bundle.sh
script does not exist or cannot be executed, an error will occur. |
Environments
Config files can contain any number of environments. To do so, wrap your config objects in an array:
[
{
"instanceName": "my-local-wordpress-1",
"containerPort": 8000,
"locale": "en_US",
},
{
"instanceName": "my-local-wordpress-2",
"containerPort": 8010,
"locale": "en_US",
},
]
The CLI will then prompt you to choose which environment to use:

Every environment has exactly one WordPress
container associated with it. Conversely, there is only one MySQL
and phpMyAdmin
container used for the databases of all WordPress
environments. The naming pattern for containers is as follows:
| Container | Name | Example |
| ----- | ---- | ------ |
| MySQL
| aivec_wp_mysql | |
| phpMyAdmin
| aivec_wp_pma | |
| WordPress
| instanceName + -phpVersion_dev_wp | test-wordpress-7.3_dev_wp |
Logging in
You can access phpMyAdmin
at localhost:22222 with the following login information:
- Username:
root
- Password:
root
For WordPress
environments that do not specify a mysqlDumpfile, the login information is the same:
- Username:
root
- Password:
root
Lifecycle details
The database for each WordPress environment will only be created the first time that environment is started, regardless of whether you set a mysqlDumpfile or not. This is because even if you stop the WordPress container, the MySQL
container will continue to run. The next time you run the containers, the database will already exist so it will not be created. If you want the database to be re-created every time you run the containers, set flushOnRestart to true
.
MailHog
Emails sent via WordPress' built-in wp_mail
function are caught by MailHog. All outgoing email can be viewed in a web UI at localhost:8025
PHP Debugging
Any environment you create will have XDebug
installed and configured by default listening on port 9900
. Visual Studio Code users can debug with the PHP Debug extension. Create a launch.json
file and place it either in the .vscode
directory of a workspace folder for a plugin/theme, or the .vscode
directory of a workspace folder specifically for managing PHP debugging.
For users who have many plugins and themes scattered across their filesystem, we recommend creating a workspace folder for managing path mappings of all of those environments with a launch.json
file like the following:
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for XDebug",
"type": "php",
"request": "launch",
"port": 9900,
"pathMappings": {
"/var/www/html/wp-content/plugins/my-plugin": "/home/user/path/to/my-plugin",
"/var/www/html/wp-content/themes/my-theme": "/home/user/path/to/my-theme"
}
}
]
}
For users who would rather have a separate launch.json
file for each of their plugin/theme workspaces, the file would look something like this:
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for XDebug",
"type": "php",
"request": "launch",
"port": 9900,
"pathMappings": {
"/var/www/html/wp-content/plugins/my-plugin": "{workspaceRoot}"
}
}
]
}
Some users prefer to keep all of their plugins and themes in a local WordPress installation folder. For those users, only one launch.json
file is required:
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for XDebug",
"type": "php",
"request": "launch",
"port": 9900,
"pathMappings": {
"/var/www/html/wp-content": "/absolute/path/to/my/local/wordpress/wp-content"
}
}
]
}
JSON Structure
Reference for the wp-instances.json
config file.
-- instanceName
- Required
- Type:
String
- Description: The
instanceName
becomes the title of your website. Note that even if you import a database with an SQL dumpfile that contains a WordPress install with a different title,instanceName
will override it.
-- containerPort
- Required
- Type:
Number
- Description: This is the port number for the WordPress installation. The final URL is
localhost
with this port number appended. AcontainerPort
of 8000 would result inlocalhost:8000
.
-- phpVersion
- Optional
- Type:
String
- Default:
7.3
- Description: This is the PHP version Apache will use. Possible values are
7.2
,7.3
, and7.4
.
-- wordpressVersion
- Optional
- Type:
String
- Default:
latest
- Description: Specify the WordPress version to install. Accepts any valid semver number,
latest
, ornightly
for beta builds. Note thatnightly
builds only support theen_US
locale. If you set this tonightly
and the locale is noten_US
,en_US
will be used anyways.
-- locale
- Optional
- Type:
String
- Default:
en_US
- Description: This is the locale used by WordPress to determine which language to use.
-- customInitScripts
- Optional
- Type:
String[]
- Description: May be any number of absolute or relative paths to
sh
orbash
scripts to be executed at the end of environment creation. Note that absolute paths are resolved starting from your home directory and relative paths are resolved starting from the folder of thewp-instances.json
config file
-- database
- Optional
- Type:
Object
-- database.mysqlDumpfile
- Optional
- Type:
String
- Description: A relative or absolute path to a
MySQL
dump file with the extension.sql
. Note that absolute paths are resolved starting from your home directory and relative paths are resolved starting from the folder of thewp-instances.json
config file
-- database.flushOnRestart
- Optional
- Type:
Boolean
- Default:
false
- Description: If
true
, the database will be re-created every time the WordPress container is stopped and started again.
-- database.dbName
- Optional
- Type:
String
- Description: By default, if you do not specify a
mysqlDumpfile
then the database name will become theinstanceName
. If you do specify amysqlDumpfile
and the database name therein is different than theinstanceName
, you must define this property with the database name defined in the dump file.
-- database.dbPrefix
- Optional
- Type:
String
- Default:
wp_
- Description: This is the prefix for table names. Again, if
mysqlDumpfile
is specified but the table prefix therein differs from the default, you must define this property.
Example
{
"database": {
"mysqlDumpfile": "dumpfiles/testdatabase.sql",
"flushOnRestart": true,
"dbName": "dbname",
"dbPrefix": "dbprefix_"
}
}
-- env
- Optional
- Type:
Object
- Description: May be any number of arbitrary key-value pairs to be set as PHP environment variables at start-up. The environment variables can then be accessed in PHP via the PHP environmment global
$_ENV
-- downloadPlugins
- Optional
- Type:
String[]
- Description: May be any number of plugins that you want to be installed during environment creation. You must use the plugin
slug
(name of the plugins base folder), not the name. Also, the plugin must be available onwordpress.org
.
-- downloadThemes
- Optional
- Type:
String[]
- Description: May be any number of themes that you want to be installed during environment creation. You must use the theme
slug
(name of the themes base folder), not the name. Also, the theme must be available onwordpress.org
.
-- localPlugins
- Optional
- Type:
String[]
- Description: May be any number of relative or absolute paths pointing to local plugins that you want mapped into the container. Note that absolute paths are resolved starting from your home directory and relative paths are resolved starting from the folder of the
wp-instances.json
config file
-- localThemes
- Optional
- Type:
String[]
- Description: May be any number of relative or absolute paths pointing to local themes that you want mapped into the container. Note that absolute paths are resolved starting from your home directory and relative paths are resolved starting from the folder of the
wp-instances.json
config file
-- ftp
- Optional
- Type:
Object[]
- Description:
ftp
may contain an array of any number offtp
config objects
-- ftp[{}.confname]
- Optional*
- Type:
String
- Description: Can refer to either a file of the same name in a folder called
aivec-devenv-configs
which exists in your home folder, or the name of one of the properties specified in a file calledftp.json
which exists in theaivec-devenv-configs
folder. If you do not have anaivec-devenv-configs
folder, create one in your home directory. For more information about FTP config files, refer to the FTP Config Files section. - * Required if
confpath
is not set
-- ftp[{}.confpath]
- Optional*
- Type:
String
- Description: Can be either a relative or absolute path pointing to a
.json
file containing login information. If bothconfname
andconfpath
are set,confpath
will take precedence. For information about FTP config files, refer to the FTP Config Files section. - * Required if
confname
is not set
-- ftp[{}.plugins]
- Optional
- Type:
String[]
- Description: Can be any number of relative paths to plugin
.zip
files that exist on the FTP server. Paths are resolved relative to the directory that is opened upon access via FTP. Do not include the extension.zip
as part of the file name.
-- ftp[{}.themes]
- Optional
- Type:
String[]
- Description: Can be any number of relative paths to theme
.zip
files that exist on the FTP server. Paths are resolved relative to the directory that is opened upon access via FTP. Do not include the extension.zip
as part of the file name.
Example
{
"ftp": [
{
"confname": "my-ftp-config",
"plugins": ["relative/path/to/plugin/zipfile/noextension"],
"themes": ["relative/path/to/theme/zipfile/noextension"]
},
{
"confpath": "path/to/ftp-config.json",
"plugins": ["relative/path/to/plugin/zipfile/noextension"],
"themes": ["relative/path/to/theme/zipfile/noextension"]
}
]
}
-- ssh
- Optional
- Type:
Object[]
- Description:
ssh
may contain an array of any number ofssh
config objects
-- ssh[{}.confname]
- Optional*
- Type:
String
- Description: Can refer to either a file of the same name in a folder called
aivec-devenv-configs
which exists in your home folder, or the name of one of the properties specified in a file calledssh.json
which exists in theaivec-devenv-configs
folder. If you do not have anaivec-devenv-configs
folder, create one in your home directory. For more information about SSH config files, refer to the SSH Config Files section. - * Required if
confpath
is not set
-- ssh[{}.confpath]
- Optional*
- Type:
String
- Description: Can be either a relative or absolute path pointing to a
.json
file containing login information. If bothconfname
andconfpath
are set,confpath
will take precedence. For information about SSH config files, refer to the SSH Config Files section. - * Required if
confname
is not set
-- ssh[{}.plugins]
- Optional
- Type:
String[]
- Description: Can be any number of relative paths to plugin
.zip
files that exist on the SSH server. Paths are resolved relative to the directory that is opened upon access via SSH. Do not include the extension.zip
as part of the file name.
-- ssh[{}.themes]
- Optional
- Type:
String[]
- Description: Can be any number of relative paths to theme
.zip
files that exist on the SSH server. Paths are resolved relative to the directory that is opened upon access via SSH. Do not include the extension.zip
as part of the file name.
Example
{
"ssh": [
{
"confname": "my-ssh-config",
"plugins": ["relative/path/to/plugin/zipfile/noextension"],
"themes": ["relative/path/to/theme/zipfile/noextension"]
},
{
"confpath": "path/to/ssh-config.json",
"plugins": ["relative/path/to/plugin/zipfile/noextension"],
"themes": ["relative/path/to/theme/zipfile/noextension"]
}
]
}
FTP/SSH Config Files
For security reasons, FTP and SSH login information is not part of the wp-instances.json
schema. Instead, the SSH and FTP properties in wp-instances.json
contain a reference to their respective config files that exist elsewhere. This makes it easier and safer to share wp-instances.json
config files between colleagues and teammates.
FTP
If confname
is used for your FTP config and a file named <confname>.json
exists in the aivec-devenv-configs
folder in your home directory, the file must have the following JSON
structure:
{
"host": "some-host.com",
"user": "ftpuser",
"password": "somepassword"
}
If confpath
is used for your FTP config then the JSON
structure of the file must be the same as above.
Alternatively, if confname
is used for your FTP config and a file named <confname>.json
does not exist in the aivec-devenv-configs
folder in your home directory, the CLI will look for a key in aivec-devenv-configs/ftp.json
with the same name as confname
. For example, given that confname
is my-ftp-conf
, the ftp.json
would look like this:
{
"my-ftp-conf": {
"host": "some-host.com",
"user": "ftpuser",
"password": "somepassword"
}
}
This method allows you to have any number of unique configurations all in the same file.
SSH
The same rules used for FTP config resolution also apply to SSH configs. SSH configs must have the following JSON
structure:
{
"host": "my-server.com",
"user": "ssh-user",
"privateKeyPath": "relativepath/sshkeys/my_key"
}
privateKeyPath
can be either a relative or absolute path. Relative paths are resolved starting from the same folder as the ssh config file, while absolute paths are resolved starting from the users home directory.
Additionally, the private key must not be password protected. There are tools out there like sshpass
for automating password input for password protected SSH keys, but in our experience this is not very reliable and not worth the effort. Our recommendation is to generate a passwordless SSH key pair where only non-interactive downloads are allowed.