An introduction to deploying wordpress with mina

GFX9.COM share An introduction to deploying wordpress with mina, you can download now.

As a PHP application, WordPress is usually deployed by a very old method: uploading files via FTP.

We have some deployment tools, but they often requires some type of Ruby skill. For example, one popular, powerful tool is Capistrano, but it's also very heavy with many Ruby/Rails related features. I also think that it's little bit tricky to install Capistrano for a PHP developer without any Ruby knowledge.

So what options do we have as WordPress developers?

In this tutorial, I will introduce you Mina: A small, light tool aims to fast deployment and server automation.

Why Do We Need Automated Deployment?

Automated deployment saves us the time of doing repeat tasks every time we start to deploy our WordPress project. It also help to minimize down time during deployment and eliminate human mistakes such as missing files, uploading wrong files, and so on.

This automation process can be shared between multiple developers in the team thus creating an unique method for deployment in whole team. One company almost went bankrupt because of their lack of a good deployment process. An automated deployment method is usually tied with a source code control system: Git, SVN, Mercurial, and so on.

In the scope of this tutorial we will be taking a look at Git. If you had a Git repository, feel free to use it; otherwise, grab a free one at BitBucket or GitLab. These services allow you to create private Git repositories.

Before we go further, let make sure we meet the requirements:

  • an SSH connection
  • a Git repository
  • oermission to edit and change web server configuration.

[note] I assume that you are running WordPress on Apache with PHP installing as Apache PHP module. If you are using PHP with FPM or CGI, please be flexible when we're talking about our web server or our PHP process. [/note]

Deployment With Git Hooks

The general idea is that when we push to the server, Git will invoke a URL which could reference a PHP script to perform deploying by pulling or merging the newest code from repository.

While this works perfectly, and is very useful, it exposes a hole: We opened a secret door on the server. If someone knows that URL, they can trigger a deployment manually. Another danger is doing this is that we must build features for clean up, for rollback, for lock (to make sure only a single deployment process is running), and so on.

If we start to code these features, we are reinventing the wheel, so why not use an existing tool?

What is Mina?

Mina is a deployment tool aim to be very fast; You will be surprised at how fast it is when try it. According to Mina website:

Really fast deployer and server automation tool. Really bloody fast.

Simply put, here' how to think of Mina: Instead of logging into server and type a sequence of commands to deploy. Mina generate a shell script that is a set of these commands.

  1. Create a new directory,
  2. Pull latest code into this directory,
  3. Make symbolic point to common resources,
  4. Point the public directory to this new directory,
  5. Clean up the old data.

Mina uploads this shell script to server and executes it. This generation process is run on your local machine.

All Mina tasks, are just a sequence of shell commands. Because it's just shell commands, you won't have access to fantastic Ruby helper as in Capistrano or Vlad.

Another way to think of Mina is this: Mina organizes your shell commands that you need to run on remote machine into code blocks (which are called Mina tasks).

Step 1. Prepare Server Layout for Mina

Mina requires a directory layout for your website. You will need to change the public directory of your web server. To make it easier, let assume that your current server directory structure as following.

/var/www/     # The public directory where your WordPress sits
|-  index.php
|-  wp-admin
|-  wp-content
|-  wp-includes
|-  wp-login.php
|-  wp-activate.php
|-  …. points to /var/www/, and index.php file, which sits inside this directory, is executed and responds to your request. With Mina, you have to change directory layout a little bit.

Mina expects this structure:

/var/www/     # The deploy_to path
|-  releases/              # Holds releases, one subdir per release
|   |- 1/                       # Each of WordPress deployment sit here
|   |- 2/                       # Each of WordPress deployment sit here
|   |- 3/                       # Each of WordPress deployment sit here
|   |- ...
|-  shared/                # Holds files shared between releases: such as log file, config,…
|   |- logs/                 # Log files are usually stored here
|   - ...
|-  current/               # A symlink to the current release in releases, this will be new public folder

Mina adds three directories:

  1. releases: each deployment will be stored in separate directories inside this folder which allows us to maintain version 1, version 2, version 3 and so on.
  2. shared: contains common file/folders which is shared between multiple deployment. An example is wp-content/uploads. At each of deployment, we will have a new directory wp-content/uploads that is different from previous wp-content/uploads directory. And content inside is gone. Therefore, we will use a common directory: shared/wp-content/uploads. We will have: /var/www/ is a symlink points to /var/www/
  3. current: points to current release. Example: /var/www/ points to /var/www/

