# Leveraging WP-CLI Aliases in Your WordPress Development Workflow

**URL:** https://discourse.roots.io/t/leveraging-wp-cli-aliases-in-your-wordpress-development-workflow/8414
**Category:** blog
**Tags:** guide
**Created:** 2016-12-29T16:13:51Z
**Posts:** 39

## Post 1 by @anon56740962 — 2016-12-29T16:13:51Z

Originally published at: [https://roots.io/leveraging-wp-cli-aliases-in-your-wordpress-development-workflow/](https://roots.io/leveraging-wp-cli-aliases-in-your-wordpress-development-workflow/)  
   
Not to be confused with the hit TV series starring Jennifer Garner, alias support was added to WP-CLI from an update in July, 2016. If you aren’t familiar, WP-CLI is a tool for interacting with WordPress via the command line. It allows you to perform many admin actions such as activating plugins or users and…

---

## Post 2 by @ben — 2016-12-29T18:45:52Z

If you’re looking for an example script that does the opposite of what’s in the post, going from development to production (useful during initial solo-dev), here’s `sync-dev-to-prod-FOR-REAL.sh` :slight_smile: :

```
read -r -p "Would you really like to reset THE PRODUCTION DATABASE and send up the latest from dev? [y/N] " response
if [[$response =~ ^([yY][eE][sS]|[yY])$ ]]
then
    wp @development db export - > sql-dump-development.sql
    scp sql-dump-development.sql web@example.com:/srv/www/example.com/current

    wp @production db export just-in-case.sql
    wp @production db reset --yes

    wp @production db import sql-dump-development.sql
    wp @production search-replace https://example.dev http://example.com
    scp -r web/app/uploads/ web@example.com:/srv/www/example.com/shared
else
    exit 0
fi
```

---

## Post 3 by @ericgauvin — 2016-12-30T15:30:45Z

This looks really cool, but I just started using wp-cli and I don’t quite get it. As I understand it, you’d normally need to ssh into a server and go into the web woot where WordPress resides and run “wp” commands there. I was able to ssh into vagrant and run “wp --info” and “which wp”. If I try to run “wp” commands from my command line I get “wp not found”.

So how is this supposed to work?

1. Normally “wp” needs to be run on the server from within the folder where WordPress lives? Requires ssh-ing into a server?
2. However, now with aliases you can run “wp” commands without ssh-ing into other servers?
3. Am I supposed to be able to run “wp” commands from my computer’s command line?

---

## Post 4 by @ericgauvin — 2016-12-30T16:56:34Z

**wp-cli tips for newbies (also some questions, someone knowledgeable please verify this is correct)**

1. You may need to install wp-cli on your system, it’s not listed as a requirement, so I didn’t have it installed. (follow the instructions at [http://wp-cli.org/](http://wp-cli.org/)).

2. Trellis does install wp-cli on vagrant and other servers.

3. Even if you haven’t installed wp-cli on your system, you can run “wp” commands when you ssh into vagrant or other environments.

4. The aliases look like this. Add them to an existing file (wp-cli.yml) in the “site” folder. (aliases were appended after “path: web/wp” which was alrealy in the file)

5. The whole point of aliases is so that you can run “wp” commpands on your system that will run on vagrant or remote servers without having to ssh in.

6. You need to run your aliases from the same folder that has the wp-cli.yml file (sites) or it won’t find your alias. (If you’re running other “wp” commands that don’t require an alias, you should be able to run “wp” commands globally if you installed it globally.)

7. The example in the tutorial (`wp @production plugin activate soil`) ([https://roots.io/leveraging-wp-cli-aliases-in-your-wordpress-development-workflow/](https://roots.io/leveraging-wp-cli-aliases-in-your-wordpress-development-workflow/)) may not work if you don’t have soil installed. You can try a different plugin. Here I’m installing wp-migrate-db with composer and then activating it with wp-cli. (very very cool!)

composer require wpackagist-plugin/wp-migrate-db  
wp @development plugin activate wp-migrate-db

---

## Post 5 by @ericgauvin — 2016-12-30T21:39:00Z

Ben Word’s database syncing shell script above works great! And it copies over the media also!! Awesome!

**My questions:**

1. I have a permissions problem. Any idea how to fix this?

`mysqldump: Can't create/write to file 'just-in-case.sql' (Errcode: 13 "Permission denied")`

1. what’s the best practice for where to store this script in “trellis/bin”?
2. it leaves a copy of sql-dump-development.sql (in “sites”). Guessing that would be easy to change in the script unless it’s good to keep.
3. you say this is good for “initial solo dev”. What do you see as the limitations of this as compared to the wp-migrate-db plugin?

---

## Post 6 by @anon56740962 — 2016-12-30T22:07:09Z

Thanks for trying out aliases!

This should probably be moved to a support thread rather than discussion of the post.

You can include your questions there, and additionally please include information about where you are running the script to provide more context.

Consider separating out #4 as another thread as it’s own can of worms.

---

## Post 8 by @aaemnnosttv — 2017-02-11T15:48:16Z

Nice post Raquelle! Aliases are awesome.

I found you can remove the need for the `sql-dump-development.sql` file and run it in one go like this from the environment you want to run the import on:

```
wp @remote db export - | wp db import -
```

This won’t be as practical for large databases where you might need to compress it to get a reasonable transfer time, but it makes for a nice one-liner :ok_hand:

---

## Post 9 by @Simeon — 2017-02-12T02:08:25Z

Hold on … you’re telling me we can duplicate WP Migrate DB Pro with just a shell script?

---

## Post 10 by @nathobson — 2017-02-12T10:07:51Z

Just getting round to trying this now and I can certainly see a lot of value in this.

On a Trellis + Bedrock set up, I can get staging and prod aliases working fine but I’m having trouble with development.

```
@development:
  ssh: vagrant@example.dev/srv/www/example.com/current
```

Fairly new to Trellis but this gives me:

`Error: Cannot connect over SSH using provided configuration.`

And if I manually try:

`vagrant@example.dev`

I get:

`Permission denied (publickey).`

Maybe I’m totally missing something or maybe the Trellis setup has changed since this post?

**Edit**

OK, got it figured out. I wasn’t sure as the post referenced Trellis and Vagrant separately, so I tried the Vagrant part of the post, running the following in the Trellis directory:

`$ vagrant ssh-config --host example.dev >> ~/.ssh/config`

This then let the SSH connection work. Probably my bad for not realising this needed to be done first.

---

## Post 11 by @ben — 2017-02-24T21:14:01Z

I made a script to auto-update my local SSH config on `vagrant up` to make sure that the port always works. I noticed that after rebooting or having different Vagrant boxes running that my SSH attempts would fail.

Add the following to your `Vagrantfile` within the VM provisioner (`config.vm.provision provisioner do |ansible|`)

```
# Run script to update local SSH config for .dev hostname after `vagrant up`
    if Vagrant.has_plugin?('vagrant-triggers')
      config.trigger.after :up do
        run "./bin/ssh-config-development.sh"
      end
    else
      fail_with_message "vagrant-triggers missing, please install the plugin with this command:\nvagrant plugin install vagrant-triggers"
    end
```

Then create `bin/ssh-config-development.sh` with the following:

```
host="example.dev"
sed "/^$/d;s/Host /$NL&/" ~/.ssh/config | sed '/^Host '"$host"'$/,/^$/d;' > config &&
cat config > ~/.ssh/config &&
rm config &&
vagrant ssh-config --host example.dev >> ~/.ssh/config
```

---

## Post 12 by @ben — 2017-02-24T21:24:20Z

Also, here’s the latest version of the sync script I’ve been using that lives at `site/scripts/sync.sh`

```
#!/bin/sh

DEVDIR="web/app/uploads/"
DEVSITE="https://example.dev"

PRODDIR="web@example.com:/srv/www/example.com/shared/uploads/"
PRODSITE="https://example.com"

STAGDIR="web@staging.example.com:/srv/www/example.com/shared/uploads/"
STAGSITE="https://staging.example.com"

FROM=$1
TO=$2

case "$1-$2" in
  development-production) DIR="up"; FROMSITE=$DEVSITE; FROMDIR=$DEVDIR; TOSITE=$PRODSITE; TODIR=$PRODDIR; ;;
  development-staging) DIR="up" FROMSITE=$DEVSITE; FROMDIR=$DEVDIR; TOSITE=$STAGSITE; TODIR=$STAGDIR; ;;
  production-development) DIR="down" FROMSITE=$PRODSITE; FROMDIR=$PRODDIR; TOSITE=$DEVSITE; TODIR=$DEVDIR; ;;
  staging-development) DIR="down" FROMSITE=$STAGSITE; FROMDIR=$STAGDIR; TOSITE=$DEVSITE; TODIR=$DEVDIR; ;;
  *) echo "usage: $0 development production | development staging | production development | production staging" && exit 1 ;;
esac

read -r -p "Would you really like to reset the $TO database and sync $DIR from $FROM? [y/N] " response

if [["$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
  cd ../ &&
  wp "@$TO" db export &&
  wp "@$FROM" db export - | wp "@$TO" db import - &&
  wp "@$TO" search-replace "$FROMSITE" "$TOSITE" &&
  rsync -az --progress "$FROMDIR" "$TODIR"
fi
```

Usage:

```
$ ./sync.sh
usage: ./sync.sh development production | development staging | production development | production staging
```

eg.

```
$ ./sync.sh development production
$ ./sync.sh production development
```

---

## Post 13 by @nathobson — 2017-02-25T13:05:01Z

@ben that looks great. I had already tweaked around with your last script but that’s a whole let better than what I had put together. Just gave this a test run and worked perfectly.

Question: I had added to mine the removal of the resulting .sql files. It looks like in this script we get left with a single .sql file on the remote server. Should this be removed after import?

---

## Post 14 by @ben — 2017-02-25T16:06:04Z

I left it as a way of leaving a backup incase you didn’t mean to push up a new DB, but have considered moving that to a more temporary area. Feel free to remove/tweak :slight_smile:

---

## Post 15 by @nathobson — 2017-02-25T16:17:43Z

Makes sense, appreciate the reply :slight_smile:

---

## Post 16 by @runofthemill — 2017-03-01T22:10:25Z

> [@ericgauvin](#):
>
> 1. The aliases look like this. Add them to an existing file (wp-cli.yml) in the “site” folder. (aliases were appended after “path: web/wp” which was alrealy in the file)
> 
> path: web/wp  
> @development:  
> ssh: vagrant@example.dev/srv/www/example.com/current  
> @staging:  
> ssh: [admin@staging.example.com](mailto:admin@staging.example.com)/srv/www/example.com/current  
> @production:  
> ssh: [admin@example.com](mailto:admin@example.com)/srv/www/example.com/current
> 
> 1. The whole point of aliases is so that you can run “wp” commpands on your system that will run on vagrant or remote servers without having to ssh in.
> 
> 2. You need to run your aliases from the same folder that has the wp-cli.yml file (sites) or it won’t find your alias. (If you’re running other “wp” commands that don’t require an alias, you should be able to run “wp” commands globally if you installed it globally.)

WP-CLI supports a global config file! Per the [docs](https://make.wordpress.org/cli/handbook/config/) you can have a global config in `~/.wp-cli/config.yml` - I just set it up with aliases, and now I can use my aliases from any folder :grin:

---

## Post 17 by @runofthemill — 2017-03-01T22:58:43Z

I just noticed the last case doesn’t match up with the last usage example - I added in `production <> staging` and updated the usage text. I also made the site URLs root relative, just for compatibility reasons.

The first case also has `;` after `DIR="up"` whereas the others don’t; I added to make consistent though I’m not sure it actually matters since each statement is just a variable declaration.

```
#!/bin/sh

DEVDIR="web/app/uploads/"
DEVSITE="//example.dev"

PRODDIR="web@example.com:/srv/www/example.com/shared/uploads/"
PRODSITE="//example.com"

STAGDIR="web@staging.example.com:/srv/www/example.com/shared/uploads/"
STAGSITE="//staging.example.com"

FROM=$1
TO=$2

case "$1-$2" in
  development-production) DIR="up"; FROMSITE=$DEVSITE; FROMDIR=$DEVDIR; TOSITE=$PRODSITE; TODIR=$PRODDIR; ;;
  development-staging) DIR="up"; FROMSITE=$DEVSITE; FROMDIR=$DEVDIR; TOSITE=$STAGSITE; TODIR=$STAGDIR; ;;
  production-development) DIR="down"; FROMSITE=$PRODSITE; FROMDIR=$PRODDIR; TOSITE=$DEVSITE; TODIR=$DEVDIR; ;;
  production-staging) DIR="horizontally"; FROMSITE=$PRODSITE; FROMDIR=$PRODDIR; TOSITE=$STAGSITE; TODIR=$STAGDIR; ;;
  staging-development) DIR="down"; FROMSITE=$STAGSITE; FROMDIR=$STAGDIR; TOSITE=$DEVSITE; TODIR=$DEVDIR; ;;
  *) echo "usage: $0 development production | development staging | production development | production staging | staging development" && exit 1 ;;
esac

read -r -p "Would you really like to reset the $TO database and sync $DIR from $FROM? [y/N] " response

if [["$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
  cd ../ &&
  wp "@$TO" db export &&
  wp "@$FROM" db export - | wp "@$TO" db import - &&
  wp "@$TO" search-replace "$FROMSITE" "$TOSITE" &&
  rsync -az --progress "$FROMDIR" "$TODIR"
fi
```

Edit: though I’m still using WP Migrate DB, I figured I’d add in the option to skip syncing uploads, so after the case block I changed to the following:

```
read -r -p "Would you like to sync the uploads folder as well? [y/N] " uploads
read -r -p "Would you really like to reset the $TO database and sync $DIR from $FROM? [y/N] " response

if [["$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
  cd ../ &&
  wp "@$TO" db export &&
  wp "@$FROM" db export - | wp "@$TO" db import - &&
  if [["$uploads" =~ ^([yY][eE][sS]|[yY])$ ]]; then
    wp "@$TO" search-replace "$FROMSITE" "$TOSITE" &&
    rsync -az --progress "$FROMDIR" "$TODIR"
  else
    wp "@$TO" search-replace "$FROMSITE" "$TOSITE"
  fi
fi
```

---

## Post 18 by @strarsis — 2017-03-07T22:49:57Z

Wouldn’t this be a great candidate for a GitHub repository?

---

## Post 19 by @strarsis — 2017-03-07T23:02:50Z

[–skip-columns=guid](http://wp-cli.org/commands/search-replace/#examples) should be used when replacing, see  
[https://codex.wordpress.org/Changing\_The\_Site\_URL#Important\_GUID\_Note](https://codex.wordpress.org/Changing_The_Site_URL#Important_GUID_Note)

---

## Post 20 by @jasperfrumau — 2017-03-09T05:38:51Z

I am working on a repo [here](https://github.com/jasperf/trellis-sync). Raquelle as author (@cedarstay ) as well as @ben have been mentioned for the script they made and I added there. Thanks so much for sharing this and teaching me more about aliases and shell scripts! README is not quite done yet and tweaks me be needed. Feel free to submit pull requests or clone it.

---

## Post 21 by @strarsis — 2017-03-09T21:27:25Z

Also a plain backup option would be nice that dumps the database and uploads/ folder to a specific local path (for remote regular backups).

---

## Post 22 by @jasperfrumau — 2017-03-10T05:01:33Z

Actually another pull request by @guilro is in line for automating database and file backups using [Stouts.backup](https://github.com/Stouts/Stouts.backup) Ansible role with [Duplicity](https://help.ubuntu.com/community/DuplicityBackupHowto) and is mentioned here: [https://github.com/roots/trellis/pull/650](https://github.com/roots/trellis/pull/650) . I think that would be really good to have and can be configured in many different ways.

---

## Post 23 by @mockey — 2017-03-10T18:11:29Z

I use the `--export` option of `wp search-replace` for this and also add `gzip`. No wp-aliases though, but not much different. It’s basically like:

```
ssh $srv1 "cd $dir1; wp search-replace 'http://$srv1' 'http://$srv2' --export | gzip" \
| ssh $srv2 "cd $dir2; gunzip | wp db import -"
```

---

## Post 24 by @jasperfrumau — 2017-03-13T15:20:35Z

Just did a run to move stuff from a dev site to a production site. Latter is a demo so worries there. But I ran into an error

```
./sync-all.sh development production
Would you really like to reset the production database and sync up from development? [y/N] y
mysqldump: Can't create/write to file 'filathlos_imagewize_com_production-a822007.sql' (Errcode: 13 "Permission denied")
```

Must be a permissions issue. But should did not work using user `web`?

---

## Post 25 by @jasperfrumau — 2017-03-14T05:16:17Z

Well when I used the user web in the wp-cli.yml file it all worked

```
./sync-all.sh development production
Would you really like to reset the production database and sync up from development? [y/N] y
Success: Exported to 'filathlos_imagewize_com_production-a9ab231.sql'.
Success: Imported from '-'.
```

I only had a Sage error:

`Error: Sage › ErrorAutoloader not found.. You must run composer install from the Sage directory.`

, but when I commented out build-before.yml and ran deployment again I solved that too

---

## Post 26 by @seraphim — 2017-04-30T10:44:29Z

Thank you for this very helpful script!

In my case, I ran [sync.sh](http://sync.sh) from within my Trellis-provisioned development vagrant vm and it only worked when I changed the first line from #!/bin/sh to #!/bin/bash. Otherwise it will throw the following error on line 25 of the script:

`./sync.sh: 25: ./sync.sh: Syntax error: "(" unexpected (expecting "then")`

I’m far from being a Unix shell expert though, so I’m wondering if anyone has encountered this?

---

## Post 28 by @jasperfrumau — 2017-05-04T02:51:13Z

Yeah, others encountered this common Bash issue too. See [https://github.com/jasperf/trellis-sync/issues/3#issuecomment-291927603](https://github.com/jasperf/trellis-sync/issues/3#issuecomment-291927603) where I use @ben and Raquelle’s scripts and another person @redstartinternet encountered the same POSIX issue.

---

## Post 29 by @allurewebsolutions — 2017-08-24T22:36:44Z

I opened up a pull request to fix the upload and db syncing scripts.

---

## Post 30 by @s3w47m88 — 2017-09-04T21:30:45Z

@seraphim I’m also getting that error. Did anyone experience this and resolve the issue?

> <https://gist.github.com/s3w47m88/9ae663f26ee4c5b2aff8f64a501ecd6c>

---

## Post 31 by @patram1121 — 2017-09-05T13:58:21Z

> [@seraphim](#):
>
> #!/bin/sh to #!/bin/bash

Did you try changing #!/bin/sh to #!/bin/bash at the top of the script, as he suggested?

---

## Post 32 by @s3w47m88 — 2017-09-13T22:34:07Z

Yes. But that didn’t change my result.

---

## Post 33 by @jasperfrumau — 2017-10-11T06:51:38Z

Just reading this here now, but did deal with the pull request at Github earlier on :wink:

---

## Post 34 by @allurewebsolutions — 2018-01-11T08:53:39Z

If your `wp @prod` alias is not working, you need to do the same thing for your prod server. Something like adding the below to your ~/.ssh/config file:

Host **[example.com](http://example.com)**  
HostName **[XXX.XXX.XXX.XXX](http://XXX.XXX.XXX.XXX) (IP ADDRESS)**  
User admin  
UserKnownHostsFile /dev/null  
StrictHostKeyChecking no  
PasswordAuthentication no  
ForwardAgent yes

---

## Post 36 by @nickkeenan — 2018-05-08T20:39:15Z

It looks like the vagrant-triggers plugin has been merged into Vagrant in recent versions, and this vagrant-triggers script (which I :heart:) is not working as is (at least with Vagrant 2.1.0). Anyone else experienced this?

---

## Post 37 by @nickkeenan — 2018-05-24T04:34:48Z

If you want to use this automatic SSH configuration after updating Vagrant \> 2.1.0, you can uninstall vagrant-triggers and replace this code in your Vagrantfile:

```
# Run script to update local SSH config for .dev hostname after `vagrant up`
if Vagrant.has_plugin?('vagrant-triggers')
  config.trigger.after :up do
    run "./bin/ssh-config-development.sh"
  end
else
  fail_with_message "vagrant-triggers missing, please install the plugin with this command:\nvagrant plugin install vagrant-triggers"
end
```

with

```
config.trigger.after :up do |trigger|
  trigger.run = { path: "./bin/ssh-config-development.sh"}
end
```

---

## Post 38 by @runofthemill — 2018-08-03T19:13:16Z

I made a few modifications to this, including the change noted by @nickkeenan to automate both the script itself, while removing the hardcoded host name in the script, and creating a `wp-cli.local.yml` file in the project root so that you can use the `@dev` alias with wp-cli from anywhere in the project :slight_smile:

Put this block in `Vagrantfile` after `config.vm.provision provisioner do |ansible|`:

```
# Run script to update local SSH config for .dev hostname after `vagrant up`
    config.trigger.after :up do |trigger|
      trigger.run = {
        path: "./bin/ssh-config-development.sh",
        args: [main_hostname, "#{ANSIBLE_PATH}", trellis_config.wordpress_sites.keys.first]
      }
    end
```

And the updated script:

```
# bin/ssh-config-development.sh
#!/bin/bash
host=$1
path=$2
site=$3

# add ssh-config for vagrant to ~/.ssh/config
sed "/^$/d;s/Host /$NL&/" ~/.ssh/config | sed "/^Host $host$/,/^$/d;"> config &&
cat config > ~/.ssh/config &&
rm config &&
vagrant ssh-config --host $host >> ~/.ssh/config

# add wp-cli.local.yml to project root if it doesn't exist with alias for @dev
cd $path/.. &&
if [! -f wp-cli.local.yml]
then
  touch wp-cli.local.yml
fi

if ! grep -Fxq "@dev" wp-cli.local.yml
then

cat << EOF > wp-cli.local.yml
@dev:
  ssh: vagrant@$host/srv/www/$site/current
  path: web/wp
EOF

fi
```

A few caveats:

- if you have more than one site in your `wordpress_sites.yml` it only creates an alias for the first one
- the script assumes your ssh config is located at `~/.ssh/config`
- it only checks for the existence of a `@dev` alias in `wp-cli.local.yml` so if one already exists, it won’t do anything. this might mean the alias won’t work if you’ve run `vagrant up` then changed your site name or URL and run `vagrant up` again
- by default, the alias will not work in the `site/` directory  
– to fix, either add the alias to `site/wp-cli.yml`, delete `site/wp-cli.yml`, or add this to the [configuration](https://make.wordpress.org/cli/handbook/config/#config-files) in `site/wp-cli.yml`:

```
_:
  inherit: ../wp-cli.local.yml
```

---

## Post 39 by @ericgauvin — 2018-11-22T17:28:39Z

I have this working but some problems.

- This is a fresh install of trellis/bedrock. (as of 11/22/2018, Happy Thanksgiving!)
- I have my wp-cli aliases in wp-cli.yml in “site”  
Example:  
@staging:  
ssh: [web@staging.example.com](mailto:web@staging.example.com)/srv/www/example.com/current
- The script is in “site/scripts/sync-db.sh”
- I have this at the top of my script “#!/bin/bash”
- I have sshd\_permit\_root\_login: false in my security.yml
- I’m using the new and improved script by benword (above) with the conditional uploads added by runofthemill.

My problems/lack of understanding:

1. When I run the script I have to be inside “scripts” “./sync-db.sh development staging” Previously, I was able to the script in “site” “scripts/sync-db.sh”  
(Update: I understand this now. In the script cds into the folder up `cd ../ &&` so that’s why it couldn’t find my aliases.)

2. The script runs fine, but then when I go to staging to see the result I have a white screen. I can access the admin, after I login and logout I can see the page.  
(Update: this is the behavior on other devices. For example, first I see a white screen on my iPhone, then after log in/out on laptop, site works fine on iPhone. Is this normal? why is it doing this?)
