DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
World desk4 min

How to Manage a Git Repository on a Linux VPS

A practical Linux VPS guide to bare Git repositories, SSH authentication, GitLab deploy keys, safe deployment hooks, atomic releases, rollback, and recovery testing.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a bare Git repository as the central remote, keep every checked-out copy in a separate working or release directory, and authenticate unattended operations with narrowly scoped SSH keys. A safe VPS setup separates the repository, deployment releases, live web root, logs, and service accounts so a failed push cannot overwrite the running site or destroy the previous release.

You can either host the bare remote on the VPS itself or keep the source of truth on GitLab and let the VPS pull with a read-only deploy key. The right choice depends on whether you prioritize local control or hosted collaboration and review.

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 layout before installing anything

Git distinguishes between a repository and a working tree. A bare repository contains Git’s objects, references, and configuration but no checked-out files. It is therefore suitable for receiving pushes from several developers. A normal clone has a working tree and is the copy you inspect, build, test, or deploy.

Do not use the bare repository directory as a website document root. A push changes repository data, not a validated production release. Keep these locations separate:

#1 Best Overall
  • Central remote: for example, /srv/git/project.git, owned by a dedicated git service account.
  • Release directories: versioned, non-public paths such as /srv/releases/project-<commit>.
  • Live path: a stable symlink such as /srv/www/project-current that points to the approved release.
  • Logs and service files: outside the repository and writable only by the processes that need them.

For a self-hosted workflow, developers push to an SSH URL such as [email protected]:/srv/git/project.git. For a hosted-source workflow, the VPS clones from GitLab and uses a project deploy key to fetch a selected commit.

Self-host a bare remote on the VPS

Install Git and OpenSSH

Install the distribution packages for Git and an SSH server, then record their versions. Package names and service commands differ between distributions, so use the package manager and service manager provided by your Linux release. Recording versions makes a later rebuild reproducible.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
# Debian or Ubuntu example
sudo apt update
sudo apt install git openssh-server
git --version
ssh -V

Confirm that the firewall permits SSH from the addresses that need repository access. Do not expose a public web directory containing the repository itself.

Create a restricted service account and paths

Create a system account used only for Git transport. Give it no interactive shell unless an administrator has a specific reason to allow one. The account needs ownership of the bare repository, not ownership of your application process or web-server configuration.

sudo useradd --system --home /srv/git --shell /usr/sbin/nologin git
sudo install -d -o git -g git -m 0750 /srv/git
sudo install -d -o git -g git -m 0750 /srv/releases

The exact nologin path can differ by distribution. Check it before setting the account. If you later need administrative access, use a separate administrator account and elevate deliberately rather than turning the Git transport account into a general-purpose login.

Initialize the bare repository

Initialize the central repository as the git user and keep its permissions private to that account and its group.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
sudo install -d -o git -g git -m 0750 /srv/git/project.git
sudo -u git git init --bare /srv/git/project.git
sudo chown -R git:git /srv/git/project.git
sudo chmod -R go-rwx /srv/git/project.git

A bare repository has no working tree, so commands that assume files are checked out—such as editing application files directly inside /srv/git/project.git—are the wrong deployment method. Inspect it with Git commands or clone it elsewhere.

Set up SSH authentication safely

Create and protect a key pair

SSH uses two keys: a public key and a private key. Generate the pair on the developer or automation host, not on the VPS when the VPS is only receiving pushes. Prefer an ED25519 key when the installed OpenSSH versions support it.

ssh-keygen -t ed25519 -C "project-deploy"

Store the private key with restrictive permissions, protect it with a passphrase for human use, and never commit it to the repository or place it in shell history. Copy only the public key to the account that needs access. For a human developer, that normally means the git account’s authorized_keys file. For automation, use a separate key so it can be revoked without affecting people.

sudo install -d -o git -g git -m 0700 /srv/git/.ssh
sudo install -o git -g git -m 0600 developer_key.pub /srv/git/.ssh/authorized_keys

When you administer authorized_keys, consider restrictions appropriate to the workflow, such as disabling agent forwarding, X11 forwarding, and port forwarding. A forced command can restrict a key to Git’s SSH command handling, but test it carefully because an incorrect restriction can prevent legitimate Git operations.

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

Verify the VPS host key before the first connection

Host-key verification protects against connecting to an impostor server. Obtain the VPS host-key fingerprint through an independent channel—such as the provider console or a trusted administrator—and compare it with the key presented by SSH. Do not treat an unverified ssh-keyscan result as proof of identity.

# Run from a trusted administration path and compare out of band
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub

For unattended jobs, pin the expected host key in the job's SSH configuration or a controlled known_hosts file. If the host key changes unexpectedly, stop the job and investigate rather than accepting the replacement automatically.

Push the first branch and test from a clean clone

Add the remote and publish an intentional branch

