Back to Blog

How to Deploy with Git: Automatic Deployment Guide

How to deploy to a server with Git: deploy keys, a deploy script, automatic deploys with GitHub Actions, a separate user, secrets and rolling back.

Nodesty Team6 min readServers
The words Git Deploy with SSH, Actions and Rollback on a dark Nodesty background

Deploying with Git means releasing your application by pulling its code from a Git repository instead of uploading it to the server by hand with FTP. In its simplest form, you run git pull on the server and restart the application; one step further, a tool like GitHub Actions does this for you as soon as you push code to the main branch. Because every release matches a commit, you always know what's running on the server, and when something goes wrong you can go back within seconds.

This guide covers adding a deploy key to the server, writing a deploy script, setting up automatic deploys with GitHub Actions, security measures and rolling back.

Why deploy with Git?

  • Repeatability: Every release is done with the same steps; the "I forgot that file this time" problem goes away.
  • Traceability: You can see which commit is running on the server with git log -1.
  • Easy rollback: You go back from a faulty release to the previous version with a single command.
  • Teamwork: Everyone pushes code to the repository; only the deploy process touches the server.

Step 1: A separate deploy user

Run the application and the deploy process as a separate user instead of root. After connecting to your server with SSH:

sudo adduser deploy
sudo mkdir -p /var/www/app
sudo chown deploy:deploy /var/www/app
sudo su - deploy

Step 2: The deploy key

So that the server can pull code from the repository, create a deploy key that can access only that repository:

ssh-keygen -t ed25519 -f ~/.ssh/deploy_key -N "" -C "deploy@server"
cat ~/.ssh/deploy_key.pub

Add the public key on GitHub under the repository's Settings → Deploy keys → Add deploy key and leave write access turned off. Then tell SSH to use this key for GitHub by adding this to the ~/.ssh/config file:

Host github.com
    IdentityFile ~/.ssh/deploy_key
    IdentitiesOnly yes

Clone the repository onto the server:

git clone git@github.com:user/project.git /var/www/app

Because the deploy key can read only a single repository, even if the server is compromised, the attacker can't reach your other repositories or your account.

Step 3: The deploy script

Put the steps that run on every release into a script, for example /home/deploy/deploy.sh. For a Node.js application:

#!/bin/bash
set -euo pipefail

cd /var/www/app
git fetch origin
git reset --hard origin/main

npm ci
npm run build

pm2 reload app
echo "Deployed: $(git log -1 --oneline)"
  • set -euo pipefail: If any step fails, the script stops, so a half-finished build never goes live.
  • git reset --hard origin/main: Makes the code on the server exactly match the repository's main branch. Changes made by hand on the server are deleted, which is why the habit of changing code only through the repository matters.
  • npm ci: Installs exactly the versions in the package-lock.json file.
  • pm2 reload: Restarts the application without downtime. We cover running applications with pm2 and systemd in our How to host a Discord bot 24/7 guide.

In PHP projects, composer install --no-dev --optimize-autoloader replaces the npm lines, and Laravel also adds php artisan migrate --force and the cache commands. Make the script executable:

chmod +x ~/deploy.sh

Now all you need to do for a release is run ~/deploy.sh on the server.

Step 4: Automatic deploys with GitHub Actions

Going one step further, you can make the script run on its own on every push to the main branch.

  1. Create a separate SSH key for Actions (on your own computer):
    ssh-keygen -t ed25519 -f actions_key -N "" -C "github-actions"
    

    Add the contents of actions_key.pub to the ~/.ssh/authorized_keys file of the deploy user on the server.
  2. Define the secrets on GitHub. Under the repository's Settings → Secrets and variables → Actions, add DEPLOY_SSH_KEY (the contents of the actions_key file) and SERVER_HOST (your server's IP address).
  3. Create the workflow file: .github/workflows/deploy.yml
    name: Deploy
    on:
        push:
            branches: [main]
    
    jobs:
        deploy:
            runs-on: ubuntu-latest
            steps:
                - name: Deploy to server
                  env:
                      SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
                      HOST: ${{ secrets.SERVER_HOST }}
                  run: |
                      mkdir -p ~/.ssh
                      echo "$SSH_KEY" > ~/.ssh/id_ed25519
                      chmod 600 ~/.ssh/id_ed25519
                      ssh-keyscan -H "$HOST" >> ~/.ssh/known_hosts
                      ssh deploy@"$HOST" '~/deploy.sh'
    

