Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
VGSources
Blog

How to Manage a Git Repository on a Linux VPS: Bare Remotes, SSH and Safe Deployments

A practical Linux VPS guide to bare Git repositories, SSH authentication, GitLab deploy keys, safe release directories, deployment hooks, rollback, and recovery testing.
Length10 min Posted Quest giverVGSources Team

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The reliable pattern is to keep a bare Git repository as the central remote, use SSH keys for access, and deploy each approved commit into a separate release directory. Never use the bare repository or a developer clone as your public web root. A Linux VPS can host that bare remote itself, or it can pull from a hosted Git provider such as GitLab with a narrowly scoped deploy key.

This guide covers both designs, the exact setup sequence, secure SSH authentication, push-triggered deployment, rollback, backups, and the failure modes that most often turn a simple Git server into an outage.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose the repository architecture first

Your choice determines who owns collaboration, authentication, backups, and deployment events. There are two sound arrangements.

Self-hosted bare remote

The VPS stores the canonical repository at a path such as /srv/git/project.git. Developers push to an SSH URL such as [email protected]:/srv/git/project.git. The repository is owned by a dedicated git service account and has no checked-out files.

This is the traditional Git server model. It minimizes external dependencies and gives you complete control over repository location and hooks, but you are responsible for user access, auditing, backups, upgrades, and recovery.

Hosted source with the VPS as a deployment target

GitLab remains the source of truth for collaboration, merge requests, and review. The VPS keeps one or more release directories and pulls a selected commit over SSH. A project deploy key installed on the VPS should be read-only when the server only needs to fetch code. Write access is justified only when the VPS must receive pushes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

This arrangement centralizes collaboration and can reduce the amount of Git administration on the VPS, but it adds dependence on the provider and on the deploy key’s security.

Decision Self-hosted bare remote Hosted source plus VPS pull
Repository location On the VPS under a dedicated service account Hosted provider; VPS stores working releases
Collaboration and review You must provide your own workflow and access controls Provider supplies project collaboration and review features
Operational burden Higher: permissions, hooks, updates, and backups are yours Lower for the Git service, but deployment credentials still need management
Authentication scope SSH access to your VPS account and repository Project-scoped deploy key, preferably read-only
Recovery responsibility You must restore repository objects, refs, and server configuration You still restore releases and configuration; the hosted repository may remain available
Deployment audit Hook logs and server logs are your responsibility Provider events can be combined with VPS pull and deployment logs

Prepare the Linux VPS

Install the distribution’s Git and OpenSSH packages, then record their versions. Version records make a rebuild repeatable and help explain differences after an operating-system upgrade. Package names and service-management commands vary between distributions, so use the package manager and service manager supplied by your Linux release.

Create a dedicated account

Do not let developers push into a personal administrator account. Create a service account named git (or another clearly identified name), give it ownership of the repository directory, and disable interactive login unless administration genuinely requires it. A typical system-account command is:

sudo useradd --system --home /srv/git --shell /usr/sbin/nologin git

Check the correct shell path and account conventions on your distribution. Keep the Git directory outside the public web root, for example under /srv/git, and use restrictive directory permissions. The web server, deployment process, and Git service should not all run as the same user.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Separate repository, releases, and live files

Use distinct paths such as:

  • /srv/git/project.git for the bare repository.
  • /srv/releases/project-<commit> for immutable or mostly immutable checked-out releases.
  • /srv/www/project for a symlink or service configuration pointing at the active release.
  • A separate log location for hook and application output.

Keeping these paths separate prevents a Git object database, temporary checkout, or half-completed deployment from becoming publicly downloadable.

Set up a self-hosted bare repository

  1. Install Git and OpenSSH and create the dedicated account and parent directory.

  2. Initialize the bare repository as the git user:

    sudo install -d -o git -g git -m 0750 /srv/git/project.git
    sudo -u git git init --bare /srv/git/project.git
  3. Inspect ownership and permissions. The service account must be able to read and write repository objects, refs, and hooks; unrelated users should not be able to alter them.

  4. Install the developer’s public SSH key in the authorized-key file for the account that receives Git connections. Copy only the public key; the private key remains on the developer or CI host.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  5. From a local clone, add the remote and push the intended branch:

    git remote add origin [email protected]:/srv/git/project.git
    git push -u origin main
  6. Confirm the remote URL with git remote -v, verify that the expected branch exists on the server, and perform a test clone into a clean directory. A clean clone catches missing permissions, incorrect default-branch assumptions, and accidental dependencies on files present only in a developer’s working tree.