From an existing local project, confirm the branch name and remote URL before pushing. The following example publishes main and records the upstream tracking branch.

git remote add origin [email protected]:/srv/git/project.git
git remote -v
git push -u origin main

If the project uses another default branch, substitute that name. On the VPS, inspect references as the git account or an administrator with read access:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
sudo -u git git --git-dir=/srv/git/project.git show-ref
sudo -u git git --git-dir=/srv/git/project.git symbolic-ref HEAD

Set the bare repository's symbolic HEAD to the branch your tooling expects if it does not already point there:

sudo -u git git --git-dir=/srv/git/project.git symbolic-ref HEAD refs/heads/main

Prove that the remote is usable

Test a clone into a new, empty directory. This catches incorrect permissions, an invalid default branch, and an SSH key that works for one command but not for normal Git transport.

rm -rf /tmp/project-clean-check
git clone [email protected]:/srv/git/project.git /tmp/project-clean-check
cd /tmp/project-clean-check
git status
git log --oneline -n 3

Also test a normal commit-and-push cycle from a non-production clone, then verify that the new object and reference appear on the VPS. Keep this test separate from any deployment hook until repository transport is known to work.

Use GitLab as the source and the VPS as a pull target

A hosted source is often preferable when the project needs merge requests, review, centralized permissions, or a collaboration history. In this design, GitLab remains the source of truth and the VPS holds only a deployment clone or fetched objects.

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.

Create a project-scoped deploy key

Deploy keys are intended for non-human interaction with a project. Generate a key on the VPS or in the deployment system, add only its public half to the project, and select read-only access for a pull-only deployment. Grant write permission only when the VPS genuinely must push back.

Use one key per environment or automation boundary. A production key should not also grant access to unrelated projects, and a staging key should be revocable without interrupting production.

Clone into a non-public release location

git clone [email protected]:group/project.git /srv/releases/project-<commit>

In real automation, replace <commit> with the verified commit identifier supplied by the deployment event. Fetch the intended ref, verify that the commit belongs to the expected project and branch, and deploy that immutable commit rather than whatever happens to be at a moving branch name when the job runs.

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

Configure an SSH host alias if several keys or host fingerprints are involved. The alias can select the correct identity file and known-hosts file without changing the repository URL used by your deployment script. Keep the private key in an agent or restricted secret store; do not write it into the release directory.

Deploy safely with a server-side hook

Decide whether a hook is appropriate

A hook is useful when an event on the VPS should trigger a deterministic action, such as receiving a push or completing a fetch. It is not a substitute for validation, logging, or a rollback plan. If your hosted provider already emits deployment events to a controlled runner, using that runner may provide clearer audit records than executing arbitrary work in a repository transport process.

GitLab documents executable server hooks placed in a configured hook directory. The hook must be executable and should do only the narrow work required for the event. Never execute commit messages, ref names, or filenames as shell code.

Validate the event and build a new release

For a self-hosted bare remote, a post-receive hook receives lines containing the old object ID, new object ID, and reference name. Read those fields safely, accept only the expected branch, and reject malformed input. A robust deployment flow is:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Read the received ref and ignore or reject branches that are not deployment sources.
  2. Resolve the new object ID and verify that it is a commit in the repository.
  3. Create a uniquely named temporary release directory outside the bare repository.
  4. Check out or archive the exact commit into that directory without exposing it publicly.
  5. Install dependencies and run application tests, migrations, asset builds, or configuration checks as the deployment policy requires.
  6. Stop on any failed command and leave the current live release untouched.
  7. Prepare a temporary symlink to the validated release, then replace the live symlink in one filesystem operation.
  8. Record the commit ID, release path, start time, end time, and exit status in a deployment log.

Use a dedicated deployment user with only the permissions needed for releases and the live-link directory. If the application needs a privileged service restart, grant a narrowly scoped service-manager rule rather than unrestricted administrative access.

Switch releases atomically and retain rollback capacity

Keep at least one known-good release after a successful deployment. A typical switch creates a temporary link and renames it over the stable link:

ln -s /srv/releases/project-NEWCOMMIT /srv/www/project-current.next
mv -Tf /srv/www/project-current.next /srv/www/project-current

The exact rename options and filesystem behavior should be tested on your distribution and filesystem. Ensure the web server or application service reads the stable path on each request or is reloaded in a controlled way after the switch. To roll back, point the stable link to the previous validated release, reload only what is necessary, and record the rollback event.

Do not delete the previous release until health checks, logs, and user-facing monitoring show that the new one is operating correctly. Keep failed releases for diagnosis only if they do not contain secrets and your retention policy allows it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Separate permissions, secrets, and runtime users

  • The git account should own the bare repository and receive Git transport connections, but it should not own the live web process.
  • The deployment account should write releases and, if required, update the live symlink; it should not have broad access to the operating system.
  • The web-server or application user should read the selected release and write only designated runtime directories such as caches or uploads.
  • Configuration secrets should be injected through the service manager or a restricted secret store, not committed to Git or copied into a public release.
  • Repository, release, log, and web-root directories should have explicit ownership and mode settings. Check them after every provisioning change.

