in Developer Tools, WordPress

Update: Gitlab has just announced paid plans, with a monthly limit on CI runner minutes (2,000). For personal projects this likely won’t have a major impact, but something to keep in mind if you frequently deploy or have long builds.

I have been really impressed with Gitlab.

They are constantly cranking out new features and fixes as they release every month like clockwork. What has really attracted me to Gitlab is their integrated CI (Continuous Integration). I’ve used Scrutinizer, Travis and DeployBot for professional projects, but I wanted to create a deployment system for my smaller, personal projects.

I have many small personal sites that I want to be able to easily edit from anywhere without depending on having everything on my local machine. I wanted to only require git and a text editor, and let the deployment tool handle the rest.

Most of these sites don’t make any money so I wasn’t real excited about paying for tools (even though I’d happily do so for my other projects) and it was also a fun challenge. I wanted to bring the structure and ease of deployment I have in my professional projects to my personal projects. I usually avoided making changes to these projects as I could never remember where the servers were, the credentials and what had potentially changed on the server in the meantime.

The Criteria

I needed this system to be free, automated, and host and local machine agnostic.


As mentioned above, I wanted to see whether I could do this with freely available tools. Gitlab CI provides free runners that you can use, or you can run your own on a server you control. For these projects, I am only using the free runners as they are not mission critical applications.


I could use these tools to deploy straight from my local machine, but I wanted to be able to deploy just by pushing to Gitlab or by clicking a button in the UI.

Local Machine Agnostic

You can use any of the command line deployment tools I mention below to sync local files to another server. This is great, but it would mean that every machine I wanted to deploy from needed to have these tools installed and configured, and I needed to have the login credentials available.

Host Agnostic

My goal here was to have one deployment process that I could use regardless of host. Most the hosts provide SSH access, but not all. So I wanted something that only required STFP to deploy. This ruled out tools like Capistrano for me as SSH access is required.

The Deployment Tool

I originally used Dandelion, a Ruby deployment tool. It worked great, but the Ruby dependency meant I needed a separate container just to install it. The build runs much quicker if only one container is needed, so I sought out a PHP deployment tool.

Enter PHPloy.

PHPloy is an “Incremental Git (S)FTP deployment tool that supports multiple servers, submodules and rollbacks.” Rather than deploying your entire repo every time, it will only deploy what has changed since your last deployment. It does this by writing a .revision file to your remote server that stores the most recent deployment’s git commit hash. PHPloy determines which files have changed compared to that revision and only deploys those.

Some of the features that were great in Dandelion were missing or broken in PHPloy. It took a few PRs and bug reports to get it to the point where I’m comfortable using it for my projects and recommending it. The repository is fairly active and the author regularly merges PRs. I suggest getting involved on Github if this is a tool that would be useful to you.


All you need to get started with Gitlab CI is a Gitlab repo with a .gitlab-ci.yml file. Below is an example configuration for deploying a PHP project with PHPloy.


- deploy

  image: php:5.6-cli
  stage: deploy
  environment: production
    - apt-get update -yqq
    - apt-get install git zip unzip curl wget openssh-client -yqq
    - mkdir -p ~/.ssh
    - echo "$PRIVATE_KEY" | tr -d '\r' > ~/.ssh/deploy_rsa
    - chmod 600 ~/.ssh/deploy_rsa
    - ssh-keyscan -p $PHPLOY_PORT -H "$PHPLOY_HOST" >> ~/.ssh/known_hosts
    - wget --quiet
    - unzip -qq
    - mv ./PHPloy-master/dist/phploy.phar phploy
    - php phploy -s production
  when: manual

This file creates one stage called deploy that is run manually when triggered from the Gitlab UI.

  • Uses a PHP5.6-cli image which matches our production environment. You can choose any docker image.
  • Installs dependencies needed for deployment.
  • Copies the private key needed for server authentication to the container and adds the destination server to known_hosts.
  • Downloads the latest version of PHPloy
  • Runs the production PHPloy configuration to deploy.


; NOTE: If non-alphanumeric characters are present, enclose in value in quotes.
scheme = sftp
path = /home/site/public_html
privkey = '~/.ssh/deploy_rsa'
branch = master
exclude[] = '.env.example'
exclude[] = '.gitignore'
exclude[] = '.gitlab-ci.yml'
exclude[] = 'phploy.ini'
exclude[] = ''

This file:

  • Sets the scheme, you’re not limited to SFTP.
  • The remote server deployment path.
  • The private key file path.
  • The branch to deploy.
  • Files to exclude

For SSH deployments you can use pre and post deploy hooks to run commands to backup or flush cache on deploy.

The only other configuration you need is to define four environment variables in Gitlab: PHPLOY_HOST, PHPLOY_PORT, PHPLOY_USER, and PRIVATE_KEY and create an environment.

Gitlab PHPloy environment variables

For my example here, I created an environment named production which matches the environment in the .gitlab-ci.yml file.

Once this is configured, on your next push you should see a pipeline trigger in Gitlab and provide the option to run your deploy_to_production stage.

Gitlab Pipeline Deployment

What’s missing?

Some of the downsides:

  • This isn’t a generic deployment tool. It works best for PHP projects that use git.
  • No easy way to include untracked files. This is something I love with DeployBot’s Build Tools. Any new files created as part of the build process are deployed to your server, as long as they aren’t .gitignored. I’m still looking for a good option here.
  • It takes longer to set up than an off-the-shelf deployment tool.

Future plans:

  • Use composer to pull down PHP dependencies and WordPress plugins.
  • Determine how to include untracked files (like composer dependencies).
  • Run unit tests and build scripts on the container before deploying.

There’s still more I can do here and may in the future, but for most of my small projects this works great. How do you deploy your hobby projects?

Update: I’ve edited the .gitlab-ci.yml file to reflect the new location of the phploy phar file.

Sources: Pushing to Dokku from Gitlab CI

Write a Comment


This site uses Akismet to reduce spam. Learn how your comment data is processed.

  1. Josh this is cool. I’ve been trying to get GitLab CI based deployment working for ages and never seem to get it working just right. I have the added hurdle needing to do it with SFTP user/pass because one host doesn’t support SSH keys (LWMWP), however I still haven’t gotten it working reliably using LFTP. I’m going to try PHPloy, it looks cool.

    1. How’s it working lately? Still going strong
    2. Would it be better to use a docker container for PHPloy? for example.

    • 1. Yep, still working great. One thing I’m not sure it handles is stacking deployments. If you push master multiple times without running the deploy job, I’ve been running them sequentially still because I’m not sure if it will include all unpushed commits or not.
      2. It probably would be, Docker is not my strong point, so I limited its use in this.