Deploying to server with git

August 23, 2020

Tags: git, digital ocean, deployment

I have a Digital Ocean Droplet that has been hosting an old Python project for years now. I distinctly remember being able to git push to deploy that service, but I don’t remember how I set it up. Now I’m going back to figure that out. First, I have a look at my project on my old development machine, to maybe get a bit wiser:

~/projects/projectname $ git remote -v
live	ssh:// (fetch)
live	ssh:// (push)
livegit (fetch)
livegit (push)
origin (fetch)
origin (push)

There is an origin pointing to Github, fine. Apparently, I was also thinking to separate the web page from the api I was developing. That was never how I ended up doing things, I’m certain. But that’s that. Anyway, we see that I was using a user named git on the server. That means I was not deploying with my admin account. Good thing, that.

Now, lets have a look at the server itself.


The server process runs in a tmux session. That session can be accessed with tmux attach. We shut down the session with Ctrl+C and see that the site itself is located in /var/www/project. This is different from the repo location that we just found, and I’d like to know why.

To recap: The dev machine is pushing to a remote repo called live. It lives on the VM under /var/repo/project.git, meanwhile, the server process is hosted from the directory /var/www/project. Again, why? And how?

Looking around /var/repo/project.git, we find the hooks folder. This contains a number of files, one of them is called post-receive. It’s the only file not named *_sample. Interesting.

$ cat /var/repo/project.git/hooks/post-receive
git --work-tree=/var/www/project --git-dir=/var/repo/project.git checkout -f

This is a clever hack for keeping the work tree and the repo separate. Obviously, Git is called, but then the two locations are passed as named parameters, and finally the checkout argument is forced. When this command is used in a git hook like this, it runs automatically every time anything is pushed to the repo. The checkout means that the code that is live on the site is going to get refreshed with the latest changes.

This operation translates to a re-deploy of the entire repo, thus updating the web service.

But why? Well, this was a long time ago, and I am sure I had not heard about continuous delivery at that point. This solution allows for direct and secure communication with the deployment server through ssh, and with no middle man. In other words, it’s a deploy script. Looking back, this seems like a good value proposition, discounting the fact that there is no automated regression testing in sight. But had I known you could deploy a branch automatically with Jenkins, I would probably have done that. Also, I’m still not sure why the repo and work tree are separate.

That said, this is a fun exercise for learning about git hooks, and maybe exploring how to go about building an automation system of one’s own.