From now on, on every push to the main branch, GitHub connects to your server and runs the deploy script. You can follow the result in the repository's Actions tab. If you like, you can add a step that runs your tests before the deploy step so that only code that passes the tests gets released.

Security tips

  • Don't put secrets in the repository. Add the .env file to .gitignore, create it by hand on the server and keep only a .env.example with empty values in the repository.
  • Limit the deploy user's permissions. If the user only needs sudo to restart the application service, allow a single command with sudo visudo:
    deploy ALL=(root) NOPASSWD: /bin/systemctl restart app
    
  • Use the Actions key only for deploying. If this key leaks, you just need to delete it from the authorized_keys file.
  • Protect SSH. Turn off password logins and install Fail2ban against automated attacks.

Rolling back

If you deployed a faulty version, go back to the previous commit on the server:

cd /var/www/app
git log --oneline -5
git reset --hard <previous-commit>
npm ci && npm run build && pm2 reload app

A cleaner way is to undo the faulty commit with git revert on your own computer and push it to the main branch; the automatic deploy process releases the fixed version by itself and the repository history stays consistent with the server. Make it a habit to back up the database before releases that include database changes (migrations); rolling back the code doesn't roll back the database.

Nodesty servers for deploying with Git

Nodesty AMD Ryzen 9 9950X VPS plans speed up build steps with high single-core speed and give your application low response times, while AMD EPYC 7763 VPS plans offer plenty of RAM for running multiple applications and databases on the same server. With full root access, you can install Git, Node.js, PHP and Docker however you like, and keep your projects safe with NVMe storage and free daily backups kept for 7 days.

Frequently Asked Questions

Why should I use Git instead of uploading files with FTP?

With Git, every release matches a commit; you always know which version is running on the server, and when something goes wrong you go back to the previous version with a single command. Uploading files by hand, on the other hand, leaves missing or old files on the server and makes it impossible to know who changed what.

What's the difference between a deploy key and a personal SSH key?

A deploy key is a key that can access only a single repository and usually has read-only permission. Even if the server is compromised, the attacker can only read that repository; they can't reach your account or all the repositories your personal key can access.

Should I add the .env file to Git?

No. Once passwords, API keys and database details get into the Git history, they're hard to remove from it completely. Add the .env file to .gitignore, create it by hand on the server and keep only a .env.example file with empty values in the repository.

Does the site go down during a deploy?

A simple git pull and restart can cause a few seconds of downtime. Tools like pm2 reload avoid downtime by restarting the application one process at a time. For projects where no downtime at all is acceptable, a common method is to set up each release in a separate folder and switch to the new version with a symbolic link once it's ready.

Can I use GitLab or my own Git server instead of GitHub?

Yes. The deploy key and git pull approach is the same with every Git provider; GitLab also has deploy keys in the repository settings. For automatic deploys, you can use GitLab CI/CD or a post-receive hook on your own Git server instead of GitHub Actions.

I deployed the wrong version. How do I go back?

In the project folder on the server, find the previous commit with git log, go back to that commit with git reset --hard, then rebuild and start the application. A cleaner way is to undo the faulty commit with git revert, push it to the main branch and let the normal deploy process run.

Nodesty

About the author

Nodesty Team · Infrastructure & Support

The Nodesty team runs our servers, network and DDoS protection day to day and answers customer support requests around the clock. These guides come from that work.

Related articles

Launch your server now.