A bare repository deliberately has no working tree. It is therefore suitable for receiving pushes but not for editing files or serving a website. To inspect code, clone it into a separate working directory.

Configure SSH without weakening authentication

Create and protect keys

SSH uses two keys: a public key and a private key. Generate an ED25519 pair when the installed OpenSSH versions support it:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
ssh-keygen -t ed25519 -C "deploy-or-developer-key"

Protect the private-key file with a passphrase and restrictive filesystem permissions. Do not commit it to the repository, paste it into shell history, or place it in a world-readable automation directory. For unattended jobs, load it into a constrained SSH agent or store it in a secret manager that your deployment system controls.

Install only the corresponding .pub file on the receiving account. For a self-hosted remote, that is the VPS account used in the SSH URL. For a pull deployment, it is the public half of the deploy key registered with the hosted project.

Verify the VPS host key

Before the first push or clone, verify the VPS host fingerprint through a channel you trust. An unattended job should use a pinned, reviewed host-key entry rather than accepting whatever key is presented on first contact. A changed fingerprint can indicate a rebuild, a DNS mistake, or an interception attempt; stop and investigate instead of deleting the old entry automatically.

Test authentication separately from Git when possible. The test should confirm that the intended account is reached, that interactive shell access is disabled where appropriate, and that the key cannot access unrelated directories.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use GitLab deploy keys for pull-based deployment

When GitLab is the canonical repository, create a project deploy key on the VPS and use it for the deployment account. Deploy keys are intended for non-human interaction with GitLab and can be scoped to a project. Select read-only access for a server that only clones or fetches. Grant write permission only when the server has a documented requirement to push changes.

  1. Create a dedicated deployment key pair on the VPS or in your automation environment. Keep the private half readable only by the deployment user or secret-management system.

  2. Verify GitLab’s SSH host key before the first unattended fetch, using the same pinned-fingerprint process used for the VPS.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  3. Add the public key to the specific GitLab project as a deploy key. Avoid a personal account key when a project-scoped key is sufficient.

  4. Clone into a non-public release directory, not into the live web root:

    git clone [email protected]:group/project.git /srv/releases/project-<commit>
  5. Record the exact commit deployed. A release directory named with the commit identifier makes rollback and incident review unambiguous.

For recurring deployments, fetch or clone the selected commit, run application checks, and activate it only after those checks pass. Do not give the deployment key broader repository or shell privileges than the workflow needs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Build a safe push-to-deploy flow

A deployment hook should never turn an arbitrary push into an in-place edit of the live directory. The safe sequence is: validate the event, create a new release, run checks, switch the active pointer, and preserve the previous release.

Validate the hook event

For a self-hosted bare remote, a post-receive hook can read the old revision, new revision, and ref name from standard input. Accept only the branch you intend to deploy, usually refs/heads/main. Validate that the new revision is a full hexadecimal commit ID before using it in a path. Never evaluate commit messages, branch names, or filenames as shell code.

Check out into a new release

The following is a minimal pattern to adapt to your application. It deliberately keeps the repository and live directory separate:

#!/bin/sh
set -eu

repo=/srv/git/project.git
base=/srv/releases/project
live=/srv/www/project

read oldrev newrev refname
[ "$refname" = "refs/heads/main" ] || exit 0
case "$newrev" in
  ''|*[!0-9a-f]*) exit 1 ;;
esac

release="$base/$newrev"
tmp="$base/.tmp-$newrev"
rm -rf "$tmp"
mkdir -p "$tmp"
git --git-dir="$repo" --work-tree="$tmp" checkout -f "$newrev"

# Run project-specific dependency installation, tests, and asset builds here.
# Exit nonzero on failure; do not change the live pointer.

ln -s "$release" "$base/.current.new"
mv -Tf "$base/.current.new" "$live"
rm -rf "$tmp"

In a real hook, set the release variable to the directory you actually populated, or move the completed temporary directory into its final commit-named path before creating the symlink. The build commands depend on the application and should run as a least-privileged deployment user. Do not run package installation or application build steps as root unless there is no safer design.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Make activation atomic and reversible

