Deploying to a web server using GitHub webhooks

By Samuel, 4 February, 2022

Secure and fairly simple approach using a PHP handler script

When you manage your repository on GitHub and want to deploy it to your web server, you can first look for a suitable GitHub action. If you're using the services of a Content Delivery Network or one of the big cloud providers, you most likely will find a specialized GitHub action for exactly that purpose that you can use. But if you have your site with some typical shared web hosting company or on your virtual private server, maybe you find yourself wondering as I did:

How on earth do I automate deployment in that simple scenario?

I understand the principles behind, so of course I want to manage everything with git and of course I want to set up a good CI/CD process. But I don't have much practical experience with it yet and finding a solution here was much harder than I thought before. The many commercial CI/CD providers as well as the open source automation server Jenkins are targeting other scenarios, so here they would be overkill and still not solve my situation.

Looking through the simpler options, I first dismissed the idea of deploying via SFTP: Really feels like last century and doesn't scale when the repository gets bigger - not an option for the main repository for a whole Drupal or mediawiki site.

I have SSH access to the server and can run git on it, so essentially I want to trigger a git pull on the server in the right directory when something is happening in the repository (and be able to extent more functionality in the future).

Next idea: Deployment via SSH

I did quite some research and the best guide I could find was Deploying to a server via SSH and Rsync in a Github Action. Following it I got it working but I realized that I feel uncomfortable about this approach: I end up storing a private SSH key as a GitHub secret. I assume GitHub is trying their best to protect secrets, but still: If that gets leaked to someone, they have full access to the server. And we all know that this might happen - Jesus already said “there is nothing concealed that will not be disclosed, or hidden that will not be made known” (The Bible, Matthew 10:26)

Better alternative: Limit the SSH key to a single command

A friend hinted me at a way to redeem this approach: In ~/.ssh/authorized_keys I can limit a SSH key to one command (see command= directive in “man sshd”). So I can limit the private key stored as a GitHub secret to be only able to run my deploy script and nothing else. That is definitely acceptable. Still it doesn’t feel like the most elegant solution: Getting the GitHub action working was a bit tedious, I need to edit ~/.ssh/authorized_keys and still I need to store the exact SSH url and port number as secrets on GitHub.

Also I noticed that when merging a PR with five commits, the action got triggered five times as well. All of them started running at the same time, doing all the same things, but only two of them were successful, the other three failed at different points. I did not go into details of finding out what went wrong - it seems it could be related on how the action needs to set up itself first and/or my shared hosting provider may have limits on concurrent SSH connections.

Implementing the best solution: Webhooks

So I looked into the most secure and flexible solution: Webhooks. First I was hesitant because I didn't see any ready-to-use solution for my scenario. But after seeing the disadvantages of my first attempts I decided to put some effort into it and create a solution that hopefully others can use as well. It's easy to configure GitHub to call a custom URL like whenever something is pushed to the repository. Now I just need to put a PHP handler script on that address on my server which will trigger the deployment script.

There were some existing code snippets but they all seemed to be a bit outdated and weren't working out-of-the box (did I miss something?) So I took one, improved the code, added two example deploy scripts and put everything into a public repo:

PHP handler for webhooks

Now you just need to configure which script should be run for which repository and branch, check your deployment script and that's it - see the readme in the repository for detailed instructions.

It is now in production to deploy this very website (see the repository on GitHub) as well :)

Some final thoughts

Thinking about why there doesn't seem so much around for this simple use case, I came up with some guesses:

  • Enough people are fine with entrusting a SSH private key to GitHub (or alternatives like deployer) - am I too paranoid?
  • Many people using simple shared web hosting don't use git and automated deployment to manage their stuff
  • Those who are managing everything with git and have good CI/CD often have bigger systems, like a self-hosted GitLab instance or jenkins to deploy to their servers

I'd be happy to hear your thoughts and feedback!


Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.