---
title: GitHub SSH on a VPS (Reliable Deploy Access)
description: How to set up a stable SSH-based Git connection on a VPS for pull-based deployments.
pubDate: '2026-03-04T19:09:00+01:00'
category: en/on-premises-private-cloud
tags:
  - podman
  - ssh
seriesParent: astro-ssr-with-podman
seriesOrder: 4
---

The first time I tried `git pull` on the VPS to ship an update, GitHub slammed the door:

`Invalid username or token. Password authentication is not supported`

Pasting a personal access token onto the server worked once but felt wrong — tokens expire, end up in shell history, and bind the deploy to a single human account. An SSH Deploy Key fixes all three in one move.

This is the setup I settled on for every pull-based deploy since.

## Create a dedicated SSH key on the VPS

A key that lives only on this VPS, labelled so I can identify it in the GitHub UI later:

```bash
mkdir -p ~/.ssh && chmod 700 ~/.ssh
ssh-keygen -t ed25519 -C "vps-website" -f ~/.ssh/id_ed25519 -N ""
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
cat ~/.ssh/id_ed25519.pub
```

Ed25519 over RSA because it's shorter, faster, and GitHub has supported it for years now. Copy the printed public key for the next step.

## Add the key as a Deploy Key

In the repository on GitHub:

- `Settings`
- `Deploy keys`
- `Add deploy key`

I keep it read-only unless the VPS actually needs to push — which for pull-based deploys it never does. A read-only deploy key is strictly less dangerous than a PAT: it's scoped to a single repository, can't be used to escalate elsewhere, and doesn't expire.

## Trust GitHub host keys

Pre-seeding `known_hosts` avoids the interactive "Are you sure?" prompt during the first `git clone` in a non-interactive deploy script:

```bash
ssh-keyscan github.com >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
```

## Test SSH access

```bash
ssh -T git@github.com
```

A friendly "Hi `<repo>`! You've successfully authenticated…" message means the key is live. If it fails here, fix it before touching the repo remote.

## Switch the repository remote from HTTPS to SSH

```bash
cd /opt/website
git remote set-url origin git@github.com:adrian-altner/website.git
git remote -v
git fetch
git pull
```

From this point on, deploys don't involve any interactive authentication at all.

## Optional: explicit SSH config

For multi-key hosts or anywhere I want strict "use only this key for this host" behaviour:

```txt
# ~/.ssh/config
Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes
```

```bash
chmod 600 ~/.ssh/config
```

`IdentitiesOnly yes` is the important one — it stops `ssh` from offering every key the agent knows about before trying the right one, which is what usually causes "too many authentication failures" errors on machines with more than a couple of keys.

## Quick troubleshooting

- `Permission denied (publickey)` — the key isn't attached to the right repo, or to the right account. Double-check under the repository's Deploy keys, not your personal SSH keys.
- `Repository not found` — usually a typo in the remote URL, or the deploy key is attached to a different repo than you think.
- `Host key verification failed` — `known_hosts` is missing the GitHub entry. Re-run `ssh-keyscan github.com >> ~/.ssh/known_hosts`.

## What to take away

- Deploy Keys are scoped, non-expiring, and read-only by default — a better fit for servers than PATs.
- Ed25519 keys are the sensible default in 2026; RSA buys you nothing here.
- Pre-seed `known_hosts` so non-interactive deploys don't prompt.
- `IdentitiesOnly yes` saves hours of debugging on hosts with multiple SSH identities.
- Deployments should depend on machine identity, not a password a human types.