Keep the live path as a symlink or service configuration that points to a completed release. Create a temporary symlink, then replace the live pointer with a filesystem rename such as mv -Tf. Readers see either the old complete release or the new complete release, not a directory half-filled by a checkout.

Retain at least one previous known-good release. If health checks fail after activation, point the live symlink back to that release and investigate the failed commit. A failed hook must leave the previous release serving traffic; it should log the error and return a nonzero status rather than deleting the working version.

Place and permission hooks correctly

Hook files must be executable. In a self-hosted bare repository, install them in the repository’s hooks directory and make them owned by the account that runs the receive operation. If a hosted Git service or GitLab server hook is involved, use that platform’s configured hook directory and event rules; an executable file in an arbitrary directory will not run. Monitor hook exit status and preserve logs long enough to diagnose failed deployments.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Back up Git and prove that recovery works

A repository backup is more than a copy of a directory. Preserve the bare repository’s objects and refs, hook scripts, service-account configuration, deployment scripts, web or service-manager configuration, and the metadata needed to retrieve deployment secrets. Store the backup on infrastructure independent of the VPS so a disk failure or compromised host cannot destroy both copies.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Define recovery objectives

Write down how much recent history you can afford to lose (recovery point objective) and how quickly service must return (recovery time objective). Retention intervals are an operator decision; choose them according to the value and change rate of the project rather than assuming a universal schedule.

Run a restoration drill

  1. Restore the bare repository and configuration to a separate path or isolated host.

  2. Verify refs and clone from the restored repository into a clean directory.

  3. Restore hooks and deployment metadata, then perform a deployment into a test release directory.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  4. Confirm that the application starts, the live-pointer switch works, and the previous release can be selected.

  5. Record elapsed time, missing credentials, and manual steps. A backup is not considered usable until this drill meets the stated recovery objective.

Security and operations checklist

  • Use a dedicated service and deployment account with least-privilege directory permissions.
  • Keep the bare repository, release directories, logs, and live web root separate.
  • Protect private SSH keys; place only public keys on authorized accounts.
  • Pin and verify host keys before unattended operations.
  • Prefer read-only project deploy keys for pull-only deployments.
  • Disable interactive login for service accounts unless a documented administrative task requires it.
  • Validate hook refs and commit IDs; never execute untrusted filenames or commit messages.
  • Run tests and builds before changing the live pointer.
  • Keep at least one previous release and monitor hook exit status.
  • Back up repository objects, refs, hooks, service files, and deployment-secret metadata off-host.
  • Test restoration on a separate path before relying on the backup.

Troubleshoot the common failures

Symptom Likely cause What to check
Permission denied (publickey) Wrong key, wrong account, unreadable authorized-key file, or restrictive parent-directory permissions Confirm the SSH URL, key selected by the client, public-key contents, account ownership, and host-key entry.
Push is accepted but no deployment occurs Hook is missing, not executable, installed in the wrong hook directory, or filtering a different ref Check hook permissions, configured directory, branch name, and receive logs. Test with a known commit and inspect the hook’s exit status.
Deployment exposes a partial site Files were checked out directly into the live directory Switch to commit-named releases and an atomic symlink or service configuration update.
Hook fails after creating a release Dependency installation, tests, permissions, disk space, or application configuration failed Keep the old live pointer, read the hook log, inspect the new release, and fix the failing check before retrying.
Pull from GitLab fails unattended Deploy key is missing, has insufficient scope, private key is unavailable, or GitLab’s host key is unverified Confirm the project-scoped key, read-only setting, secret permissions, and pinned host fingerprint.
Restored repository cannot deploy Only Git objects were backed up; hooks, service files, or deployment metadata were omitted Restore the complete configuration set and repeat the restoration drill.

Choose the simplest design that meets your control requirements

Use a self-hosted bare remote when repository locality, independence from a provider, or custom receive hooks is the priority and you are prepared to operate access control and backups. Use a hosted source with a read-only VPS deploy key when collaboration and review belong in the provider and the VPS only needs to fetch approved code.

Whichever model you choose, keep the invariants: the central repository has no working tree, SSH private keys remain secret, deployment happens outside the live path, activation is atomic, failed releases do not erase the previous version, and recovery is tested rather than assumed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.