Recommended Free Tools
Git deployment failures on a Linux VPS usually belong to one of two different problems: the Git server rejected your SSH credentials, or the VPS account running Git cannot read or write the local checkout. The exact wording tells you which path to investigate. Permission denied (publickey) is an SSH authentication or repository-access failure; a local Permission denied while updating files points to ownership, mode bits, directory traversal, mounts, or the account running the deployment.
Antihost offers Linux VPS plans with full root access, unlimited traffic on a 1 Gbps port and a 14-day free trial.
Capture the complete failing command, its full error, and the identity that ran it before changing permissions. Then follow the matching procedure below. Avoid repeatedly applying chmod or running Git as root: those actions often hide the real cause and can make later deployments fail under the normal deploy account.
As an Amazon Associate I earn from qualifying purchases.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →First classify the error
| What you see | What it normally means | Start here |
|---|---|---|
Permission denied (publickey) |
The remote SSH server rejected the key, user, host, or repository authorization. | Check the remote URL, connect as the git user, and inspect key selection with verbose SSH output. |
error: cannot open ...: Permission denied, unable to create file, or a failure writing .git |
The local deployment account cannot traverse a directory or write the checkout or Git metadata. | Inspect the account, ownership, mode bits, parent directories, mounts, and service context on the VPS. |
Permission denied (13) during an rsync-style transfer |
The remote destination, metadata operation, or transfer account lacks the required permission. | Check the destination owner and directory permissions, then test timestamp and metadata options. |
Do not treat these messages as interchangeable. Changing the VPS checkout’s ownership cannot fix a rejected SSH key, and replacing an SSH key cannot give a local service account write access to /var/www.
Record the failing identity and environment
A deployment may run from an interactive shell, a bare-repository hook, cron, a system service, or a CI runner. Each context can have a different user, home directory, PATH, SSH agent, and Git configuration. Run these checks in the same context that fails whenever possible:
#1 Best Overall
whoami
id
pwd
env | sort
git remote -v
git status
For a hook or service, write the output to a temporary log or invoke a small diagnostic script as that account. Record the absolute checkout path and the branch being deployed. A command that succeeds in your shell but fails in a hook is usually an identity or environment difference, not a random Git problem.
Fix remote SSH authentication and repository access
Verify the remote URL and required user
From the deployment checkout, inspect the configured remote:
git remote -v
An SSH remote normally uses the provider’s host with the git user, for example git@host:owner/repository.git. GitHub documentation says connections should use the git account, not your personal username. A remote that points to the wrong host, an old repository path, or an HTTPS URL while you are troubleshooting SSH can send you down the wrong branch of the diagnosis.
Test the same host outside Git
Use verbose SSH output against the host shown by git remote -v:
ssh -vT [email protected]
Replace the hostname with your Git provider when it is not GitHub. The verbose trace shows the configuration files read, identities offered, and the point at which the server rejects the connection. A successful authentication test does not prove that the repository path is correct, but a Permission denied (publickey) result confirms that the key or account authorization must be fixed before Git can fetch.
Make non-default keys explicit
If the deployment uses a key other than the default identity, test that exact file:
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #2
ssh -i ~/.ssh/KEY-FILE -vT [email protected]
For a repeatable deployment, put the intended host, user, and IdentityFile in the deploy account’s SSH configuration rather than relying on an interactive agent. Check that the private key exists for that account and that the corresponding public key is attached to the intended repository or account. Do not assume a key loaded in your personal shell is available to a hook, cron job, or CI session.
Check key file ownership and permissions
The deploy account must be able to read its private key, while other users should not be able to read it. Inspect the key directory and files:
ls -ld ~/.ssh
ls -l ~/.ssh
stat ~/.ssh/KEY-FILE
Correct ownership first. Then use restrictive modes appropriate to your distribution, commonly a private key readable only by its owner and an SSH directory accessible only by that owner. If SSH reports that a key is too open, follow the mode it requests rather than broadening access.
Confirm the key’s scope
A deploy key is tied to one repository. GitHub documents that deploy keys are read-only by default, may be granted write access, and do not expire automatically. Therefore, a key that works for one repository can still be rejected when the remote points to another repository. Verify both the repository association and the required operation: a read-only key can clone and pull but cannot push.
For deployments spanning multiple repositories or requiring centrally managed, fine-grained permissions, GitHub recommends GitHub Apps as a more controlled alternative. Whichever credential type you choose, grant only the operations and repositories the deployment actually needs, and define a rotation or revocation procedure.
Do not mix sudo git with normal-user Git
GitHub explicitly advises against using sudo or other elevated privileges with Git. Running one command as root changes more than file ownership: Git may read /root/.ssh, use a different SSH agent and known-hosts file, load a different Git configuration, and create root-owned files in the checkout.
Choose a dedicated non-root deployment account and run the complete pull, checkout, build, and hook process as that account. If a narrowly controlled privileged action is unavoidable, separate it from Git and make its identity and environment explicit. Check the resulting file ownership immediately so the next deployment account can still update the working tree and .git.
Inspect local VPS permissions before changing them
Identify every path Git must traverse
Git needs execute (traverse) permission on every parent directory, read access to the repository, and write access to the working tree and Git metadata for operations such as pull, merge, checkout, and fetch. Inspect the project, its .git directory, and each parent:
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →stat /path/to/project
stat /path/to/project/.git
namei -l /path/to/project
find /path/to/project -maxdepth 2 -printf '%M %u:%g %pn' | sort
namei -l is especially useful when the final directory looks writable but an upper-level directory blocks traversal. Check whether the checkout is a separate mount, a read-only filesystem, a network share, or a container volume with its own UID/GID mapping:
findmnt -T /path/to/project
mount | grep -F /path/to/project
df -h /path/to/project
A full disk, read-only mount, ACL, or security policy can produce a permission-style failure even when the basic mode bits appear correct.
Correct ownership for the account that deploys
Determine the actual account with whoami or id, then compare it with the owner and group shown by stat. If a previous root deployment created the checkout, transfer ownership to the intended deploy account rather than making the tree world-writable:
sudo chown -R deploy:deploy /path/to/project
Substitute your real account and group, and review the path carefully before running a recursive command. If a web server only needs to serve the generated files, give that service read or group access instead of granting it write access to the repository. Keep .git writable only by the process that must update it.
Rank #4
Check ACLs and special restrictions
Traditional owner/group/mode output is not the whole permission model. If the modes look right but access still fails, inspect extended ACLs and attributes:
getfacl -p /path/to/project
lsattr -d /path/to/project /path/to/project/.git
Remove or adjust an ACL only when you understand which service needs access. An immutable attribute, a restrictive parent ACL, or a security profile can override the permissions you see in a simple directory listing.
Make a push-to-deploy hook reliable
A common design receives a push into a bare repository and runs a post-receive hook to update a working tree. Pro Git documents server-side repositories, SSH keys, authorized_keys, and executable hooks; a typical hook invokes:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
git --work-tree=/path/to/site --git-dir=/path/to/bare.git checkout -f main
Install and execute the hook as the deploy account
- Place the hook in the bare repository’s
hooks/post-receivefile. - Use absolute paths for the bare repository, work tree, build tools, and restart commands.
- Set a safe
PATHbecause hooks do not necessarily inherit your login shell environment. - Make the hook executable with
chmod +x /path/to/bare.git/hooks/post-receive. - Ensure the hook’s owner is the account receiving the push and deploying the files.
- Log the received branch, commit, command exit status, and deployment output to a location that account can write.
Keep the target branch explicit. If the hook checks out main but the push updates another branch, the hook can appear to run successfully while deploying nothing. Ensure application restart or build commands have the required permissions and cannot leave root-owned artifacts in the next release.
Verify the result after each test push
After a controlled push, check the latest deployed commit and inspect the hook log:
git --work-tree=/path/to/site --git-dir=/path/to/bare.git log -1 --oneline
stat /path/to/site
sudo tail -n 100 /var/log/your-deploy.log
Use the actual log path configured for your system. If the commit changed but generated assets or a service restart failed, the Git checkout permission is fixed and the remaining problem is in the build or runtime step.
Best Value
Fix GitHub Actions, CI, and rsync deployments
For a CI runner connecting to the VPS, the private key must be stored in the runner’s secret configuration, and the matching public key must be present in the target VPS user’s authorized_keys. The runner’s account is not your local account, so test with the same key and destination user that the workflow uses.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsValidate the SSH destination
ssh -i /path/to/ci-key -v [email protected]
Confirm the host key is handled deliberately, the destination username is correct, and the key’s file permissions satisfy the SSH client. On the VPS, inspect:
id deploy
stat /home/deploy/.ssh/authorized_keys
namei -l /home/deploy/.ssh/authorized_keys
Make sure the home directory, .ssh directory, and authorized_keys are owned by the target account and not writable by unrelated users.
Diagnose rsync permission errors
When rsync reports Permission denied (13), check the destination directory owner, group, and traversal rights first. Then determine whether rsync is failing while creating a file, deleting an old file, preserving timestamps, or changing another metadata field. If the filesystem rejects timestamp updates, test the transfer with options such as --no-times where that trade-off is acceptable. Do not permanently suppress metadata errors until you know which metadata the application relies on.
Choose a deployment design that limits permission failures
| Design | Credential scope | Process identity | Permission and recovery considerations |
|---|---|---|---|
| Manual pull on the VPS | Usually one repository credential for the deploy account. | An operator’s shell or a dedicated deploy user. | Simple to inspect, but easy to mix users or leave root-owned files; document the exact account and remote. |
| Bare repository with a hook | SSH access to the receiving account and repository. | The account owning the bare repository and hook. | Fast push-to-deploy flow; requires executable hooks, absolute paths, explicit branch handling, and logs. |
| CI runner over SSH or rsync | A runner secret plus the VPS user’s authorized key. | The remote user specified by the workflow. | Centralized logs and repeatability; destination ownership and metadata options must be tested. |
| Staged releases with a switch | Credential scope can be limited to the deployment repository. | A dedicated account writes release directories and changes the active pointer. | Separates immutable releases from the live web root and makes rollback easier, but requires careful ownership of the release and switch paths. |
| Multi-repository integration | Repository-scoped deploy keys may become numerous. | A service account or application identity. | GitHub Apps offer more managed, fine-grained access than piling write-capable keys onto one server. |
Evaluate a design by credential scope, rotation and expiry options, least-privilege controls, dedicated-user execution, rollback and logging, and whether it writes directly into the live web root or stages a release first. The most convenient design is not automatically the safest if every step runs as root or every repository shares one unrestricted key.
Common fixes that make the problem worse
- Using
chmod -R 777: this hides ownership mistakes and lets every local user modify code or deployment credentials. Correct the owner, group, ACL, or mount instead. - Running one successful pull with
sudo: it can create root-owned files and prove only that root has access, not that deployment is configured correctly. - Copying a personal private key to the server: this expands the blast radius and makes rotation difficult. Use a repository-scoped deploy key or managed application identity.
- Changing SSH keys when the error is local: a failure writing
.git/index.lockor a working-tree file is on the VPS, not at the Git provider. - Fixing only the final directory: missing traverse permission on a parent directory still blocks access.
- Assuming an interactive shell equals a hook or CI job: those contexts may have different users, home directories, agents, and
PATHvalues.
A repeatable end-to-end repair procedure
- Save the evidence. Copy the full error, command, timestamp, branch, destination path, and output of
whoamiandid. - Classify the failure. Route
Permission denied (publickey)to SSH diagnostics; route local file or.giterrors to filesystem diagnostics. - Confirm the remote. Run
git remote -v, then test the same host withssh -vT git@hostand the intended key. - Confirm credential scope. Check that the key belongs to the expected account or repository and that it has read or write capability appropriate to the operation.
- Confirm the local identity. Run the failing operation as the dedicated deploy account, not root, and record its home directory and environment.
- Trace the path. Use
namei -l,stat,findmnt, and disk checks on the checkout,.git, and all parents. - Repair narrowly. Change ownership or group access for the intended account, fix an incorrect mount or ACL, and make hooks executable. Avoid world-writable modes.
- Retest the exact automation. Trigger the same hook, CI job, cron command, or rsync invocation that failed; do not substitute an interactive command.
- Verify the deployed commit and logs. Confirm the expected commit is present, generated files have the correct owner, and restart or build steps completed.
- Document the boundary. Record which account owns the repository, which credential reaches the Git provider, which paths are writable, and how to rotate or revoke access.
When to escalate beyond ordinary permissions
Escalate to system administration or hosting support when the path is on a read-only or failing filesystem, UID/GID mappings differ across containers, an ACL or security policy denies access despite correct ownership, or the SSH server rejects a key that is demonstrably offered and correctly associated. Preserve the verbose SSH trace and filesystem inspection output; those details identify whether the remaining control point is the Git provider, SSH daemon, operating system policy, or storage layer.
The Pro Git book’s server chapter is a useful reference for SSH server repositories, authorized_keys, and hooks, but it cannot replace checking the live VPS account, path, and logs. A deployment is fixed only when the same non-root identity used in production can authenticate to the intended repository and update the intended files without elevated privileges.
Quick Recap
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.




