Services like GitHub and GitLab are fantastic, and already satisfies the need of most as git hosting service. And let me be clear from the start, setting up a self-hosted git server is not for beginners. However, if you have the right need to do so, it can give you TOTAL control over your code, your deployment process, and your infrastructure.
I had the opportunity to work on setting up an custom git based source control server in the past, that would let me push code and deploy it automatically. No complicated CI/CD pipelines, no third-party services, just Git doing what Git does best – managing code.
In this guide, I’ll walk you through the entire process of setting up a self-hosted Git server with automated deployment using Git hooks. Trust me, this will revolutionize your workflow, especially if your use case demands it!
Let’s dive in!
Before we start, make sure you have:
First things first, let’s get Git installed on your server. SSH into your VPS and run:
sudo apt update
sudo apt install git Nothing fancy here, just the standard Git installation. Once it’s installed, let’s verify:
git --version You should see something like git version 2.25.1 or newer.
For security and organization, we’ll create a dedicated Git user:
sudo adduser git Follow the prompts to create the user. This user will own all our Git repositories and handle all Git-related operations.
Rather than using passwords, we’ll use SSH keys for authentication – it’s more secure and makes automation much easier.
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"Code language: JavaScript (javascript) ssh-copy-id git@your_server_ipCode language: CSS (css) ssh git@your_server_ipCode language: CSS (css) If everything’s set up correctly, you should be able to log in without a password.
While it’s technically optional, I STRONGLY recommend installing Gitolite. It makes managing Git repositories and user access so much easier.
sudo su - git git clone https://github.com/sitaramc/gitoliteCode language: PHP (php) mkdir -p $HOME/bin
gitolite/install -to $HOME/binCode language: PHP (php) mkdir -p $HOME/.ssh
touch $HOME/.ssh/authorized_keysCode language: PHP (php) scp ~/.ssh/id_rsa.pub git@your_server_ip:~/admin.pubCode language: JavaScript (javascript) $HOME/bin/gitolite setup -pk admin.pubCode language: PHP (php) Now, Gitolite is installed and configured with your key as the admin key.
On your local machine, clone the gitolite-admin repository:
git clone git@your_server_ip:gitolite-adminCode language: PHP (php) This repository is where you’ll manage repositories and users.
To create a new repository, you’ll need to modify the gitolite configuration:
gitolite-admin/conf/gitolite.conf file:repo gitolite-admin
RW+ = admin
repo testing
RW+ = @all
# Add your new repository
repo myproject
RW+ = admin
RW = developerCode language: PHP (php) cd gitolite-admin
git add conf/gitolite.conf
git commit -m "Added myproject repository"
git pushCode language: JavaScript (javascript) Gitolite will automatically create the repository on the server.
To add a new user:
gitolite-admin/keydir/username.pubFor example:
# Add a new developer
repo myproject
RW+ = admin
RW = developer @developersCode language: PHP (php) And in gitolite-admin/keydir, add the new user’s public key as newuser.pub.
This is where the magic happens. We’ll set up a single Git repository that deploys to both development and production environments based on the branch being pushed.
sudo mkdir -p /var/www/dev/myproject
sudo mkdir -p /var/www/prod/myprojectCode language: JavaScript (javascript) sudo chown git:www-data /var/www/dev/myproject
sudo chown git:www-data /var/www/prod/myprojectCode language: JavaScript (javascript) sudo chmod 775 /var/www/dev/myproject
sudo chmod 775 /var/www/prod/myprojectCode language: JavaScript (javascript) The post-receive hook is the secret sauce that makes automated deployment possible. When you push to your repository, this hook will automatically deploy your code to the right environment.
cd /home/git/repositories/myproject.git/hooks post-receive:sudo nano post-receive #!/bin/bash
while read oldrev newrev ref
do
branch=$(echo $ref | cut -d/ -f3)
if [ "$branch" == "dev" ]; then
echo "Deploying to development environment..."
WORK_PATH="/var/www/dev/myproject"
elif [ "$branch" == "master" ]; then
echo "Deploying to production environment..."
WORK_PATH="/var/www/prod/myproject"
else
echo "Not deploying branch $branch"
exit 0
fi
<em># Check if work path exists</em>
if [ ! -d "$WORK_PATH" ]; then
echo "Creating $WORK_PATH..."
mkdir -p $WORK_PATH
cd $WORK_PATH
git clone /home/git/repositories/myproject.git .
git checkout $branch
else
cd $WORK_PATH
unset GIT_DIR
git reset --hard
git checkout $branch
git pull origin $branch
fi
<em># Run any additional deployment tasks</em>
echo "Running deployment tasks..."
<em># Example: composer install, npm install, etc.</em>
<em># composer install --no-dev</em>
<em># npm install --production</em>
<em># Fix permissions</em>
find $WORK_PATH -type f -exec chmod 664 {} \;
find $WORK_PATH -type d -exec chmod 775 {} \;
echo "Deployment of $branch completed!"
doneCode language: PHP (php) sudo chmod +x post-receive This hook will:
One of the trickiest parts of setting up automated deployment is dealing with permissions. Here are some approaches to solve common issues:
This is a clean approach that works well in most scenarios:
sudo usermod -aG git www-data Then, make sure group permissions are properly set:
sudo chmod g+s /var/www/dev/myproject
sudo chmod g+s /var/www/prod/myprojectCode language: JavaScript (javascript) This ensures new files inherit the group permissions.
If you have control over your web server configuration, you can run it as the git user:
For Nginx:
sudo nano /etc/nginx/nginx.conf Change the user at the top:
user git; For Apache:
sudo nano /etc/apache2/envvars Change the following lines:
export APACHE_RUN_USER=git
export APACHE_RUN_GROUP=gitCode language: JavaScript (javascript) Restart your web server after making these changes.
If you need more complex permission management:
sudo apt install acl
sudo setfacl -R -m u:git:rwX,u:www-data:rwX /var/www/dev/myproject
sudo setfacl -R -d -m u:git:rwX,u:www-data:rwX /var/www/dev/myprojectCode language: JavaScript (javascript) This gives both users full access to the directory.
You can extend the post-receive hook to handle more complex scenarios, like deploying specific branches to specific subdomains:
if [ "$branch" == "feature-x" ]; then
echo "Deploying feature-x to its dedicated environment..."
WORK_PATH="/var/www/features/feature-x"
fiCode language: PHP (php) Add notification functionality to your post-receive hook:
# Send deployment notification
notify() {
local branch=$1
local env=$2
local message="Deployment of $branch to $env completed!"
<em># Send email notification</em>
echo "$message" | mail -s "Deployment Notification" your-email@example.com
<em># Or send Slack notification</em>
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$message\"}" \
https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
}
# Call the function after deployment
notify $branch "production"Code language: PHP (php) Add a simple rollback capability:
# Create a rollback function
rollback() {
local branch=$1
local work_path=$2
cd $work_path
git reset --hard HEAD~1
echo "Rolled back $branch to previous commit"
}
# You can call this function manually or add logic to trigger it automaticallyCode language: PHP (php) Security is crucial when hosting your own Git server. Here are some essential steps:
Edit the SSH config to restrict git user access to Git commands only:
sudo nano /etc/ssh/sshd_config Add the following lines:
Match User git
ForceCommand /usr/bin/gitolite-shell
PasswordAuthentication no
AllowTcpForwarding no
X11Forwarding no Restart SSH:
sudo systemctl restart sshd Install and configure UFW (Uncomplicated Firewall):
sudo apt install ufw
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable For secure Git operations, set up HTTPS:
Set up regular backups of your Git repositories:
# Create a backup script
sudo nano /usr/local/bin/backup-git.shCode language: PHP (php) Add the following content:
#!/bin/bash
DATE=$(date +%Y-%m-%d)
BACKUP_DIR="/var/backups/git/$DATE"
mkdir -p $BACKUP_DIR
# Backup repositories
tar -czf $BACKUP_DIR/git-repos.tar.gz /home/git/repositories
# Keep backups for 30 days
find /var/backups/git -type d -mtime +30 -exec rm -rf {} \;Code language: PHP (php) Make it executable and add a cron job:
sudo chmod +x /usr/local/bin/backup-git.sh
sudo crontab -e Add the following line:
0 2 * * * /usr/local/bin/backup-git.shCode language: JavaScript (javascript) Install basic monitoring tools:
sudo apt install htop iotop For more comprehensive monitoring, consider setting up Prometheus and Grafana.
Regular maintenance is essential:
# Update system packages
sudo apt update && sudo apt upgrade -y
# Clean up old Git objects
cd /home/git/repositories/myproject.git
git gc --aggressiveCode language: PHP (php) Setting up a self-hosted Git server with automated deployment might seem like a lot of work at first, but the benefits are ENORMOUS. You get:
The ability to push code and have it automatically deployed to the right environment without any manual intervention is incredibly powerful. Taking control of your Git infrastructure gives you flexibility and power that’s hard to match with third-party services.
So, what use case you are trying to solve for with your own self-hosted Git server? Let me know in the comments.
While this guide focuses on Linux-based servers, you can adapt the concepts to Windows servers using Git for Windows and PowerShell scripts instead of bash.
Yes, with proper configuration. The key is to use SSH keys for authentication, restrict access appropriately, and keep your server updated.
You can add database migration commands to your post-receive hook, but be careful with production databases. Consider using a migration framework that supports versioning and rollbacks.
Absolutely! The post-receive hook can trigger container builds and deployments. You’d modify the hook to build and deploy containers instead of directly updating files.
For larger teams, consider adding more structure to your gitolite configuration, implementing code review processes, and potentially using additional tools like Gerrit or GitLab for merge request management.
Learn python file handling from scratch! This comprehensive guide walks you through reading, writing, and managing files in Python with real-world examples, troubleshooting tips, and…
You've conquered the service worker lifecycle, mastered caching strategies, and explored advanced features. Now it's time to lock down your implementation with battle-tested service worker…
Unlock the full potential of service workers with advanced features like push notifications, background sync, and performance optimization techniques that transform your web app into…
This website uses cookies.