According to Wikipedia:

A symbolic link (also symlink or soft link) is a special type of file that contains a reference to another file or directory in the form of an absolute or relative path and that affects pathname resolution. Symbolic links were already present by 1978 in mini-computer operating systems from DEC and Data General's RDOS. Today they are supported by the POSIX operating-system standard, most Unix-like operating systems such as FreeBSD, GNU/Linux, and Mac OS X, and also Windows operating systems such as Windows Vista, Windows 7 and to some degree in Windows 2000 and Windows XP in the form of Shortcut files. now must points to /var/www/ instead of /var/www/ Web server will run the file /var/www/ when you visit We have to re-config web server (Apache, Nginx) to point docroot(or public directory) to this current directory.

Change DocumentRoot for Apache

Open your /etc/httpd/conf/httpd.conf, find the DocumentRoot line and change it to.

DocumentRoot /var/www/

Change Document Root for Nginx

Edit your /etc/nginx/nginx.conf and update your root definition.

server {
root /srv/http/domain/;
# …

Step 2. Installing Mina

Mina is a Ruby gem so you need to install Ruby. Installing is quite esy. If you are running OS X or Linux, chances is you already have Ruby installed; otherwise, you can follow these tutorials to install Ruby:


Once, you have Ruby, proceed to install gem. Just run:

$ gem install mina

Confirm that Mina is running properly.

$ mina -V

You should see something similar.

Mina, version v0.3.0

WordPress Deployment with Mina

One of the things that make PHP applications less secure is setting the incorrect permissions.

Take WordPress for an example: I may want to upload plugin, or upload theme to try it. To do this, I end up uploading them in WordPress dashboard. If I or one of the site administrator loses the admin password, someone get in and they can upload a PHP file and run it, as a plug in, and not only hack my site, but my entire server, as well.

Case in point: they can read a file in /etc, learn the server configuration, and then begin running shell commands via PHP.

I think we should treat a WordPress instance as a read-only installation. For theme and plugin installation, we can install via adding files locally, then re-deploy via Mina. Deploying with Mina is cheap and very fast, on my 512MB RAM DigitalOcean server, it takes under 30 seconds to deploy.

For user uploaded content (such as pictures and audio files), we will symlink them to outside of the source code directory, and it will be best if we can configure the server to prevent executing of any PHP file inside that user uploaded content folder, but that's out of scope of this tutorial.

For now, we will try to use Mina for fast deployment, and adding theme or a plugin and we'll do so locally so we can avoid giving web server the permission to write into these directories.

Step 1. Setup Mina with WordPress

As stated in beginning of tutorial, you must have a Git repository for your WordPress site. Let recap what we need here:

Let's assume that...

  1. Your WordPress project will be in ~/Site/wordpress.
  2. Your WordPress git repository is
  3. You can access your server via ssh with an account call yourname at

If you are not familiar with Git, please read the following tutorials:


If your WordPress source code isn't a Git repository, let's do it now; otherwise, jump to next step.

$ cd ~/Site/wordpress
$ git init
$ git remote add origin
$ git add .
$ git push origin master

Run below command to start setting up Mina for your project.

$ mina init

This will create you a folder config with a single file deploy.rb inside it.

$  ls
config               wp-blog-header.php   wp-load.php
index.php            wp-comments-post.php wp-login.php
latest.tar.gz        wp-config-sample.php wp-mail.php
license.txt          wp-content           wp-settings.php
readme.html          wp-cron.php          wp-signup.php
wp-activate.php      wp-includes          wp-trackback.php
wp-admin             wp-links-opml.php    xmlrpc.php

$  ls config

Step 2. Configure config/deploy.rb

Now, open config/deploy.rb in previous step and let's define some configuration.

This deploy.rb file contains deployment configuration, and a set of Mina task. Each task is wrapped inside task :taskname block. Inside each task, we can invoke another task with invoke To run a comand on server, you specify it with queue command.

For example, a task may look like this:

task :down do
  invoke :maintenance_on
  invoke :restart
  queue 'rm -rf /tmp/cache'

With that in mind, let's begin editting this deploy.rb file.

Remove Unused Lines

Comment out these line since we won't use them.

#require 'mina/bundler'
#require 'mina/rails'

Configure Server Definition

The default code look like this

set :user, 'username'
set :domain, ''
set :deploy_to, '/var/www/'
set :repository, 'git://...'
set :branch, 'master'
  1. domain is domain to the domain name of your WordPress site.
  2. deploy_to is where you want your WordPress project locate.
  3. repository is your Git repository address.
  4. branch your deployment branch. Git support many branches, and some pepple want to have a deploy branch for deployment purpose.

Let's update it with our server configuration:

set :domain, ''
set :deploy_to, '/var/www/'
set :repository, ''
set :branch, 'master'
set :user, 'your_username'    # Username in the server to SSH to.

Make sure that you can reach the repository on your remote machine.

Next, we will handle the wp-content/uploads folder. This folder contains user uploaded content. Every time we deploy, it will be a different folder from the one that we're currently using because the recently deployed code sits in different folder inside releases.

If we keep this new folder, we will lose all of the old content. As such, we have to use a symlink to point it to somewhere else. Once deployed, we will update it to point to correct one.

Find this line:

set :shared_paths, ['config/database.yml', 'log']

And change it to:

set :shared_paths, [ 'wp-content/uploads']

shared_paths is a collection of common resources(file/folder) between the various releases and can be different between release. Like log files, user uploaded content should be put in shared_paths. If we don't do that, then we will lose the data with each new release.

For example: When an user upload a file, WordPress store it in /var/www/ But /var/www/ is a symlink point to /var/www/ which is our current release. Therefore, the actual file is located at: /var/www/

Now, if you made a new release, current will point to /var/www/ The file /var/www/ is no longer there, because /var/www/ is a new folder, with no content.

As such, we must put it into shared_paths, and Mina will create a symlink to point /www/ to /www/

Configure Our Setup Task

This is task we run on first time to prepare our server environment. The default one looks like this:

task :setup => :environment do
  queue! %[mkdir -p "#{deploy_to}/shared/log"]
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/log"]

  queue! %[mkdir -p "#{deploy_to}/shared/config"]
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/config"]

  queue! %[touch "#{deploy_to}/shared/config/database.yml"]
  queue  %[echo "-----> Be sure to edit 'shared/config/database.yml'."]

Let's change it to:

task :setup => :environment do
  queue! %[mkdir -p "#{deploy_to}/shared/wp-content/uploads"]

Our setup task simply creates a wp-content/uploads directory.

Configure Our Deploy task

The default task looks like this.

task :deploy => :environment do
  deploy do
    # Put things that will set up an empty directory into a fully set-up
    # instance of your project.
    invoke :'git:clone'
    invoke :'deploy:link_shared_paths'
    invoke :'bundle:install'
    invoke :'rails:db_migrate'
    invoke :'rails:assets_precompile'

    to :launch do
      queue "touch #{deploy_to}/tmp/restart.txt"

We don't need anything that has to do with Rails. Also, since the deployment of WordPress didn't require a web server or PHP process restart so we only need to invoke git:clone and deploy:link_shared_paths task.

Let change them to:

task :deploy => :environment do
  deploy do
    # Put things that will set up an empty directory into a fully set-up
    # instance of your project.
    invoke :'git:clone'
    invoke :'deploy:link_shared_paths'

Configure Our Rollback Task

Sadly Mina doesn't have an official method for rollback, so I created a task for our own rollback process. What the rollback task will do do is that it will remove current release, and re point current symlink to previous release. Append this task to end of your deploy.rb

desc "Rollback to previous verison."
task :rollback => :environment do
  queue %[echo "----> Start to rollback"]
  queue %[if [ $(ls #{deploy_to}/releases | wc -l) -gt 1 ]; then echo "---->Relink to previos release" && unlink #{deploy_to}/current && ln -s #{deploy_to}/releases/"$(ls #{deploy_to}/releases | tail -2 | head -1)" #{deploy_to}/current && echo "Remove old releases" && rm -rf #{deploy_to}/releases/"$(ls #{deploy_to}/releases | tail -1)" && echo "$(ls #{deploy_to}/releases | tail -1)" > #{deploy_to}/last_version && echo "Done. Rollback to v$(cat #{deploy_to}/last_version)" ; else echo "No more release to rollback" ; fi]

The above code looks complex but it actually is a single line version of following code. I will break it down and put an annotation on top of each commands.

# Check whether we have than 1 releases
if [ $(ls /var/www/ | wc -l) -gt 1 ]
  echo "---->Relink to previos release"
  # Remove current symlink
  unlink /var/www/
  # Point it to the previous release
  ln -s /var/www/"$(ls /var/www/ | tail -2 | head -1)" /var/www/
  echo "Remove old releases"
  # Remove latest release which we already rollback
  rm -rf /var/www/"$(ls /var/www/ | tail -1)"
  # Log current version to last_version file
  echo "$(ls /var/www/ | tail -1)" > /var/www/
  # Out put some info to user
  echo "Done. Rollback to v$(cat /var/www/"
  # If we don't have more than 1 releases, then we cannot rollback.
  echo "No more release to rollback"

At this point, we config the server, edit setup task, deploy task and rollback task. Let start to actually using Mina now.

Deployment With Mina

In this part, I will show you how to run Mina to setup server, deploy to server and perform a rollback. Mina is a command line utility. You need to have access to a terminal. Every Mina task will be invoked from terminal.

Step 1. Prepare

We only run this the first time we prepare to deploy to a server. SSH to server and create the deploy_to directory. It's /var/www/ in our case.

$ ssh
# on your remote machine (after you connected via SSH), run this
$ sudo mkdir -p /var/www/
$ sudo chown -R your_name /var/www/

Once we run the chown command, we will change the owner of /var/www/ to user your_name. Therefore, web server cannot write anything to this directory. Our WordPress site will be more secured by this way. Next, on your local, run mina setup. It output something like this:

$ mina setup
-----> Setting up /var/www/

       total 16
       drwxr-xr-x 4 kurei root  4096 Jan 27 22:51 .
       drwxr-xr-x 3 root  root  4096 Jan 27 00:16 ..
       drwxr-xr-x 2 kurei users 4096 Jan 27 22:51 releases
       drwxr-xr-x 2 kurei users 4096 Jan 27 22:51 shared

-----> Done.
       Elapsed time: 1.00 seconds

If you want to confirm what Mina created for us on server, let's check it on remote machine:

$ cd /var/www/
$  ls
releases  shared
$ ls shared/wp-content

At this point, everything is almost ready. Remember, the upload directory stores user uploaded content. Therefore, the web server must be able to write to it. We have to determine which user your Apache/Nginx (or PHP FPM Process, depend on how you config your server) is running on, and change the owner of uploads folder to it, to allow web server write to this folder. Usually, if you open your Apache config file, or Nginx config file, you can find this user, something similar:

# For Apache, open /etc/httpd/conf/httpd.conf
User www Group www

# For Nginx, open /etc/nginx/nginx.conf

user http http; worker_processes 2;

Let assume that Apache is running on www user.

[sourecode] ssh sudo chown -R www /var/www/ [/sourecode]

Step 2. Deploy

Every time we want to deploy the code, we use this task.

The process is:

  • update the code
  • commit the change.
  • push to the Git server.

Once your code was up on the Git server.

Let's deploy

$ mina deploy

In several seconds, it should finish. Here is an example output of deploy result:

$ mina deploy
-----> Creating a temporary build path
-----> Fetching new git commits
-----> Using git branch 'master'
       Cloning into '.'...
-----> Using this git commit

       kureikain (347d9b3):
       > Update new config/deploy

-----> Symlinking shared paths
-----> Build finished
-----> Moving build to releases/2
-----> Updating the current symlink
-----> Launching
-----> Done. Deployed v2
       Elapsed time: 1.00 seconds

Step 3. Rollback

If a release has a critical bugs, or simply we want to rollback the code to previous release for one reason or another, we do this:

$ mina rollback

Its output something like

----> Start to rollback
----> Relink to previous release
Remove old releases
Done. Rollback to v2
Connection to closed.
       Elapsed time: 0.00 seconds


You learned how to deploy with Mina. The deployment process is quite fast now. Your website will experience zero downtime. But don't stop at that, go to the Mina website and lean more about it. I shared an example WordPress project that uses Mina on GitHub.

In next part, we will learn about WP-CLI. Specifically, we will learn how to utilize it and perform common admin tasks such as updating WordPress, installing themes, plugins, and so on all via WP-CLI on top of a Mina task. We will also look at how we use it to make our WordPress installation more secure.

Until then, leave a comment what you do to make your WordPress deployment a breeze.

Similar content