I site I'm working on uses a post-receive git hook for deploying changes to the server.

At the moment, it looks like this:


# default cpanel stuff
branch=$(/usr/local/cpanel/3rdparty/bin/git branch | awk '$1 == "*"{print $2}')
while read oldrev newrev ref
  if [ "x$ref" == "xrefs/heads/$branch" ]
    echo "Recieved update on checked-out branch, queueing deployment."
    (cd .. ; /usr/bin/uapi VersionControlDeployment create repository_root=$PWD)

  branch=`echo $ref | cut -d/ -f3`

  cd ../
  # run from dir outside of .git
  # unset git dir because git ugh https://stackoverflow.com/a/10513684
  unset GIT_DIR

  if [ "master" == "$branch" ]; then
    # echo this is master
    # create new working tree in the desired directory
    git --work-tree=../prod-test checkout -f $branch

  if [ "develop" == "$branch" ]; then
    # echo this is develop
    git --work-tree=../staging checkout -f $branch

done < /dev/stdin

That works fine. I push from my local and the changes get deployed to staging.

But I want project config and plugin update changes to be automatically applied rather than having to visit the CP and click the sync changes button.

I've seen elsewhere that the way to do that is to run

  ./craft project-config/sync
  ./craft clear-caches/all
  composer update

so I thought I could add it to the hook, just above done < /dev/stdin but it doesn't work, I still have to manually update.

Has anyone else done something like this before?

  • My guess is that you need to cd into the right directory where the craft script is located. Depending on your server settings you may also need to run it as a different user with sudo -u someone-else. What happens when you run the command manually? Commented Mar 11, 2020 at 11:20
  • I can run the commands manually OK after SSHing in to the server. I thought CDing up one level was the right directory because that's where the files get checked out to.
    – Tyssen
    Commented Mar 12, 2020 at 0:05
  • I'd probably first look to log the output of those commands somewhere, which will hopefully give you some kind of error to work with. e.g. ./craft project-config/sync >> git-hooks-log-file.txt Commented Mar 12, 2020 at 9:47
  • I did that and it's telling me that Craft isn't installed yet which would probably be because it's trying to execute the command from the wrong directory. But the thing is, it created the log file in the same directory as Craft is installed so presumably it's executing the commands from the same directory it was creating the log file in.
    – Tyssen
    Commented Mar 13, 2020 at 1:43

2 Answers 2


Based on your comment that you get an error saying Craft is not installed, to me that suggests the bash script does not have access to your environment variables, (this can also be a problem when running cron jobs).

To remedy this, create an env text file to export all the same values as those in your production env variables. For this example name it something like .env_cli.txt and save it in your site in the same place as .env-example if you have one. Add it to your .gitignore too. Syntax should look something like this:

export ENVIRONMENT="live"
export SITE_URL="https://www.example.com"
export DB_DRIVER="mysql"
export DB_SERVER="123.456.789.10"
export DB_USER="aaa"
export DB_PASSWORD="bbb"
export DB_DATABASE="ccc"
export SECURITY_KEY="ddd"
export DB_TABLE_PREFIX="craft_"

Then in your git hook, you can use a leading . to "source" that env file (basically meaning to suck in and consume all those exported vars), and then run your command(s) afterwards like this:

. /path/to/your/env-file/.env_cli.txt && /path/to/craft/./craft project-config/sync && /path/to/craft/./craft clear-caches/all && /path/to/craft/composer update

You could of course stick with relative paths to make it shorter, I just tend to use absolute paths for more certainty.

  • I added . /home/pblog/pb-repo/.env_cli.txt && /home/pblog/staging/./craft project-config/sync && /home/pblog/staging/./craft clear-caches/all && /home/pblog/staging/composer update >> /home/pblog/pb-repo/git-hooks.log to the end of the hook file and when I visited the CP, I didn't get the warning about applying config changes (like I was before). But also, Craft hadn't been updated to the same version as my local, and the git-hooks.log file I created didn't get updated either. So it seems it ran project-config/sync but not the rest.
    – Tyssen
    Commented Mar 16, 2020 at 2:41
  • I think you'll need to repeat the >> /home/pblog/pb-repo/git-hooks.log after each command in order to log the output. If a command doesn't succeed it won't progress past the && to the next command, so your logging won't catch errors. Commented Mar 16, 2020 at 9:13
  • Thanks. Everything seems to be working now except for this: . /home/pblog/pb-repo/.env_cli.txt && /home/pblog/staging/composer update >> /home/pblog/pb-repo/git-hooks.log which is giving me this error PHP Fatal error: require_once(): Failed opening required '/home/pblog/pb-repo/vendor/autoload.php' (include_path='.:/opt/alt/php70/usr/share/pear') in /home/pblog/pb-repo/craft on line 12 The path is correct though, autoload.php does exist there.
    – Tyssen
    Commented Mar 17, 2020 at 0:09
  • Sounds like maybe a permissions issue remains. I'd first try executing that command as the same user as when you successfully ran it manually. You can temporarily switch users with sudo -u username before a command, but this assumes that the user running the command has permission to use sudo, so you might also need to add them to the sudoers file and specify which commands they can run. E.g. serverfault.com/questions/269359/… Commented Mar 17, 2020 at 12:25
  • Adding sudo -u username to the beginning of the composer update command made the error go away but it's also still not executing, as no updates are being made. The log file just shows the successful output of running project-config/sync and clear-caches
    – Tyssen
    Commented Mar 17, 2020 at 23:53

Late to the party and I can't really help on the git hook code per se.

But, for those who like me found this as they are looking how to automate their deployments, know that you should use composer install --no-interaction and not composer update. This ensure that the components installed will be exactly the same as your local environment.

This error explains why Tyssen had a different Craft version once his script was working.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.