{"id":5802,"date":"2021-04-08T04:27:48","date_gmt":"2021-04-08T04:27:48","guid":{"rendered":"https:\/\/blog.ssdnodes.com\/blog\/?p=5802"},"modified":"2025-05-18T19:53:09","modified_gmt":"2025-05-18T19:53:09","slug":"git-hooks-deploy","status":"publish","type":"post","link":"https:\/\/www.ssdnodes.com\/blog\/git-hooks-deploy\/","title":{"rendered":"Git Hooks &#8212; Easily deploy code via git push"},"content":{"rendered":"<p>The best way to get into any field is to start with a problem statement, and then learn the necessary concepts as you try and solve that problem. The same is true with DevOps and today we will start with a very simple problem and craft a very rudimentary solution for it.<\/p>\n<h2>Goal: Deploying code to remote server via git push<\/h2>\n<p>Let's say you have a website hosted on a VPS, and you have a local copy of its source code on your laptop. As your needs change, you will inevitably want to make some changes to the website. If you are using git to track changes to your source code, you can use another functionality of <code>git<\/code> called <code>git hooks<\/code> to push your local changes to the remote server, and have the server deploy it for you.<\/p>\n<p>This is the crudest, and yet very effective way of getting started with DevOps. A practive which merges the Development workflow closely with that of Operations.<\/p>\n<h2>Prerequisites<\/h2>\n<p>To follow along this tutorial you will need:<\/p>\n<ul>\n<li>A very basic familiarity with the Linux command line.<\/li>\n<li>A very basic idea of how to use git, creating a repository and committing changes.<\/li>\n<li>A VPS with a public IP for which you have <a href=\"https:\/\/www.ssdnodes.com\/blog\/connecting-vps-ssh-security\/\">key-based SSH access<\/a> as root user.<\/li>\n<\/ul>\n<h2>Setting the stage<\/h2>\n<p>We will start with a very simple setup where an Nginx Server is serving plain HTML files on port 80 and we will make changes to these files locally and push the changes to the server running Nginx. If you follow along, by the end of the setup, just reloading the webpage at <a href=\"#\">http:\/\/your_ip_address<\/a> will show you all the changes that were pushed without you having to manually login into the server.<\/p>\n<h2>Local setup<\/h2>\n<p>To start with, you will need git installed on your local computer. Windows users should look at <a href=\"http:\/\/git-scm.com\/\" target=\"_blank\" rel=\"noopener\">git-scm<\/a> and if you are using Linux or WSL2 then you can install it via your package manager. Similarly, macOS users can install git via brew package manager or by simplying installing Xcode or Xcode Command Line Tools.<\/p>\n<p>Great! Now that we have git installed, we create a simple git repo, and add some very simple HTML code in it.<\/p>\n<pre><code>$ mkdir html\r\n$ git init\r\nInitialized empty Git repository in C:\/Users\/r\/html\/.git\/<\/code><\/pre>\n<p>Create a file in <code>~\/html<\/code> directory named <code>index.html<\/code> and add the following contents to it:<\/p>\n<pre><code>&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n&lt;title&gt;Welcome to My Website!&lt;\/title&gt;\r\n&lt;style&gt;\r\n    body {\r\n        width: 35em;\r\n        margin: 0 auto;\r\n        font-family: Tahoma, Verdana, Arial, sans-serif;\r\n    }\r\n&lt;\/style&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n&lt;h1&gt;Welcome to My Website.&lt;\/h1&gt;\r\n&lt;p&gt;If you see this page, the nginx web server is successfully installed and\r\nworking. Further configuration is required.&lt;\/p&gt;\r\n\r\n&lt;p&gt;For online documentation and support please refer to\r\n&lt;a href=\"http:\/\/nginx.org\/\"&gt;nginx.org&lt;\/a&gt;.&lt;br\/&gt;\r\nCommercial support is available at\r\n&lt;a href=\"http:\/\/nginx.com\/\"&gt;nginx.com&lt;\/a&gt;.&lt;\/p&gt;\r\n\r\n&lt;p&gt;&lt;em&gt;Thank you for using nginx.&lt;\/em&gt;&lt;\/p&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;<\/code><\/pre>\n<h4>Side Note<\/h4>\n<p>If you are using git for the first time. You will have to use <code>git config --global user.name \"your_name\"<\/code> and <code>git config --global user.email \"your@example.com\"<\/code> to set basic information that <code>git<\/code> uses to tag your commits.<\/p>\n<p>Next commit those files into your repo:<\/p>\n<pre><code>$ git add index.html\r\n$ git commit -m \"Add index.html\"<\/code><\/pre>\n<p>If you have used remote hosted git repositories such as those provided by GitLab, BitBucket or GitHub, you may have come across the <code>git remote<\/code> command. We will be using the same mechanism to push our files to the remote server. Just replace the <code>your_ip_address<\/code> part below with the actual IP Address (or FQDN) of your remote server.<\/p>\n<pre><code>$ git remote add production root@your_ip_address:\/var\/www\/html.git<\/code><\/pre>\n<p>This tells git that we have a new remote endpoint for this repository called <code>production<\/code> located at <code>\/var\/www\/html.git<\/code> on IP Address <code>your_ip_address<\/code>, and, to push to this repository as user <code>root<\/code>. Usually when pushing to GitHub or GitLab our remote is called <code>origin<\/code>, the user is <code>git<\/code> and the location is <code>username\/reponame.git<\/code>.<\/p>\n<p>But the remote endpoint does not exist yet. So, let's create that.<\/p>\n<h2>Remote Server Setup<\/h2>\n<p>The following is a one time setup for which you will have to SSH into your server. We will start by creating a bare directory for git repo. This is where all your git commits will be stored in a format that only git can understand, i.e, as a tree of commits and other objects. You don't have to know about its inner workings, just know that this where all the history of your repo lives.<\/p>\n<pre><code>$ cd \/var\/www\r\n$ mkdir \/var\/www\/html.git\r\n$ cd \/var\/www\/html.git\r\n$ git init --bare<\/code><\/pre>\n<p>Next we will use another feature of git which are called <code>git hooks<\/code>. These are basically shell scripts that get executed when a certain action is performed inside the repository. In case of our <code>production<\/code> remote we will create a hook that publishes the updated contents of our repository <code>\/var\/www\/html.git<\/code> to our webserver's root <code>\/var\/www\/html<\/code> directory.<\/p>\n<p>Inside <code>\/var\/www\/html.git<\/code> directory you will find a <code>hooks<\/code> folder with a bunch of sample scripts. We will ignore these and create a new file with the exact name of <code>post-receive<\/code>.<\/p>\n<pre><code>$ cd \/var\/www\/html.git\/hooks<\/code><\/pre>\n<p>Create a file called <code>post-receive<\/code> in here with the following content inside it and save it.<\/p>\n<pre><code>#!\/bin\/sh\r\n\r\n# Check out the files\r\ngit --work-tree=\/var\/www\/html --git-dir=\/var\/www\/html.git checkout -f<\/code><\/pre>\n<p>Next, we need to make the <code>post-receive<\/code> file executable.<\/p>\n<pre><code>$ chmod +x \/var\/www\/html.git\/hooks\/post-receive<\/code><\/pre>\n<p>Install Nginx and if the <code>html<\/code> directory is not present, create it:<\/p>\n<pre><code>$ sudo apt install nginx -y \r\n$ mkdir -p \/var\/www\/html<\/code><\/pre>\n<p>This is it for the remote setup. Let's take our setup for a spin!<\/p>\n<h2>Testing the workflow<\/h2>\n<p>If you've installed Nginx, then <code>\/var\/www\/html<\/code> is the default root for that webserver, and its contents will be shown in the web browser as follows:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"\" title=\"Nginx Default HTML Page\" src=\"https:\/\/www.ssdnodes.com\/wp-content\/uploads\/2021\/04\/Capture.png\" alt=\"Nginx Default HTML Page\" width=\"1073\" height=\"517\" \/><\/p>\n<p>Now let's push our new HTML code from local setup:<\/p>\n<pre><code>$ git push production master<\/code><\/pre>\n<p>If you now reload the website, you will see that the title and the first heading have been revised to match our \"custom\" HTML file.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"\" title=\"New Commit\" src=\"https:\/\/www.ssdnodes.com\/wp-content\/uploads\/2021\/04\/new-commit.png\" alt=\"New Commit\" width=\"1080\" height=\"391\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>This is it! You are now free to work on your local git repo and push changes with just a single command.<\/p>\n<h2>End Note<\/h2>\n<p>The post-receive script is just a general shell script. You can use this to reload nginx or MySQL or any other service as well as perform other actions that are required for more complicated apps running on NodeJS, PHP or the language of your choice. As your project grows you can add other remote servers for building, testing and much much more.<\/p>\n<p>Finally, you should remember that while all the contents of your source code are pushed to the remote endpoint, the same isn't true for metadata like remote endpoints. If you were to push the repo to GitHub as well, you can do it, and they won't be able to see that you have an additional remote endpoint called production. Neither will the git hook script of post-receive ever leave your server. These are not part of the repository's contents.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The best way to get into any field is to start with a problem statement, and then learn the necessary concepts as you try and solve that problem. The same is true with DevOps and today we will start with a very simple problem and craft a very rudimentary solution for it. Goal: Deploying code  &#8230;<\/p>\n","protected":false},"author":20,"featured_media":5821,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[18,30],"tags":[215],"class_list":["post-5802","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","category-tutorials","tag-git"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/posts\/5802","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/users\/20"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/comments?post=5802"}],"version-history":[{"count":4,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/posts\/5802\/revisions"}],"predecessor-version":[{"id":13074,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/posts\/5802\/revisions\/13074"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/media\/5821"}],"wp:attachment":[{"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/media?parent=5802"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/categories?post=5802"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/tags?post=5802"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}