Keep private keys out of command-line arguments and shell history. Use an SSH agent for interactive work or a protected secret mechanism for automation, and revoke a key immediately when its host, user, or purpose changes.

Back up the repository and prove that recovery works

A repository backup is more than a copy of the current working tree. Preserve the bare repository's objects and references, hook scripts, SSH and service configuration needed to reconnect it, deployment metadata, and the information required to restore secrets safely. Store backups on infrastructure independent of the VPS so a disk loss or account compromise does not remove both copies.

Choose retention and frequency according to your recovery-point and recovery-time objectives. The schedule is an operational decision; document it rather than assuming that one recent archive is sufficient.

Perform a restore drill on a separate path or host:

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.
  1. Restore the bare repository and verify that expected refs and recent commits are present.
  2. Recreate the service account, directory ownership, SSH host identity, and authorized access without copying unnecessary privileges.
  3. Clone from the restored repository into a clean directory.
  4. Reinstall the deployment hook and service configuration from the backup set.
  5. Build a release and verify that the application starts with the restored metadata and separately managed secrets.
  6. Record the elapsed recovery time, missing prerequisites, and any data that could not be restored.

A backup that has never completed a restore test is an assumption, not a demonstrated recovery capability.

Self-hosted remote versus hosted source

Decision area Self-hosted bare remote Hosted source with VPS pull
Repository location The central repository is on the VPS under a dedicated account. The source of truth remains on GitLab; the VPS keeps a clone or fetched release.
Collaboration and review You must provide your own review, access, and audit workflow. Hosted project features can centralize review and permissions.
Operational burden You operate SSH access, storage, backups, hooks, and repository availability. You still operate the VPS deployment, but the provider operates the central service.
Recovery responsibility You are responsible for off-host repository backups and restoration. You must back up deployment state and account for provider access and key revocation.
Authentication scope Developer keys grant access to the VPS repository and should be restricted per user or automation job. Project deploy keys can be read-only and scoped to the hosted project.
Event auditing SSH logs, Git references, hook logs, and deployment records are yours to collect. Provider events supplement VPS logs, but you still need local deployment and failure records.

Choose the self-hosted model when minimizing external dependencies and controlling repository location matter most. Choose the hosted-source model when collaboration and centralized review outweigh the additional provider and credential dependency.

Common failures and how to diagnose them

Symptom Likely cause Checks and remedy
Permission denied (publickey) The wrong private key is being offered, the public key is absent, or file modes are too permissive. Run SSH in verbose mode, confirm the identity file and host alias, inspect authorized_keys ownership and mode, and verify the key belongs to the intended account.
Host-key warning or changed fingerprint The VPS was rebuilt, the DNS target changed, or an interception attempt is possible. Stop unattended jobs, compare the new fingerprint through an independent channel, then update the pinned key only after the change is explained.
Clone succeeds but the default branch is empty or wrong The bare repository's HEAD points to a branch that does not exist. Inspect symbolic-ref HEAD, set it to the actual default branch, and repeat a clean clone.
Push succeeds but the site does not change No deployment hook ran, the hook rejected the ref, or the live symlink was not switched. Check hook executable status, configured hook location, event logs, accepted branch name, release creation, and the live-link target.
Deployment leaves a partially updated site Files were written directly into the live directory. Change the hook to build a separate release, run checks there, and switch the live link only after success.
Rollback fails The previous release was deleted, its dependencies were not retained, or the service still references the failed path. Keep a tested previous release, make runtime dependencies reproducible, repoint the stable link, and reload the affected service.
Hook works manually but not from Git The hook has a different environment, working directory, PATH, user, or permissions. Use absolute paths, set the required environment explicitly, log command output and exit status, and test as the actual hook user.

Operational checklist

  • Use a bare repository for a central self-hosted remote; use normal clones only as working or release copies.
  • Keep the repository outside the public web root and separate from live files.
  • Use a dedicated service account and least-privilege directory modes.
  • Prefer ED25519 keys when supported, protect private keys, and install only public keys on the destination.
  • Verify and pin the VPS host key before unattended pushes, clones, or pulls.
  • Use read-only project deploy keys for pull-only hosted-source deployments.
  • Accept only expected refs in hooks and treat all event data as untrusted input.
  • Build and test a new release before changing the live symlink.
  • Retain a previous release, monitor hook exit status, and document rollback.
  • Back up repository objects, refs, hooks, service files, and deployment metadata off-host.
  • Run a restoration drill and measure it against a stated recovery objective.

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 from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

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.