Build, scale, and conquer without limits on Nodesty's premium infrastructure.

                                                                                                                                                                                                                            
                                                                                                                                                                                                                            
                                                                                                                                                                                                                            
                                                                                                                             ············································                                                   
                                                ··································································································..........···························                                     
                                       ················..........................................................................................................................··············                             
                                  ·············.................::::::::::::::::::::::::::::::::::::::::::::::....................:::::::::::::::::::::::::::::::::::::::................···········                        
                             ············................:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::............·········                     
                         ············.................:::::::::::::::::::::::::::::::::::::::::::::::::::------------::::::::::::::::::::::::::::::----------------:::::::::::::::::::::..........········                  
                    ············......................::::::::::::::::::::::::::::::::::::------------------------------------------------------------------------------------:::::::::::::.........········                
                ··········.............................................::::::::::::::::::-------------==========================================-----------------------------------:::::::::::........·······               
             ········..................:.....................................::::::::::--------=======+++++++++++++++++++++++++++++++++++++=========================------------------::::::::::.......·······              
           ······.........::::::::::::::::::::................................:::::::-----=====++++xxxxxx**************************xxxxxxxxx++++++++++++++===================------------::::::::.......·······             
          ·····.......:::::::::::::::::::::::::::.............................:::::----===+++xxx****%%%%&&&&&&#############&&&&&&&%%%%%*******xxxxxxxx++++++++++++++==============----------:::::::......······             
         ····......::::::::-------------:::::::::::..........................::::---===+++xx***%%%&&&####@@@@@@@@@@@@@@@@@@@@@@@@####&&&&&%%%%*******xxxxxxxxx+++++++++++++===========--------::::::......·····             
        ····.....:::::::--------------------:::::::::.......................::::---===++xxx**%%&&&###@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###&&&&%%%%%********xxxxxxxxxxx++++++++++++========------::::::.....······            
        ····.....:::::--------------------------::::::::....................::::---===++xx***%%&&&####@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###&&&&&%%%%%**********xxxxxxxxxxxxx+++++++++=======------:::::.....·····            
        ····....::::::-------=============---------:::::::::................:::::---===+++xx***%%%&&&#####@@@@@@@@@@@@@@@@@@@@@@@@@#####&&&&&%%%%%%*************xxxxxxxxxxxxxx+++++++++======-----:::::....·····            
         ····....:::::-------================---------:::::::::...............:::::----===++++xxx***%%%%&&&&&&#################&&&&&&%%%%%%%*******************xxxxxxxxxxxxxxxxx++++++++======----:::::....·····            
         ·····....:::::--------=================---------::::::::::..............:::::::-----====+++++xxxxxx**************************xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx++++++++=====-----::::....·····            
          ·····.....:::::--------=================----------:::::::::::.................:::::::::-------=============++++++++++++++++++++++++++++++++++++xxxxxxxxxxxxxxxxxxxxxxx++++++++======-----::::....·····            
            ·····.....:::::::----------===========-------------::::::::::::.........................::::::::::::::::::------------------=============+++++++++++++++++++++++++++++++++=======-----::::.....·····            
              ······......:::::::---------------------------------:::::::::::::........................................:::::::::::::::-----------============+++++++++++++++++++++========------:::::.....·····             
                 ······........:::::::::---------------------------:::::::::::::::::...........................................:::::::::::::------------=============================--------::::::.....······              
                    ·········.........:::::::::::::::-------------::::::::::::::::::::::::.......................................::::::::::::::::--------------------------------------::::::::......·······                
                          ··········............::::::::::::::::::::::::::::::::::::::::::::::::::................................::::::::::::::::::::::::::-------------:::::::::::::::.........········                   
                                  ············..................:::::::::::::::::::::::::::::::::::::::::::.........................:::::::::::::::::::::::::::::::::::::................···········                        
                                             ················.............................................................................................................·················                                 
                                                           ······················................................................................···························                                                
                                                                               ·········································································