# GitHub Deploy Key: How to Set One Up, Use It and Fix It

> A GitHub deploy key is an SSH key that gives one server access to one repository. Learn how to add it, use several on one server and fix common errors.

A **GitHub deploy key** is an SSH key that lets one server read one GitHub repository. You keep the private half on the server and paste the public half into the repository under **Settings → Deploy keys**. It is read-only unless you allow write access, and it cannot be reused on a second repository.

That last rule is where most people get stuck. If you have ever added a key to a second repo and hit **"Key already in use"**, or watched a deploy fail with `Permission denied (publickey)`, this guide is for you. If you are new to Git-based deploys, our guide to **[one-click Git deploy](/one-click-git-deploy-ship-any-project-the-fastest-way/)** covers the bigger picture.

Below you will find what a deploy key is, how to create and add one, how to run several on one server, how to fix the common errors, and what xCloud does with deploy keys for you.

![How a GitHub deploy key works: the private key stays on your server, the public key sits in the repository's Deploy keys](/_landing/blog/github-deploy-key-image-2.png)

## TL;DR

Short on time? Here is the whole guide in a few lines:

- A deploy key is an SSH key attached to **one repository**, not to a person.
- Create it on the server with `ssh-keygen -t ed25519`, then paste the `.pub` file into **Settings → Deploy keys → Add deploy key**.
- Leave **Allow write access** off unless the server must push.
- One key per repository. For several repos on one server, use one key each plus a `Host` alias in `~/.ssh/config`.
- Deploy keys never expire and cannot be edited. Remove them when the server goes away.
- On xCloud, a connected GitHub account with admin rights adds a read-only key for each site automatically. Without admin, a repository admin pastes the key xCloud shows and you click **Verify connection**.

## What Deploy Key Problems Look Like in Real Support Tickets

Here is the part other guides skip. Deploy key questions come up again and again in xCloud support conversations, and they repeat in three shapes:

1. **One key, many repositories.** A team reuses the same server key on a second private repository. GitHub refuses it, and the deploy stops at the clone step.
2. **No admin rights.** The person who connected GitHub can push code but is not a repository admin, so nobody can add the key automatically.
3. **Access errors read as branch errors.** The log says `Permission denied (publickey)` or "repository not found", and the user changes the branch. That never fixes an access problem.

Every section below maps to one of these three. Our **[Git deployment troubleshooting guide](/docs/troubleshooting-git-deployments-stuck-failed-or-not-triggering/)** covers the full list of log messages.

## What Is a GitHub Deploy Key?

GitHub's own definition: a deploy key is "an SSH key that grants access to a single repository." GitHub stores the public part on the repository instead of a personal account, and the private part stays on your server ([GitHub Docs, managing deploy keys](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/managing-deploy-keys)).

Think of it as a key cut for one door. Your personal SSH key is a master key for every repository your account can open. A deploy key opens one repository, and only from the machine that holds it.

**A few things worth knowing:**

- It is **read-only by default**. Write access is an option you tick.
- It is tied to the **repository, not a user**. If the person who added it leaves, the key keeps working.
- It has **no expiry date**.
- It is usually stored **without a passphrase**, so anyone who controls the server can use it.
- It **cannot be edited**. To change one, delete it and add a new one.

### Deploy key vs other ways to give a server access

| Method | Reach | Expires | Good for | Watch out for |
|---|---|---|---|---|
| **Deploy key** | One repository | Never | A server that pulls one repo | One key per repo; remove it by hand |
| **Personal SSH key** | Every repo your account can reach | Never | Your laptop | Far too much reach for a server |
| **Machine user** | Repos you add it to | Never | One key for many repos | A full GitHub account to manage |
| **GitHub App** | Repos you install it on | Tokens last 1 hour | Automation and hosting panels | Tokens must be re-issued by code |
| **HTTPS token** | Depends on the token | Optional | Scripts and CI | It is a password; protect it like one |

GitHub itself recommends a GitHub App for fine-grained control. Deploy keys remain the simplest option for a single server pulling a single repository.

## What You Will Need

- SSH access to the server that will pull the code (on xCloud, see **[how to generate and add SSH keys](/docs/how-to-generate-and-add-ssh-keys-in-xcloud/)**).
- **Admin access** to the GitHub repository. Only admins can see **Settings → Deploy keys**.
- Outbound access from the server to `github.com` on **port 22**.

## How to Add a GitHub Deploy Key, Step by Step

### Step 1: Create a key pair on the server

Log in to the server as the user that will run `git pull`, then create a key with no passphrase so deploys can run unattended:

```bash
ssh-keygen -t ed25519 -C "deploy@example.com" -f ~/.ssh/myrepo_deploy_key -N ""
```

This writes two files: `myrepo_deploy_key` (private, stays here) and `myrepo_deploy_key.pub` (public, goes to GitHub). Ed25519 is the type GitHub suggests; older systems can use `-t rsa -b 4096`.

### Step 2: Copy the public key

```bash
cat ~/.ssh/myrepo_deploy_key.pub
```

Copy the whole line, starting with `ssh-ed25519`. Never copy or share the file without `.pub`.

### Step 3: Add it to the repository

In GitHub, open the repository and go to **Settings → Deploy keys → Add deploy key**. Give it a title that names the server, paste the key, and leave **Allow write access** unticked. Click **Add key**.

A key with write access can push to the repository, so only tick it when the server really commits code back.

### Step 4: Test and clone

```bash
ssh -T -i ~/.ssh/myrepo_deploy_key git@github.com
git clone git@github.com:OWNER/REPO.git
```

The first command should greet you with the repository name. If the server has more than one key, add `-o IdentitiesOnly=yes` so SSH offers only this one.

### Adding the key with the API instead

For scripted setups, GitHub's REST API creates the same key with `POST /repos/{owner}/{repo}/keys`, sending `title`, `key` and `read_only: true` ([GitHub REST docs](https://docs.github.com/en/rest/deploy-keys/deploy-keys)). A common mistake is leaving out the `/repos/` part of the path, which returns a 404.

## How to Use Several Deploy Keys on One Server

GitHub is clear: "You can't reuse a deploy key for multiple repositories." So a server that pulls three private repositories needs three key pairs. The trick is telling Git which key to use for which repository.

![One server with several private repositories: one deploy key per repository, each with a Host alias in ~/.ssh/config](/_landing/blog/github-deploy-key-image-3.png)

Create one key per repository (Step 1, with a different file name each time), add each public key to its own repository, then add a `Host` alias for each in `~/.ssh/config`:

```
Host github.com-repo-a
    Hostname github.com
    IdentityFile ~/.ssh/repo-a_deploy_key
    IdentitiesOnly yes

Host github.com-repo-b
    Hostname github.com
    IdentityFile ~/.ssh/repo-b_deploy_key
    IdentitiesOnly yes
```

Now clone through the alias, not through `github.com`:

```bash
git clone git@github.com-repo-b:OWNER/repo-b.git
```

For an existing clone, point it at the alias with `git remote set-url origin git@github.com-repo-b:OWNER/repo-b.git`.

## Fixing Common GitHub Deploy Key Errors

| Error | What it means | Fix |
|---|---|---|
| **Key already in use** | This public key is already on another repository or a personal account | Create a new key pair for this repository |
| **Permission denied (publickey)** | GitHub accepted none of the keys the server offered | Check the key is listed in Deploy keys and the server uses the matching private key; use an alias or `IdentitiesOnly` |
| **Repository not found** | The key is valid but belongs to a different repository | Point the remote at the right alias or add the right key |
| **Host key verification failed** | The server has never trusted GitHub's host key | Run `ssh-keyscan github.com >> ~/.ssh/known_hosts` once; do not switch off host checking |
| **Connection timed out** | Port 22 is blocked on the way out | Open outbound port 22, or ask your network team |

To find where a key is already used, run `ssh -T -i ~/.ssh/myrepo_deploy_key git@github.com`. If the reply names a repository, the key is a deploy key on that repository ([GitHub's "key already in use" guide](https://docs.github.com/en/authentication/troubleshooting-ssh/error-key-already-in-use)).

The catch worth knowing on host keys: some forum fixes set `StrictHostKeyChecking no`. That works, and it also turns off the check that stops a fake GitHub from receiving your code. Add GitHub's key to `known_hosts` instead.

## How xCloud Handles GitHub Deploy Keys for You

Everything above is the manual path. When you deploy a Git project on xCloud, most of it happens for you. Here is what the dashboard does today, checked against xCloud's code and docs.

**Public repository:** no key at all. xCloud clones it over HTTPS.

**Private repository, GitHub connected with admin access:** when you connect GitHub through the xCloud GitHub App and the connected account is a repository admin, xCloud creates the key and adds it to the repository for you. It is added as **read-only**, titled `xCloud Deploy Key-` followed by a number, and the push-to-deploy webhook is set up at the same time.

**One key per site:** each site gets its own deploy key. xCloud never reuses a key that another site holds, and never uses the key that logs in to your server. That rule is how xCloud avoids the "one key, many repositories" problem from the support tickets above.

**No admin access:** xCloud cannot add the key itself and shows **A repository admin must add this deploy key** on the site's **Git** page, with the **SSH Public Key** to copy. Then:

1. Copy the **SSH Public Key**.
2. Click **Open repository deploy keys** (or in GitHub go to **Settings → Deploy keys → Add deploy key**) and paste it, read-only. A repository admin has to do this step.
3. Back in xCloud, click **Verify connection**, then deploy.

If no key is shown yet, click **Generate deploy key**.

**SSH URL without a connected account:** paste a `git@github.com:…` address and xCloud shows a deploy key to add, the same way.

**Deleting a site:** xCloud removes the deploy key and webhook it added to the repository.

👉 Step by step with screenshots: **[How to connect GitHub and deploy Git projects with xCloud](/docs/how-to-integrate-a-git-provider-with-xcloud/)**. For new projects, see **[deploy Git projects in one click](/docs/deploy-git-projects-in-one-click-with-xcloud/)**, and for existing sites, **[set up Git on an existing WordPress or PHP site](/docs/set-up-git-integration-for-existing-wordpress-or-php-site/)**.

### What xCloud does not do

Honest limits, so you can plan around them:

- **GitHub only for automatic keys.** The automatic setup uses the xCloud GitHub App. Other Git hosts use the copy-and-paste path.
- **No admin, no automatic key.** xCloud cannot add a key to a repository your connected account does not administer. Someone with admin rights has to paste it once.
- **Keys you added by hand stay.** When you delete a site, xCloud removes only the key it added itself. Remove a hand-added key from **Settings → Deploy keys** yourself.
- **No repository swap on the Git page.** You cannot point an existing site at a different repository from the **Git** page. To use another repository, create a new site.

To undo xCloud's access to a repository, delete the deploy key under **Settings → Deploy keys** in GitHub. The next deploy will then fail until you add a key again and click **Verify connection**.

## Ship From a Private Repo Without the Key Juggling

If you run Laravel, Node.js, PHP or Docker apps from private repositories, the key-per-repo work above repeats for every site. xCloud's **[one-click Git deployment](/one-click-git-deployment/)** handles the key, the webhook and the first deploy from one screen. For containers, the **[Docker hosting](/docker-hosting/)** page and the guide to **[deploy a custom Docker application from a Git repository](/docs/how-to-deploy-custom-docker-application-from-git-repository/)** show the same flow.

The trade-off is plain: the manual path costs nothing but your time on every new server. The managed path costs a plan from the **[pricing page](/pricing/)** and saves that time.

## Give Every Server Its Own Key Starting Today

A GitHub deploy key is the smallest access you can give a server: one repository, read-only, nothing else. Create one key per repository, keep write access off, give each an SSH alias, and remove keys when servers retire.

This week, list the keys under **Settings → Deploy keys** on your busiest repository. Delete any that name a server you no longer run. Then swap any personal SSH key on a production server for a deploy key. If you deploy with xCloud, check the site's **Git** page for a deploy key panel and clear it with **Verify connection**.

For more on Git workflows, read our **[Git hooks best practices](/top-advanced-git-hooks-best-practices/)** and the guide to **[deploying custom PHP applications with xCloud](/how-to-deploy-custom-php-applications-with-xcloud/)**.

If you have found this blog helpful, feel free to [**subscribe to our blogs**](https://xcloud.host/blog/) for valuable tutorials, guides, knowledge, and tips on web hosting and server management. You can also join our [**Facebook community**](https://www.facebook.com/groups/xcloud.community) to share insights and engage in discussions.

## Frequently Asked Questions

### Is a GitHub deploy key read-only?

By default, yes. GitHub only gives a deploy key write access when you tick Allow write access while adding it. Leave it off for servers that only pull code.

### Can I use one deploy key for several repositories?

No. GitHub ties each deploy key to a single repository and refuses a key that is already in use elsewhere. Create one key pair per repository and give each an alias in ~/.ssh/config.

### What is the difference between a deploy key and my personal SSH key?

Your personal SSH key opens every repository your GitHub account can reach. A deploy key opens one repository and stays active even if the person who added it leaves the team. That narrower reach is why servers should use deploy keys.

### Do deploy keys expire?

No. GitHub deploy keys have no expiry date, so remove them yourself when a server is retired. Deploy keys cannot be edited either: to change one, delete it and add a new one.

### Why does my deployment say Permission denied (publickey)?

GitHub did not accept any key the server offered. Check that the public key is listed under the repository's Deploy keys, that the server uses the matching private key, and that the server can reach GitHub on port 22. Changing the branch will not help.

### Does xCloud need a deploy key for a public repository?

No. xCloud clones public repositories over HTTPS without a key. Private repositories need one, and xCloud adds it for you when your connected GitHub account has admin access to the repository.
